Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
353 changes: 351 additions & 2 deletions RecordParser.Test/FileReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
[InlineData(13, 5)]
[InlineData(14, 7)]
[InlineData(15, 7)]
public void Given_record_is_too_large_for_custom_buffer_size_then_exception_should_be_throw(int bufferSize, int canRead)

Check warning on line 115 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Given_record_is_too_large_for_custom_buffer_size_then_exception_should_be_throw' on test class 'FileReaderTest' does not use parameter 'bufferSize'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 115 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Given_record_is_too_large_for_custom_buffer_size_then_exception_should_be_throw' on test class 'FileReaderTest' does not use parameter 'bufferSize'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -155,7 +155,7 @@
var records = reader.ReadRecords(parser, new()
{
HasHeader = true,
// BufferSize = bufferSize,
// BufferSize = bufferSize,
});

foreach (var item in records)
Expand All @@ -176,6 +176,7 @@

public static string GetFilePath(string fileName) => Path.Combine(Directory.GetCurrentDirectory(), fileName);

public const string Header = "HEADER";
public static IEnumerable<object[]> Given_quoted_csv_file_should_read_quoted_properly_theory(string file)
{
var fileNames = new[]
Expand Down Expand Up @@ -206,7 +207,7 @@
}

if (hasHeader)
fileBuilder.Insert(index: 0, "Id,Date,Name,Rate,Ranking" + Environment.NewLine);
fileBuilder.Insert(index: 0, Header + Environment.NewLine);

if (blankLineAtEnd)
fileBuilder.AppendLine();
Expand All @@ -223,7 +224,7 @@

[Theory]
[MemberData(nameof(Given_quoted_csv_file_should_read_quoted_properly_theory), new object[] { "AllFieldsQuotedCsv.csv" })]
public void Read_csv_file_all_fields_quoted(string fileContent, bool hasHeader, bool parallelProcessing, bool blankLineAtEnd, int repeat)

Check warning on line 227 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_csv_file_all_fields_quoted' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 227 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_csv_file_all_fields_quoted' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -273,7 +274,7 @@

[Theory]
[MemberData(nameof(Given_quoted_csv_file_should_read_quoted_properly_theory), new object[] { "QuotedCsv.csv" })]
public void Read_quoted_csv_file(string fileContent, bool hasHeader, bool parallelProcessing, bool blankLineAtEnd, int repeat)

Check warning on line 277 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_quoted_csv_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 277 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_quoted_csv_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -363,7 +364,7 @@

[Theory]
[MemberData(nameof(Given_not_quoted_csv_file_should_read_quoted_properly_theory))]
public void Read_not_quoted_csv_file(string fileContent, bool hasHeader, bool parallelProcessing, bool blankLineAtEnd, bool containgQuote, int repeat)

Check warning on line 367 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_not_quoted_csv_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 367 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_not_quoted_csv_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -452,7 +453,7 @@

[Theory]
[MemberData(nameof(Given_fixed_length_file_should_read_quoted_properly_theory))]
public void Read_fixed_length_file(string fileContent, bool parallelProcessing, bool blankLineAtEnd, int repeat)

Check warning on line 456 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 456 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -537,7 +538,7 @@

[Theory]
[MemberData(nameof(Given_fixed_length_file_should_read_quoted_properly_theory))]
public void Read_plain_text_of_fixed_length_file(string fileContent, bool parallelProcessing, bool blankLineAtEnd, int repeat)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'repeat'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'parallelProcessing'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'repeat'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'blankLineAtEnd'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)

Check warning on line 541 in RecordParser.Test/FileReaderTest.cs

View workflow job for this annotation

GitHub Actions / dotnet test

