From 21a5ec34490abfb4ec8429f7d52079f376cf0b37 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 3 Dec 2014 20:21:39 +0000 Subject: [PATCH] fpspreadsheet: Complete unit tests for copying of data, formats and formulas git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3821 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsexprparser.pas | 9 +- components/fpspreadsheet/tests/copytests.pas | 277 +++++++++++++++--- .../fpspreadsheet/tests/spreadtestgui.lpi | 6 +- 3 files changed, 250 insertions(+), 42 deletions(-) diff --git a/components/fpspreadsheet/fpsexprparser.pas b/components/fpspreadsheet/fpsexprparser.pas index f961a82d3..1cbe37144 100644 --- a/components/fpspreadsheet/fpsexprparser.pas +++ b/components/fpspreadsheet/fpsexprparser.pas @@ -1844,8 +1844,13 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula); begin r := AFormula[AIndex].Row; c := AFormula[AIndex].Col; - flags := AFormula[AIndex].RelFlags; - ANode := TsCellExprNode.Create(self, FWorksheet, r, c, flags); + if (LongInt(r) < 0) or (LongInt(c) < 0) then + ANode := TsConstExprNode.CreateError(self, errIllegalRef) + else + begin + flags := AFormula[AIndex].RelFlags; + ANode := TsCellExprNode.Create(self, FWorksheet, r, c, flags); + end; dec(AIndex); end; fekCellRange: diff --git a/components/fpspreadsheet/tests/copytests.pas b/components/fpspreadsheet/tests/copytests.pas index 1061c5cc6..b77083196 100644 --- a/components/fpspreadsheet/tests/copytests.pas +++ b/components/fpspreadsheet/tests/copytests.pas @@ -4,6 +4,8 @@ unit copytests; interface { Tests for copying cells + NOTE: The code in these tests is very fragile because the test results are + hard-coded. Any modification in "InitCopyData" must be carefully verified! } uses @@ -36,6 +38,9 @@ type procedure Test_CopyFormatsToEmptyCells; procedure Test_CopyFormatsToOccupiedCells; + + procedure Test_CopyFormulasToEmptyCells; + procedure Test_CopyFormulasToOccupiedCells; end; implementation @@ -83,6 +88,8 @@ begin end; end; +{ IMPORTANT: Carefully check the Test_Copy method if anything is changed here. + The expected test results are hard-coded in this method! } procedure InitCopyData; begin SourceCells[0] := InitNumber(1.0, scTransparent); // will be in A1 @@ -110,9 +117,11 @@ begin inherited TearDown; end; -{ This test prepares a worksheet and copies Values (ATestKind = 1), Formats - (AWhat = 2), or Formulas (AWhat = 3). The worksheet is saved, reloaded - and compared to expectated data } +{ This test prepares a worksheet and copies Values (ATestKind = 1 or 2), Formats + (AWhat = 3 or 4), or Formulas (AWhat = 5 or 6). The odd ATestKind number + copy the data to the empty column C, the even value copy them to the + occupied column B which contains the source data (in column A) shifted down + by 1 cell. "The worksheet is saved, reloaded and compared to expectated data } procedure TSpreadCopyTests.Test_Copy(ATestKind: Integer); const AFormat = sfExcel8; @@ -120,38 +129,39 @@ var TempFile: string; MyWorksheet: TsWorksheet; MyWorkbook: TsWorkbook; - row, col: Integer; + i, row, col: Integer; cell: PCell; + expectedFormula: String; begin TempFile := GetTempFileName; MyWorkbook := TsWorkbook.Create; try - MyWorkbook.Options := MyWorkbook.Options + [boAutoCalc]; +// MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving]; //boAutoCalc]; MyWorkSheet:= MyWorkBook.AddWorksheet(CopyTestSheet); // Prepare the worksheet in which cells are copied: // Store the SourceCells to column A and B; in B shifted down by 1 cell - { A B + { A B C 1 1.0 - 2 2.0 1.0 - 3 3.0 (yellow) 2.0 - 4 Lazarus (red) 3.0 - 5 A1+1 Lazarus - 6 $A1+1 A1+1 - 7 A$1+1 $A1+1 - 8 $A$1+1 (gray) A$1+1 - 9 (empty) $A$1+1 (gray) - 10 (empty) + 2 2.0 1.0 + 3 3.0 (yellow) 2.0 + 4 Lazarus (red) 3.0 + 5 A1+1 Lazarus + 6 $A1+1 A1+1 + 7 A$1+1 $A1+1 + 8 $A$1+1 (gray) A$1+1 + 9 (empty) $A$1+1 (gray) + 10 (empty) } for col := 0 to 1 do for row := 0 to High(SourceCells) do begin - // Why is there a row index of "row + col" below? The first column has the - // data starting at the top, in cell A1. In the second column each row - // index is incremented by 1, i.e. the data are shifted down by 1 cell. + // Adding the col to the row index shifts the data in the second column + // down. Offsetting the second column is done to avoid that the "copy" + // action operates on cells having a different content afterwards. case SourceCells[row].ContentType of cctNumber: cell := MyWorksheet.WriteNumber(row+col, col, SourceCells[row].NumberValue); @@ -169,23 +179,17 @@ begin MyWorksheet.CalcFormulas; // Now perform the "copy" operations - case ATestKind of - 1, 2: - // copy the source cell VALUES to the empty column C (ATestKind = 1) - // or occupied column B (ATestKind = 2) - begin - if ATestKind = 1 then col := 2 else col := 1; - for row := 0 to High(SourceCells) do - Myworksheet.CopyValue(MyWorksheet.FindCell(row, 0), row, col); - end; - 3, 4: - // copy the source cell FORMATS to the empty column C (ATestKind = 1) - // or occupied column B (ATestKind = 2) - begin - if ATestKind = 1 then col := 2 else col := 1; - for row := 0 to High(SourceCells) do - MyWorksheet.CopyFormat(MyWorksheet.FindCell(row, 0), row, col); - end; + for row := 0 to High(SourceCells) do + begin + cell := Myworksheet.FindCell(row, 0); + case ATestKind of + 1: MyWorksheet.CopyValue(cell, row, 2); + 2: MyWorksheet.CopyValue(cell, row, 1); + 3: MyWorksheet.CopyFormat(cell, row, 2); + 4: MyWorksheet.CopyFormat(cell, row, 1); + 5: MyWorksheet.CopyFormula(cell, row, 2); + 6: MyWorksheet.CopyFormula(cell, row, 1); + end; end; // Write to file @@ -201,6 +205,196 @@ begin MyWorkbook.ReadFromFile(TempFile, AFormat); MyWorksheet := MyWorkbook.GetFirstWorksheet; + if odd(ATestKind) then col := 2 else col := 1; + + for i:=0 to Length(SourceCells) do // the "-1" is dropped to catch the down-shifted column! + begin + row := i; + cell := MyWorksheet.FindCell(row, col); + + // (1) -- Compare values --- + + case ATestKind of + 1, 2: // Copied values + if cell <> nil then + begin + // Check formula results + if HasFormula(@SourceCells[row]) then + CheckEquals( + SourceCells[0].NumberValue + 1, + cell^.NumberValue, + 'Result of copied formula mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ) + else + if (SourceCells[row].ContentType in [cctNumber, cctUTF8String, cctEmpty]) then + CheckEquals( + GetEnumName(TypeInfo(TCellContentType), Integer(SourceCells[row].ContentType)), + GetEnumName(TypeInfo(TCellContentType), Integer(cell^.ContentType)), + 'Content type mismatch, cell '+CellNotation(MyWorksheet, row, col) + ); + case SourceCells[row].ContentType of + cctNumber: + CheckEquals( + SourceCells[row].NumberValue, + cell^.NumberValue, + 'Number value mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + cctUTF8String: + CheckEquals( + SourceCells[row].UTF8StringValue, + cell^.UTF8StringValue, + 'String value mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + end; + end; + + 3: // Copied formats to empty column -> there must not be any content + if (cell <> nil) and (cell^.ContentType <> cctEmpty) then + CheckEquals( + true, // true = "the cell has no content" + (cell = nil) or (cell^.ContentType = cctEmpty), + 'No content mismatch, cell ' + CellNotation(MyWorksheet, row,col) + ); + + 4: // Copied formats to occupied column --> data must be equal to source + // cells, but offset by 1 cell + if (row = 0) then + CheckEquals( + true, // true = "the cell has no content" + (cell = nil) or (cell^.ContentType = cctEmpty), + 'No content mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ) + else begin + if (SourceCells[i+col-2].ContentType in [cctNumber, cctUTF8String, cctEmpty]) then + CheckEquals( + GetEnumName(TypeInfo(TCellContentType), Integer(SourceCells[i+col-2].ContentType)), + GetEnumName(TypeInfo(TCellContentType), Integer(cell^.ContentType)), + 'Content type mismatch, cell '+CellNotation(MyWorksheet, row, col) + ); + case SourceCells[i+col-2].ContentType of + cctNumber: + CheckEquals( + SourceCells[i+col-2].NumberValue, + cell^.NumberValue, + 'Number value mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + cctUTF8String: + CheckEquals( + SourceCells[i+col-2].UTF8StringValue, + cell^.UTF8StringValue, + 'String value mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + end; + end; + end; + + // (2) -- Compare formatting --- + + case ATestKind of + 1, 5: + CheckEquals( + true, + (cell = nil) or (cell^.UsedFormattingFields = []), + 'Default formatting mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + 2, 6: + if (row = 0) then + CheckEquals( + true, + (cell = nil) or (cell^.UsedFormattingFields = []), + 'Default formatting mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ) + else + begin + CheckEquals( + true, + SourceCells[i+(col-2)].UsedFormattingFields = cell^.UsedFormattingFields, + 'Used formatting fields mismatch, cell ' + CellNotation(myWorksheet, row, col) + ); + if (uffBackgroundColor in SourceCells[i].UsedFormattingFields) then + CheckEquals( + SourceCells[i+(col-2)].BackgroundColor, + cell^.BackgroundColor, + 'Background color mismatch, cell ' + CellNotation(Myworksheet, row, col) + ); + end; + 3, 4: + if cell <> nil then + begin + CheckEquals( + true, + SourceCells[i].UsedFormattingFields = cell^.UsedFormattingFields, + 'Used formatting fields mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + if (uffBackgroundColor in SourceCells[i].UsedFormattingFields) then + CheckEquals( + SourceCells[i].BackgroundColor, + cell^.BackgroundColor, + 'Background color mismatch, cell ' + CellNotation(Myworksheet, row, col) + ); + end; + end; + + // (3) --- Check formula --- + + case ATestKind of + 1, 2, 3: + CheckEquals( + false, + HasFormula(cell), + 'No formula mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + 4: + if (row = 0) then + CheckEquals( + false, + (cell <> nil) and HasFormula(cell), + 'No formula mismatch, cell ' + CellNotation(Myworksheet, row, col) + ) + else + CheckEquals( + SourceCells[i+col-2].FormulaValue, + cell^.Formulavalue, + 'Formula mismatch, cell ' + CellNotation(MyWorksheet, row, col) + ); + 5: + if cell <> nil then + begin + case SourceCells[i].FormulaValue of + 'A1+1' : expectedFormula := 'C1+1'; + 'A$1+1': expectedFormula := 'C$1+1'; + else expectedFormula := SourceCells[i].FormulaValue; + end; + CheckEquals( + expectedFormula, + cell^.FormulaValue, + 'Formula mismatch, cell ' + Cellnotation(Myworksheet, row, col) + ); + end; + 6: + begin + if row = 0 then + expectedFormula := '' + else + begin + case SourceCells[i].FormulaValue of + 'A1+1' : expectedFormula := 'B1+1'; + 'A$1+1': expectedFormula := 'B$1+1'; + '$A1+1': expectedFormula := '$A1+1'; + else expectedFormula := SourceCells[i].FormulaValue; + end; + CheckEquals( + expectedFormula, + cell^.FormulaValue, + 'Formula mismatch, cell ' + Cellnotation(Myworksheet, row, col) + ); + end; + end; + end; + end; // For + +(* + case ATestKind of 1, 2: // Copied VALUES in first colum to empty third column (ATestKind = 1) or @@ -383,6 +577,7 @@ begin end; end; +*) finally MyWorkbook.Free; @@ -415,6 +610,18 @@ begin Test_Copy(4); end; +{ Copy given cell formulas to empty cells } +procedure TSpreadCopyTests.Test_CopyFormulasToEmptyCells; +begin + Test_Copy(5); +end; + +{ Copy given cell formulas to occupied cells } +procedure TSpreadCopyTests.Test_CopyFormulasToOccupiedCells; +begin + Test_Copy(6); +end; + initialization RegisterTest(TSpreadCopyTests); diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 9b7f3012a..d9c45b0a4 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -48,7 +48,6 @@ - @@ -61,7 +60,6 @@ - @@ -70,7 +68,6 @@ - @@ -109,7 +106,6 @@ - @@ -118,11 +114,11 @@ - +