Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpreadsheetBuffer string interpolation handler #30

Merged
merged 30 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0f0fa55
WIP for a string interpolation handler for SpreadsheetBuffer
sveinungf Dec 25, 2023
4d84662
Use SpreadsheetBuffer.TryWrite for number DataCells
sveinungf Dec 25, 2023
3e56f8e
Write styled number cells with SpreadsheetBuffer.TryWrite
sveinungf Dec 25, 2023
e998aac
Write integer DataCell with reference using SpreadsheetBuffer.TryWrite
sveinungf Dec 25, 2023
470c095
Fix premature advancement of the buffer in the SpreadsheetBuffer stri…
sveinungf Dec 26, 2023
d4456e7
TryWriteCellWithReference for number cells using SpreadsheetBuffer.Tr…
sveinungf Dec 26, 2023
4290682
Write DateTime cells with reference using SpreadsheetBuffer.TryWrite
sveinungf Dec 26, 2023
aa2e992
Use SpreadsheetBuffer.TryWrite for string data cells
sveinungf Dec 26, 2023
d6b8e40
Remove literal length check in string interpolation handler
sveinungf Dec 26, 2023
4fd7dfb
Write number formula cells with SpreadsheetBuffer.TryWrite
sveinungf Dec 26, 2023
2347183
Write DateTime formula cells with SpreadsheetBuffer.TryWrite
sveinungf Dec 26, 2023
06a481c
Write formula number cells with reference using SpreadsheetBuffer.Try…
sveinungf Dec 26, 2023
99a6279
Write DateTime formula cells with reference using SpreadsheetBuffer.T…
sveinungf Dec 26, 2023
670d35a
WriteValuePieceByPiece for number cell writers with SpreadsheetBuffer…
sveinungf Dec 26, 2023
ff07a52
Use SpreadsheetBuffer.TryWrite in NumberCellValueWriterBase
sveinungf Dec 26, 2023
a20f99b
Use SpreadsheetBuffer.TryWrite in NullValueWriterBase
sveinungf Dec 26, 2023
f3bfda1
Use SpreadsheetBuffer.TryWrite for boolean cells
sveinungf Dec 26, 2023
8519427
Use SpreadsheetBuffer.TryWrite for string cells
sveinungf Dec 26, 2023
f9c4242
GetSpan() helper method in string interpolation handler
sveinungf Dec 26, 2023
83ac14e
Remove unused method
sveinungf Dec 26, 2023
eb4bea9
More use of SpreadsheetBuffer.TryWrite
sveinungf Dec 26, 2023
30915a0
Rename some static ReadOnlySpan<byte>
sveinungf Dec 26, 2023
2e8420c
Cleanup in SpreadsheetBuffer
sveinungf Dec 26, 2023
dba02f4
Fix style interpolation in boolean writers
sveinungf Dec 26, 2023
4a910da
Extend test to cover all cell data types
sveinungf Dec 26, 2023
bae4d94
Cover false boolean cells in tests
sveinungf Dec 26, 2023
f9130ea
Test for DateTime formula cell without the default DateTime style
sveinungf Dec 27, 2023
c8630a2
Exclude unused methods on string interpolation handler from code cove…
sveinungf Dec 27, 2023
1934d11
Test for DateTime cell reference with default style
sveinungf Dec 27, 2023
876e80e
Test for styled cells with long string values
sveinungf Dec 27, 2023
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
12 changes: 6 additions & 6 deletions SpreadCheetah.Test/Helpers/CellFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ internal static class CellFactory