Theory method 'Read_plain_text_of_fixed_length_file' on test class 'FileReaderTest' does not use parameter 'parallelProcessing'. (https://xunit.github.io/xunit.analyzers/rules/xUnit1026)
{
// Arrange

Expand Down Expand Up @@ -568,5 +569,353 @@

result.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

public record RegularCaseRecord(int Aaa, int Bbb, int Ccc, int Ddd);

[Theory]
[InlineData("\"AAA\",\"BBB\",\"CCC\",\"DDD\"")]
[InlineData("Aaa,Bbb,Ccc,Ddd")]
[InlineData("AAA,BBB,CCC,DDD")]
[InlineData("aaa,bbb,ccc,ddd")]
[InlineData("AAA , BBB , CCC , DDD")]
[InlineData("A_AA,B_BB,C_CC,D_DD")]
public void Read_csv_file_with_autobind_should_match_header_case_insensitive(string header)
{
// Arrange

var fileContent = $"""
{header}
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
87,88,89,100
89,99,100,101
88,89,90,91
""";

var reader = new StringReader(fileContent);
var expected = new RegularCaseRecord[]
{
new(1,2,3,4),
new(5,6,7,8),
new(9,10,11,12),
new(13,14,15,16),
new(87,88,89,100),
new(89,99,100,101),
new(88,89,90,91),
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = false,
};

// Act

var items = reader.ReadRecords<RegularCaseRecord>(readOptions);

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

[Fact]
public void Read_csv_file_with_autobind_should_support_additional_configuration()
{
// Arrange

var fileContent = $"""
AAA,BBB,CCC,DDD
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
87,88,89,100
89,99,100,101
88,89,90,91
""";

var reader = new StringReader(fileContent);
var expected = new RegularCaseRecord[]
{
new(10,20,30,40),
new(50,60,70,80),
new(90,100,110,120),
new(130,140,150,160),
new(870,880,890,1000),
new(890,990,1000,1010),
new(880,890,900,910),
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = false,
};

// Act

var items = reader.ReadRecords<RegularCaseRecord>(readOptions, builder =>
builder.DefaultTypeConvert(x => int.Parse(x) * 10));

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

[Fact]
public void Read_csv_file_with_autobind_should_support_nested_fields()
{
// Arrange

var fileContent = $"""
BirthDay ; Name ; Mother.BirthDay; Mother.Name
2020.05.23 ; son name ; 1980.01.15 ; mother name
""";

var reader = new StringReader(fileContent);
var expected = new Person[]
{
new Person
{
BirthDay = new DateTime(2020, 05, 23),
Name = "son name",
Mother = new Person
{
BirthDay = new DateTime(1980, 01, 15),
Name = "mother name",
}
}
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = false,
};

// Act

var items = reader.ReadRecords<Person>(readOptions);

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

[Fact]
public void Read_csv_file_with_autobind_should_support_inherited_properties()
{
// Arrange

var fileContent = $"""
Id ; BirthDay ; Name ; Mother.Id ; Mother.BirthDay; Mother.Name
99 ; 2020.05.23 ; son name ; 100 ; 1980.01.15 ; mother name
""";

var reader = new StringReader(fileContent);
var expected = new PersonDerivated[]
{
new ()
{
Id = 99,
BirthDay = new DateTime(2020, 05, 23),
Name = "son name",
Mother = new ()
{
// can not find 'Id' because property 'Mother' has type 'Person' and not 'PersonDerivated'
// Id = 100,
BirthDay = new DateTime(1980, 01, 15),
Name = "mother name",
}
}
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = true,
};

// Act

var items = reader.ReadRecords<PersonDerivated>(readOptions);

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

[Theory]
[InlineData(" ", false)]
[InlineData(" ", true)]
[InlineData("BirthDay ; Name ; Mother.BirthDay ; Mother.Name", false)]
public void Read_csv_file_with_autobind_should_throw_when_missing_header(string header, bool hasHeader)
{
// Arrange

var fileContent = $"""
{header}
99 ; 2020.05.23 ; son name ; 100 ; 1980.01.15 ; mother name
""";

var reader = new StringReader(fileContent);
var expected = new Person[]
{
new ()
{
BirthDay = new DateTime(2020, 05, 23),
Name = "son name",
Mother = new ()
{
BirthDay = new DateTime(1980, 01, 15),
Name = "mother name",
}
}
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = hasHeader,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
};

// Act

var action = () => reader.ReadRecords<Person>(readOptions);

// Assert

action.Should()
.Throw<InvalidOperationException>()
.WithMessage("Header is mandatory when using auto-binding overload.");
}

[Theory]
[InlineData(",")]
[InlineData(";")]
[InlineData("\t")]
[InlineData("|")]
[InlineData(":")]
public void Read_csv_file_with_autobind_should_detect_common_separators(string separator)
{
// Arrange

var fileContent = $"""
Aaa,Bbb,Ccc,Ddd
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
87,88,89,100
89,99,100,101
88,89,90,91
"""
.Replace(",", separator);

var reader = new StringReader(fileContent);
var expected = new RegularCaseRecord[]
{
new(1,2,3,4),
new(5,6,7,8),
new(9,10,11,12),
new(13,14,15,16),
new(87,88,89,100),
new(89,99,100,101),
new(88,89,90,91),
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = false,
};

// Act

var items = reader.ReadRecords<RegularCaseRecord>(readOptions);

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}

[Theory]

// inferred
[InlineData(",", null)]
[InlineData(";", null)]
[InlineData("\t", null)]
[InlineData("|", null)]
[InlineData(":", null)]

// explicit (detectable)
[InlineData(",", ",")]
[InlineData(";", ";")]
[InlineData("\t", "\t")]
[InlineData("|", "|")]
[InlineData(":", ":")]

// explicit (not detectable)
[InlineData("||", "||")]
[InlineData("@", "@")]
public void Read_csv_file_with_autobind_should_support_explict_separators(string fileSeparator, string informedSeparator)
{
// Arrange

var fileContent = $"""
Aaa,Bbb,Ccc,Ddd
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
87,88,89,100
89,99,100,101
88,89,90,91
"""
.Replace(",", fileSeparator);

var reader = new StringReader(fileContent);
var expected = new RegularCaseRecord[]
{
new(1,2,3,4),
new(5,6,7,8),
new(9,10,11,12),
new(13,14,15,16),
new(87,88,89,100),
new(89,99,100,101),
new(88,89,90,91),
};

var readOptions = new VariableLengthReaderAutoBindOptions
{
HasHeader = true,
ParallelismOptions = new() { Enabled = false },
ContainsQuotedFields = true,
SkipUnmatchedColumns = false,
Separator = informedSeparator,
};

// Act

var items = reader.ReadRecords<RegularCaseRecord>(readOptions);

// Assert

items.Should().BeEquivalentTo(expected, cfg => cfg.WithStrictOrdering());
}
}
}
7 changes: 6 additions & 1 deletion RecordParser.Test/TestTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ public enum FlaggedEnum
None = 8
}

internal class Person
public class PersonDerivated : Person
{
public int Id { get; set; }
}

public class Person
{
public DateTime BirthDay { get; set; }
public string Name { get; set; }
Expand Down
Loading
Loading