public static object Create(CellType cellType, CellValueType valueType, bool isNull, StyleId? styleId, out object? value) => valueType switch
{
CellValueType.Bool => CreateForBool(cellType, isNull, styleId, out value),
CellValueType.BoolFalse => CreateForBool(cellType, isNull ? null : false, styleId, out value),
CellValueType.BoolTrue => CreateForBool(cellType, isNull ? null : true, styleId, out value),
CellValueType.DateTime => CreateForDateTime(cellType, isNull, styleId, out value),
CellValueType.Decimal => CreateForDecimal(cellType, isNull, styleId, out value),
CellValueType.Double => CreateForDouble(cellType, isNull, styleId, out value),
Expand All @@ -106,7 +107,8 @@ internal static class CellFactory

public static Cell Create(Formula formula, CellValueType valueType, bool isNull, StyleId? styleId, out object? value) => valueType switch
{
CellValueType.Bool => CreateForBool(formula, isNull, styleId, out value),
CellValueType.BoolFalse => CreateForBool(formula, isNull ? null : false, styleId, out value),
CellValueType.BoolTrue => CreateForBool(formula, isNull ? null : true, styleId, out value),
CellValueType.DateTime => CreateForDateTime(formula, isNull, styleId, out value),
CellValueType.Decimal => CreateForDecimal(formula, isNull, styleId, out value),
CellValueType.Double => CreateForDouble(formula, isNull, styleId, out value),
Expand All @@ -117,9 +119,8 @@ internal static class CellFactory
_ => throw new ArgumentOutOfRangeException(nameof(valueType), valueType, null)
};

private static object CreateForBool(CellType cellType, bool isNull, StyleId? styleId, out object? value)
private static object CreateForBool(CellType cellType, bool? actualValue, StyleId? styleId, out object? value)
{
bool? actualValue = isNull ? null : true;
value = actualValue;

return cellType switch
Expand All @@ -131,9 +132,8 @@ private static object CreateForBool(CellType cellType, bool isNull, StyleId? sty
};
}

private static Cell CreateForBool(Formula formula, bool isNull, StyleId? styleId, out object? value)
private static Cell CreateForBool(Formula formula, bool? actualValue, StyleId? styleId, out object? value)
{
bool? actualValue = !isNull ? true : null;
value = actualValue;
return new Cell(formula, actualValue, styleId);
}
Expand Down
3 changes: 2 additions & 1 deletion SpreadCheetah.Test/Helpers/CellValueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public enum CellValueType
Double,
Decimal,
DateTime,
Bool
BoolTrue,
BoolFalse
}
2 changes: 1 addition & 1 deletion SpreadCheetah.Test/Helpers/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace SpreadCheetah.Test.Helpers;
internal static class TestData
{
private static readonly CellType[] CellTypeArray = EnumHelper.GetValues<CellType>();
private static readonly CellType[] StyledCellTypeArray = new[] { CellType.StyledCell, CellType.Cell };
private static readonly CellType[] StyledCellTypeArray = [CellType.StyledCell, CellType.Cell];
private static readonly CellValueType[] CellValueTypeArray = EnumHelper.GetValues<CellValueType>();
private static readonly RowCollectionType[] RowCollectionTypeArray = EnumHelper.GetValues<RowCollectionType>();

Expand Down
80 changes: 51 additions & 29 deletions SpreadCheetah.Test/Tests/SpreadsheetFormulaRowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,19 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndStyle(bool bold)
// Arrange
const string formulaText = "SUM(A1,A2)";
using var stream = new MemoryStream();
await using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream))
{
await spreadsheet.StartWorksheetAsync("Sheet");
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream);
await spreadsheet.StartWorksheetAsync("Sheet");

var style = new Style();
style.Font.Bold = bold;
var styleId = spreadsheet.AddStyle(style);
var style = new Style();
style.Font.Bold = bold;
var styleId = spreadsheet.AddStyle(style);

var formula = new Formula(formulaText);
var cell = new Cell(formula, styleId);
var formula = new Formula(formulaText);
var cell = new Cell(formula, styleId);

// Act
await spreadsheet.AddRowAsync(cell);
await spreadsheet.FinishAsync();
}
// Act
await spreadsheet.AddRowAsync(cell);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
Expand Down Expand Up @@ -108,30 +106,54 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndCachedValue(CellValueType
Assert.Equal(valueType.GetExpectedDefaultNumberFormat() ?? "", actualCell.Style.NumberFormat.Format);
}

[Fact]
public async Task Spreadsheet_AddRow_CellWithFormulaAndCachedDateTimeValueWithoutDefaultStyle()
{
// Arrange
const string formulaText = "SUM(A1,A2)";
using var stream = new MemoryStream();
var options = new SpreadCheetahOptions { DefaultDateTimeFormat = null };
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options);
await spreadsheet.StartWorksheetAsync("Sheet");
var formula = new Formula(formulaText);
var cachedValue = new DateTime(2020, 1, 2, 3, 4, 5, DateTimeKind.Utc);
var cell = new Cell(formula, cachedValue);

// Act
await spreadsheet.AddRowAsync(cell);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var workbook = new XLWorkbook(stream);
var worksheet = workbook.Worksheets.Single();
var actualCell = worksheet.Cell(1, 1);
Assert.Equal(formulaText, actualCell.FormulaA1);
Assert.Equal(cachedValue.ToOADate(), actualCell.GetValue<double>(), 0.0001);
}

public static IEnumerable<object?[]> ItalicWithValueTypes() => TestData.CombineWithValueTypes(true, false);

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool italic)
[MemberData(nameof(ItalicWithValueTypes))]
public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool italic, CellValueType cachedValueType, RowCollectionType rowType, bool cachedValueIsNull)
{
// Arrange
const string formulaText = "SUM(A1,A2)";
const int cachedValue = int.MinValue;
using var stream = new MemoryStream();
await using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream))
{
await spreadsheet.StartWorksheetAsync("Sheet");
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream);
await spreadsheet.StartWorksheetAsync("Sheet");

var style = new Style();
style.Font.Italic = italic;
var styleId = spreadsheet.AddStyle(style);
var style = new Style();
style.Font.Italic = italic;
var styleId = spreadsheet.AddStyle(style);

var formula = new Formula(formulaText);
var cell = new Cell(formula, cachedValue, styleId);
var formula = new Formula(formulaText);
var cell = CellFactory.Create(formula, cachedValueType, cachedValueIsNull, styleId, out var cachedValue);

// Act
await spreadsheet.AddRowAsync(cell);
await spreadsheet.FinishAsync();
}
// Act
await spreadsheet.AddRowAsync(cell, rowType);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
Expand All @@ -140,7 +162,7 @@ public async Task Spreadsheet_AddRow_CellWithFormulaAndStyleAndCachedValue(bool
var actualCell = worksheet.Cell(1, 1);
Assert.Equal(formulaText, actualCell.FormulaA1);
Assert.Equal(italic, actualCell.Style.Font.Italic);
Assert.Equal(cachedValue, actualCell.CachedValue.GetNumber());
Assert.Equal(cachedValue.GetExpectedCachedValueAsString(), actualCell.CachedValue.ToString(CultureInfo.InvariantCulture));
}

[Theory]
Expand Down
27 changes: 27 additions & 0 deletions SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,33 @@ public async Task Spreadsheet_AddRow_ExplicitCellReferencesForLongStringValueCel
Assert.Equal(expectedRow1Refs, actualSheet2Refs);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Spreadsheet_AddRow_ExplicitCellReferenceForDateTimeCellWithDefaultStyling(bool isNull)
{
// Arrange
using var stream = new MemoryStream();
var options = new SpreadCheetahOptions { WriteCellReferenceAttributes = true };
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options);
await spreadsheet.StartWorksheetAsync("Sheet1");

DateTime? cellValue = isNull ? null : new DateTime(2019, 8, 7, 6, 5, 4, DateTimeKind.Utc);
var cell = new Cell(cellValue);

// Act
await spreadsheet.AddRowAsync(cell);
await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var actual = SpreadsheetDocument.Open(stream, true);
var sheetPart = Assert.Single(actual.WorkbookPart!.WorksheetParts);
var actualRow = Assert.Single(sheetPart.Worksheet.Descendants<Row>());
var actualCell = Assert.Single(actualRow.Descendants<OpenXmlCell>());
Assert.Equal("A1", actualCell.CellReference?.Value);
}

public static IEnumerable<object?[]> RowHeights() => TestData.CombineWithCellTypes<double?>(
0.1,
10d,
Expand Down
44 changes: 44 additions & 0 deletions SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,50 @@ public async Task Spreadsheet_AddRow_ExplicitCellReferencesForStyledCells(CellVa
Assert.Equal(expectedRow1Refs, actualSheet2Refs);
}

[Theory]
[MemberData(nameof(TestData.StyledCellTypes), MemberType = typeof(TestData))]
public async Task Spreadsheet_AddRow_LongStringValueStyledCells(CellType cellType, RowCollectionType rowType)
{
// Arrange
using var stream = new MemoryStream();
var options = new SpreadCheetahOptions { BufferSize = SpreadCheetahOptions.MinimumBufferSize };
await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options);
var value = new string('a', options.BufferSize * 2);
var style = new Style();
style.Fill.Color = Color.Thistle;
var styleId = spreadsheet.AddStyle(style);

var row1 = Enumerable.Range(1, 10).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList();
var row2 = Enumerable.Range(1, 1).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList();
var row3 = Enumerable.Range(1, 100).Select(_ => CellFactory.Create(cellType, value, styleId)).ToList();

// Act
await spreadsheet.StartWorksheetAsync("Sheet1");
await spreadsheet.AddRowAsync(row1, rowType);
await spreadsheet.AddRowAsync(row2, rowType);
await spreadsheet.AddRowAsync(row3, rowType);

await spreadsheet.StartWorksheetAsync("Sheet2");
await spreadsheet.AddRowAsync(row1, rowType);

await spreadsheet.FinishAsync();

// Assert
SpreadsheetAssert.Valid(stream);
using var actual = SpreadsheetDocument.Open(stream, true);
var sheetParts = actual.WorkbookPart!.WorksheetParts.ToList();
Assert.Equal(2, sheetParts.Count);

var sheet1Rows = sheetParts[0].Worksheet.Descendants<Row>().ToList();
Assert.Equal(3, sheet1Rows.Count);
Assert.All(sheet1Rows[0].Descendants<OpenXmlCell>(), x => Assert.Equal(value, x.InnerText));
Assert.All(sheet1Rows[1].Descendants<OpenXmlCell>(), x => Assert.Equal(value, x.InnerText));
Assert.All(sheet1Rows[2].Descendants<OpenXmlCell>(), x => Assert.Equal(value, x.InnerText));

var sheet2Row = Assert.Single(sheetParts[1].Worksheet.Descendants<Row>());
Assert.All(sheet2Row.Descendants<OpenXmlCell>(), x => Assert.Equal(value, x.InnerText));
}

[Theory]
[MemberData(nameof(TestData.StyledCellTypes), MemberType = typeof(TestData))]
public async Task Spreadsheet_AddRow_ExplicitCellReferencesForLongStringValueStyledCells(CellType cellType, RowCollectionType rowType)
Expand Down
Loading
Loading