You've already forked lazarus-ccr
fpspreadsheet: Add test cases for insert/delete rows into worksheets containing formulas. Issues remaining when cells referred to by the formula are deleted. Add detection of error values to the formula scanner.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3581 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -41,7 +41,8 @@ begin
|
|||||||
//cell := Worksheet.WriteFormula(1, 0, 'Day(Date(2014, 1, 12))');
|
//cell := Worksheet.WriteFormula(1, 0, 'Day(Date(2014, 1, 12))');
|
||||||
//cell := Worksheet.WriteFormula(1, 0, 'SUM(1,2,3)');
|
//cell := Worksheet.WriteFormula(1, 0, 'SUM(1,2,3)');
|
||||||
//cell := Worksheet.WriteFormula(1, 0, 'CELL("address",A1)');
|
//cell := Worksheet.WriteFormula(1, 0, 'CELL("address",A1)');
|
||||||
cell := Worksheet.WriteFormula(1, 0, 'REPT("Hallo", 3)');
|
// cell := Worksheet.WriteFormula(1, 0, 'REPT("Hallo", 3)');
|
||||||
|
cell := Worksheet.WriteFormula(1, 0, '#REF!');
|
||||||
|
|
||||||
WriteLn('A1: ', worksheet.ReadAsUTF8Text(0, 0));
|
WriteLn('A1: ', worksheet.ReadAsUTF8Text(0, 0));
|
||||||
WriteLn('B1: ', worksheet.ReadAsUTF8Text(0, 1));
|
WriteLn('B1: ', worksheet.ReadAsUTF8Text(0, 1));
|
||||||
@ -76,6 +77,7 @@ begin
|
|||||||
fekInteger : Write(' / integer value: ', IntToStr(formula[i].IntValue));
|
fekInteger : Write(' / integer value: ', IntToStr(formula[i].IntValue));
|
||||||
fekString : Write(' / string value: "', formula[i].StringValue, '"');
|
fekString : Write(' / string value: "', formula[i].StringValue, '"');
|
||||||
fekBool : Write(' / boolean value: ', BoolToStr(formula[i].DoubleValue <> 0, true));
|
fekBool : Write(' / boolean value: ', BoolToStr(formula[i].DoubleValue <> 0, true));
|
||||||
|
fekErr : Write(' / error value: ', GetErrorValueStr(TsErrorValue(formula[i].IntValue)));
|
||||||
end;
|
end;
|
||||||
WriteLn;
|
WriteLn;
|
||||||
end;
|
end;
|
||||||
|
@ -57,20 +57,11 @@ uses
|
|||||||
type
|
type
|
||||||
{ Tokens }
|
{ Tokens }
|
||||||
|
|
||||||
(* { Basic operands }
|
|
||||||
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
|
|
||||||
fekString, fekBool, fekErr, fekMissingArg,
|
|
||||||
{ Basic operations }
|
|
||||||
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
|
||||||
fekConcat, // string concatenation
|
|
||||||
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
|
|
||||||
fekParen,
|
|
||||||
*)
|
|
||||||
TsTokenType = (
|
TsTokenType = (
|
||||||
ttCell, ttCellRange, ttNumber, ttString, ttIdentifier,
|
ttCell, ttCellRange, ttNumber, ttString, ttIdentifier,
|
||||||
ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight,
|
ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight,
|
||||||
ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual,
|
ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual,
|
||||||
ttListSep, ttTrue, ttFalse, ttEOF
|
ttListSep, ttTrue, ttFalse, ttError, ttEOF
|
||||||
);
|
);
|
||||||
|
|
||||||
TsExprFloat = Double;
|
TsExprFloat = Double;
|
||||||
@ -409,7 +400,8 @@ type
|
|||||||
constructor CreateDateTime(AParser: TsExpressionParser; AValue: TDateTime);
|
constructor CreateDateTime(AParser: TsExpressionParser; AValue: TDateTime);
|
||||||
constructor CreateFloat(AParser: TsExpressionParser; AValue: TsExprFloat);
|
constructor CreateFloat(AParser: TsExpressionParser; AValue: TsExprFloat);
|
||||||
constructor CreateBoolean(AParser: TsExpressionParser; AValue: Boolean);
|
constructor CreateBoolean(AParser: TsExpressionParser; AValue: Boolean);
|
||||||
constructor CreateError(AParser: TsExpressionParser; AValue: TsErrorValue);
|
constructor CreateError(AParser: TsExpressionParser; AValue: TsErrorValue); overload;
|
||||||
|
constructor CreateError(AParser: TsExpressionParser; AValue: String); overload;
|
||||||
function AsString: string; override;
|
function AsString: string; override;
|
||||||
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
|
||||||
function NodeType : TsResultType; override;
|
function NodeType : TsResultType; override;
|
||||||
@ -653,6 +645,7 @@ type
|
|||||||
procedure ScanError(Msg: String);
|
procedure ScanError(Msg: String);
|
||||||
protected
|
protected
|
||||||
procedure SetSource(const AValue: String); virtual;
|
procedure SetSource(const AValue: String); virtual;
|
||||||
|
function DoError: TsTokenType;
|
||||||
function DoIdentifier: TsTokenType;
|
function DoIdentifier: TsTokenType;
|
||||||
function DoNumber: TsTokenType;
|
function DoNumber: TsTokenType;
|
||||||
function DoDelimiter: TsTokenType;
|
function DoDelimiter: TsTokenType;
|
||||||
@ -842,6 +835,7 @@ uses
|
|||||||
const
|
const
|
||||||
cNull = #0;
|
cNull = #0;
|
||||||
cDoubleQuote = '"';
|
cDoubleQuote = '"';
|
||||||
|
cError = '#';
|
||||||
|
|
||||||
Digits = ['0'..'9']; // + decimalseparator
|
Digits = ['0'..'9']; // + decimalseparator
|
||||||
WhiteSpace = [' ', #13, #10, #9];
|
WhiteSpace = [' ', #13, #10, #9];
|
||||||
@ -998,6 +992,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TsExpressionScanner.DoError: TsTokenType;
|
||||||
|
var
|
||||||
|
C: Char;
|
||||||
|
s: String;
|
||||||
|
begin
|
||||||
|
C := CurrentChar;
|
||||||
|
while (not IsWordDelim(C)) and (C <> cNull) do
|
||||||
|
begin
|
||||||
|
FToken := FToken + C;
|
||||||
|
C := NextPos;
|
||||||
|
end;
|
||||||
|
S := UpperCase(Token);
|
||||||
|
Result := ttError;
|
||||||
|
end;
|
||||||
|
|
||||||
function TsExpressionScanner.DoIdentifier: TsTokenType;
|
function TsExpressionScanner.DoIdentifier: TsTokenType;
|
||||||
var
|
var
|
||||||
C: Char;
|
C: Char;
|
||||||
@ -1137,6 +1146,8 @@ begin
|
|||||||
Result := DoString
|
Result := DoString
|
||||||
else if IsDigit(C) then
|
else if IsDigit(C) then
|
||||||
Result := DoNumber
|
Result := DoNumber
|
||||||
|
else if (C = cError) then
|
||||||
|
Result := DoError
|
||||||
else if IsAlpha(C) or (C = '$') then
|
else if IsAlpha(C) or (C = '$') then
|
||||||
Result := DoIdentifier
|
Result := DoIdentifier
|
||||||
else
|
else
|
||||||
@ -1662,6 +1673,8 @@ begin
|
|||||||
Result := TsCellExprNode.Create(self, FWorksheet, CurrentToken)
|
Result := TsCellExprNode.Create(self, FWorksheet, CurrentToken)
|
||||||
else if (TokenType = ttCellRange) then
|
else if (TokenType = ttCellRange) then
|
||||||
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken)
|
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken)
|
||||||
|
else if (TokenType = ttError) then
|
||||||
|
Result := tsConstExprNode.CreateError(self, CurrentToken)
|
||||||
else if not (TokenType in [ttIdentifier]) then
|
else if not (TokenType in [ttIdentifier]) then
|
||||||
ParserError(Format(SerrUnknownTokenAtPos, [Scanner.Pos, CurrentToken]))
|
ParserError(Format(SerrUnknownTokenAtPos, [Scanner.Pos, CurrentToken]))
|
||||||
else
|
else
|
||||||
@ -2648,6 +2661,28 @@ begin
|
|||||||
FValue.ResError := AValue;
|
FValue.ResError := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
constructor TsConstExprNode.CreateError(AParser: TsExpressionParser;
|
||||||
|
AValue: String);
|
||||||
|
var
|
||||||
|
err: TsErrorValue;
|
||||||
|
begin
|
||||||
|
if AValue = '#NULL!' then
|
||||||
|
err := errEmptyIntersection
|
||||||
|
else if AValue = '#DIV/0!' then
|
||||||
|
err := errDivideByZero
|
||||||
|
else if AValue = '#VALUE!' then
|
||||||
|
err := errWrongType
|
||||||
|
else if AVAlue = '#REF!' then
|
||||||
|
err := errIllegalRef
|
||||||
|
else if AVAlue = '#NAME?' then
|
||||||
|
err := errWrongName
|
||||||
|
else if AValue = '#FORMULA?' then
|
||||||
|
err := errFormulaNotSupported
|
||||||
|
else
|
||||||
|
AParser.ParserError('Unknown error type.');
|
||||||
|
CreateError(AParser, err);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TsConstExprNode.Check;
|
procedure TsConstExprNode.Check;
|
||||||
begin
|
begin
|
||||||
// Nothing to check;
|
// Nothing to check;
|
||||||
@ -2671,6 +2706,7 @@ begin
|
|||||||
rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime, Parser.FFormatSettings) + ''''; // Probably wrong !!!
|
rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime, Parser.FFormatSettings) + ''''; // Probably wrong !!!
|
||||||
rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE';
|
rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE';
|
||||||
rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings);
|
rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings);
|
||||||
|
rtError : Result := GetErrorValueStr(FValue.ResError);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2682,6 +2718,7 @@ begin
|
|||||||
rtDateTime : Result := RPNNumber(FValue.ResDateTime, ANext);
|
rtDateTime : Result := RPNNumber(FValue.ResDateTime, ANext);
|
||||||
rtBoolean : Result := RPNBool(FValue.ResBoolean, ANext);
|
rtBoolean : Result := RPNBool(FValue.ResBoolean, ANext);
|
||||||
rtFloat : Result := RPNNumber(FValue.ResFloat, ANext);
|
rtFloat : Result := RPNNumber(FValue.ResFloat, ANext);
|
||||||
|
rtError : Result := RPNErr(ord(FValue.ResError), ANext);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -5068,9 +5068,13 @@ var
|
|||||||
r, c, rr, cc: Cardinal;
|
r, c, rr, cc: Cardinal;
|
||||||
r1, c1, r2, c2: Cardinal;
|
r1, c1, r2, c2: Cardinal;
|
||||||
cell, nextcell, basecell: PCell;
|
cell, nextcell, basecell: PCell;
|
||||||
|
lastCol, lastRow: Cardinal;
|
||||||
begin
|
begin
|
||||||
|
lastCol := GetLastColIndex;
|
||||||
|
lastRow := GetLastOccupiedRowIndex;
|
||||||
|
|
||||||
// Loop along the column to be deleted and fix merged cells and shared formulas
|
// Loop along the column to be deleted and fix merged cells and shared formulas
|
||||||
for r := 0 to GetLastRowIndex do
|
for r := 0 to lastRow do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(r, ACol);
|
cell := FindCell(r, ACol);
|
||||||
|
|
||||||
@ -5099,8 +5103,8 @@ begin
|
|||||||
// Write adapted formula to the cell below.
|
// Write adapted formula to the cell below.
|
||||||
WriteFormula(nextcell, basecell^.Formulavalue); //ReadFormulaAsString(nextcell));
|
WriteFormula(nextcell, basecell^.Formulavalue); //ReadFormulaAsString(nextcell));
|
||||||
// Have all cells sharing the formula use the new formula base
|
// Have all cells sharing the formula use the new formula base
|
||||||
for rr := r to GetLastOccupiedRowIndex do
|
for rr := r to lastRow do
|
||||||
for cc := ACol+1 to GetLastOccupiedColIndex do
|
for cc := ACol+1 to lastCol do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(rr, cc);
|
cell := FindCell(rr, cc);
|
||||||
if (cell <> nil) and (cell^.SharedFormulaBase = basecell) then
|
if (cell <> nil) and (cell^.SharedFormulaBase = basecell) then
|
||||||
@ -5112,7 +5116,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Delete cells
|
// Delete cells
|
||||||
for r := GetLastRowIndex downto 0 do
|
for r := lastRow downto 0 do
|
||||||
RemoveCell(r, ACol);
|
RemoveCell(r, ACol);
|
||||||
|
|
||||||
// Update column index of cell records
|
// Update column index of cell records
|
||||||
|
@ -23,6 +23,7 @@ type
|
|||||||
DeleteCol: Integer;
|
DeleteCol: Integer;
|
||||||
DeleteRow: Integer;
|
DeleteRow: Integer;
|
||||||
Formula: String;
|
Formula: String;
|
||||||
|
SollFormula: String;
|
||||||
SharedFormulaRowCount: Integer;
|
SharedFormulaRowCount: Integer;
|
||||||
SharedFormulaColCount: Integer;
|
SharedFormulaColCount: Integer;
|
||||||
MergedColCount: Integer;
|
MergedColCount: Integer;
|
||||||
@ -31,7 +32,7 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
InsDelTestData: array[0..5] of TInsDelTestDataItem;
|
InsDelTestData: array[0..21] of TInsDelTestDataItem;
|
||||||
|
|
||||||
procedure InitTestData;
|
procedure InitTestData;
|
||||||
|
|
||||||
@ -54,6 +55,30 @@ type
|
|||||||
procedure TestWriteRead_InsDelColRow_3; // first
|
procedure TestWriteRead_InsDelColRow_3; // first
|
||||||
procedure TestWriteRead_InsDelColRow_4; // middle
|
procedure TestWriteRead_InsDelColRow_4; // middle
|
||||||
procedure TestWriteRead_InsDelColRow_5; // last
|
procedure TestWriteRead_InsDelColRow_5; // last
|
||||||
|
// Writes out simple cell layout and inserts rows
|
||||||
|
procedure TestWriteRead_InsDelColRow_6; // before first
|
||||||
|
procedure TestWriteRead_InsDelColRow_7; // middle
|
||||||
|
procedure TestWriteRead_InsDelColRow_8; // before last
|
||||||
|
// Writes out simple cell layout and deletes rows
|
||||||
|
procedure TestWriteRead_InsDelColRow_9; // first
|
||||||
|
procedure TestWriteRead_InsDelColRow_10; // middle
|
||||||
|
procedure TestWriteRead_InsDelColRow_11; // last
|
||||||
|
|
||||||
|
// Writes out cell layout with formula and inserts columns
|
||||||
|
procedure TestWriteRead_InsDelColRow_12; // before formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_13; // after formula cell
|
||||||
|
// Writes out cell layout with formula and inserts rows
|
||||||
|
procedure TestWriteRead_InsDelColRow_14; // before formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_15; // after formula cell
|
||||||
|
// Writes out cell layout with formula and deletes columns
|
||||||
|
procedure TestWriteRead_InsDelColRow_16; // before formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_17; // after formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_18; // cell in formula
|
||||||
|
// Writes out cell layout with formula and deletes rows
|
||||||
|
procedure TestWriteRead_InsDelColRow_19; // before formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_20; // after formula cell
|
||||||
|
procedure TestWriteRead_InsDelColRow_21; // cell in formula
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@ -77,12 +102,17 @@ begin
|
|||||||
DeleteCol := -1;
|
DeleteCol := -1;
|
||||||
DeleteRow := -1;
|
DeleteRow := -1;
|
||||||
Formula := '';
|
Formula := '';
|
||||||
|
SollFormula := '';
|
||||||
SharedFormulaColCount := 0;
|
SharedFormulaColCount := 0;
|
||||||
SharedFormulaRowCount := 0;
|
SharedFormulaRowCount := 0;
|
||||||
MergedColCount := 0;
|
MergedColCount := 0;
|
||||||
MergedRowCount := 0;
|
MergedRowCount := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ ---------------------------------------------------------------------------}
|
||||||
|
{ Simple layouts }
|
||||||
|
{ ---------------------------------------------------------------------------}
|
||||||
|
|
||||||
// Insert a column before col 0
|
// Insert a column before col 0
|
||||||
with InsDelTestData[0] do begin
|
with InsDelTestData[0] do begin
|
||||||
Layout := '12345678|'+
|
Layout := '12345678|'+
|
||||||
@ -160,6 +190,305 @@ begin
|
|||||||
'3456789|'+
|
'3456789|'+
|
||||||
'4567890';
|
'4567890';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Insert a ROW before row 0
|
||||||
|
with InsDelTestData[6] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
InsertRow := 0;
|
||||||
|
SollLayout := ' |'+
|
||||||
|
'12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Insert a ROW before row 2
|
||||||
|
with InsDelTestData[7] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
InsertRow := 2;
|
||||||
|
SollLayout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
' |'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Insert a ROW before last row
|
||||||
|
with InsDelTestData[8] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
InsertRow := 5;
|
||||||
|
SollLayout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
' |'+
|
||||||
|
'67890|';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Delete the first row
|
||||||
|
with InsDelTestData[9] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
DeleteRow := 0;
|
||||||
|
SollLayout := '23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Delete row #2
|
||||||
|
with InsDelTestData[10] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
DeleteRow := 2;
|
||||||
|
SollLayout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Delete last row
|
||||||
|
with InsDelTestData[11] do begin
|
||||||
|
Layout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789|'+
|
||||||
|
'67890|';
|
||||||
|
DeleteRow := 5;
|
||||||
|
SollLayout := '12345|'+
|
||||||
|
'23456|'+
|
||||||
|
'34567|'+
|
||||||
|
'45678|'+
|
||||||
|
'56789';
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ ---------------------------------------------------------------------------}
|
||||||
|
{ Layouts with formula }
|
||||||
|
{ ---------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
// Insert a column before #1, i.e. before formula cell
|
||||||
|
with InsDelTestData[12] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
InsertCol := 1;
|
||||||
|
Formula := 'C3';
|
||||||
|
SollFormula := 'D3'; // col index increases due to inserted col
|
||||||
|
SollLayout := '1 2345678|'+
|
||||||
|
'2 3456789|'+
|
||||||
|
'3 4565890|'+
|
||||||
|
'4 5678901|'+
|
||||||
|
'5 6789012|'+
|
||||||
|
'6 7890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Insert a column before #3, i.e. after formula cell
|
||||||
|
with InsDelTestData[13] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
InsertCol := 3;
|
||||||
|
Formula := 'C3';
|
||||||
|
SollFormula := 'C3'; // no change of cell because insertion is behind
|
||||||
|
SollLayout := '123 45678|'+
|
||||||
|
'234 56789|'+
|
||||||
|
'345 65890|'+
|
||||||
|
'456 78901|'+
|
||||||
|
'567 89012|'+
|
||||||
|
'678 90123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Insert a row before #1, i.e. before formula cell
|
||||||
|
with InsDelTestData[14] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
InsertRow := 1;
|
||||||
|
Formula := 'E4';
|
||||||
|
SollFormula := 'E5'; // row index increaes due to inserted row
|
||||||
|
SollLayout := '12345678|'+
|
||||||
|
' |'+
|
||||||
|
'23456789|'+
|
||||||
|
'34568890|'+
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Insert a row before #4, i.e. after formula cell
|
||||||
|
with InsDelTestData[15] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
InsertRow := 5;
|
||||||
|
Formula := 'E4';
|
||||||
|
SollFormula := 'E4'; // row index not changed dur to insert after cell
|
||||||
|
SollLayout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'34568890|'+
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
' |'+
|
||||||
|
'67890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes column #1, i.e. before formula cell
|
||||||
|
with InsDelTestData[16] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteCol := 1;
|
||||||
|
Formula := 'C3';
|
||||||
|
SollFormula := 'B3'; // col index decreases due to delete before cell
|
||||||
|
SollLayout := '1345678|'+
|
||||||
|
'2456789|'+
|
||||||
|
'3565890|'+
|
||||||
|
'4678901|'+
|
||||||
|
'5789012|'+
|
||||||
|
'6890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes column #5, i.e. after formula cell
|
||||||
|
with InsDelTestData[17] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteCol := 5;
|
||||||
|
Formula := 'C3';
|
||||||
|
SollFormula := 'C3'; // col index unchanged due to deleted after cell
|
||||||
|
SollLayout := '1234578|'+
|
||||||
|
'2345689|'+
|
||||||
|
'3456590|'+
|
||||||
|
'4567801|'+
|
||||||
|
'5678912|'+
|
||||||
|
'6789023';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes column #2, i.e. cell appearing in formula is gone --> #REF! error
|
||||||
|
with InsDelTestData[18] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteCol := 2;
|
||||||
|
Formula := 'C3';
|
||||||
|
SollFormula := '#REF!'; // col index unchanged due to deletion after cell
|
||||||
|
SollLayout := '1245678|'+
|
||||||
|
'2356789|'+
|
||||||
|
'346E890|'+ // "E" = error
|
||||||
|
'4578901|'+
|
||||||
|
'5689012|'+
|
||||||
|
'6790123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes row #1, i.e. before formula cell
|
||||||
|
with InsDelTestData[19] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteRow := 1;
|
||||||
|
Formula := 'E4';
|
||||||
|
SollFormula := 'E3'; // row index decreases due to delete before cell
|
||||||
|
SollLayout := '12345678|'+
|
||||||
|
// '23456789|'+
|
||||||
|
'34568890|'+
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes row #4, i.e. after formula cell
|
||||||
|
with InsDelTestData[20] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteRow := 4;
|
||||||
|
Formula := 'E4';
|
||||||
|
SollFormula := 'E4'; // row index unchanged (delete is after cell)
|
||||||
|
SollLayout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'34568890|'+
|
||||||
|
'45678901|'+
|
||||||
|
// '56789012|'+
|
||||||
|
'67890123';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Deletes row #2, i.e. row containing cell used in formula --> #REF! error!
|
||||||
|
with InsDelTestData[21] do begin
|
||||||
|
Layout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'3456F890|'+ // "F" = Formula in row 2, col 4
|
||||||
|
'45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
DeleteRow := 3;
|
||||||
|
Formula := 'E4';
|
||||||
|
SollFormula := '#REF!';
|
||||||
|
SollLayout := '12345678|'+
|
||||||
|
'23456789|'+
|
||||||
|
'34568890|'+
|
||||||
|
// '45678901|'+
|
||||||
|
'56789012|'+
|
||||||
|
'67890123';
|
||||||
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -211,6 +540,7 @@ begin
|
|||||||
for col := 0 to Length(s)-1 do
|
for col := 0 to Length(s)-1 do
|
||||||
case s[col+1] of
|
case s[col+1] of
|
||||||
'0'..'9': MyWorksheet.WriteNumber(row, col, StrToInt(s[col+1]));
|
'0'..'9': MyWorksheet.WriteNumber(row, col, StrToInt(s[col+1]));
|
||||||
|
'F' : MyWorksheet.WriteFormula(row, col, InsDelTestData[ATestIndex].Formula);
|
||||||
' ' : ;
|
' ' : ;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -237,7 +567,9 @@ begin
|
|||||||
// Open the spreadsheet
|
// Open the spreadsheet
|
||||||
MyWorkbook := TsWorkbook.Create;
|
MyWorkbook := TsWorkbook.Create;
|
||||||
try
|
try
|
||||||
|
MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas, boAutoCalc];
|
||||||
MyWorkbook.ReadFromFile(TempFile, AFormat);
|
MyWorkbook.ReadFromFile(TempFile, AFormat);
|
||||||
|
|
||||||
if AFormat = sfExcel2 then
|
if AFormat = sfExcel2 then
|
||||||
MyWorksheet := MyWorkbook.GetFirstWorksheet
|
MyWorksheet := MyWorkbook.GetFirstWorksheet
|
||||||
else
|
else
|
||||||
@ -253,10 +585,19 @@ begin
|
|||||||
MyCell := MyWorksheet.FindCell(row, col);
|
MyCell := MyWorksheet.FindCell(row, col);
|
||||||
if MyCell = nil then
|
if MyCell = nil then
|
||||||
actual := actual + ' '
|
actual := actual + ' '
|
||||||
else
|
else begin
|
||||||
case MyCell^.ContentType of
|
case MyCell^.ContentType of
|
||||||
cctEmpty : actual := actual + ' ';
|
cctEmpty : actual := actual + ' ';
|
||||||
cctNumber: actual := actual + IntToStr(Round(Mycell^.NumberValue));
|
cctNumber: actual := actual + IntToStr(Round(Mycell^.NumberValue));
|
||||||
|
cctError : actual := actual + 'E';
|
||||||
|
end;
|
||||||
|
if HasFormula(MyCell) then begin
|
||||||
|
CheckEquals(
|
||||||
|
MyWorksheet.ReadFormulaAsString(MyCell),
|
||||||
|
InsDelTestData[ATestIndex].SollFormula,
|
||||||
|
'Formula mismatch, cell '+CellNotation(MyWorksheet, Row, Col)
|
||||||
|
);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
CheckEquals(actual, expected,
|
CheckEquals(actual, expected,
|
||||||
@ -309,6 +650,101 @@ begin
|
|||||||
TestWriteRead_InsDelColRow(5);
|
TestWriteRead_InsDelColRow(5);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_6;
|
||||||
|
// insert row before first one
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(6);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_7;
|
||||||
|
// insert row before #2
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(7);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_8;
|
||||||
|
// insert row before last one
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(8);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_9;
|
||||||
|
// delete first row
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(9);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_10;
|
||||||
|
// delete row #2
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(10);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_11;
|
||||||
|
// delete last row
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(11);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_12;
|
||||||
|
// insert column before formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(12);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_13;
|
||||||
|
// insert column after formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(13);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_14;
|
||||||
|
// insert row before formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(14);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_15;
|
||||||
|
// insert row after formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(15);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_16;
|
||||||
|
// delete column before formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(16);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_17;
|
||||||
|
// delete column after formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(17);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_18;
|
||||||
|
// delete column containing a cell used in formula
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(18);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_19;
|
||||||
|
// delete row before formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(19);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_20;
|
||||||
|
// delete row after formula cell
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(20);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteRead_InsDelColRow_Tests.TestWriteRead_InsDelColRow_21;
|
||||||
|
// delete row containing a cell used in formula
|
||||||
|
begin
|
||||||
|
TestWriteRead_InsDelColRow(21);
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
RegisterTest(TSpreadWriteRead_InsDelColRow_Tests);
|
RegisterTest(TSpreadWriteRead_InsDelColRow_Tests);
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
<Unit1>
|
<Unit1>
|
||||||
<Filename Value="datetests.pas"/>
|
<Filename Value="datetests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="datetests"/>
|
||||||
</Unit1>
|
</Unit1>
|
||||||
<Unit2>
|
<Unit2>
|
||||||
<Filename Value="stringtests.pas"/>
|
<Filename Value="stringtests.pas"/>
|
||||||
@ -56,7 +57,6 @@
|
|||||||
<Unit3>
|
<Unit3>
|
||||||
<Filename Value="numberstests.pas"/>
|
<Filename Value="numberstests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
<UnitName Value="numberstests"/>
|
|
||||||
</Unit3>
|
</Unit3>
|
||||||
<Unit4>
|
<Unit4>
|
||||||
<Filename Value="manualtests.pas"/>
|
<Filename Value="manualtests.pas"/>
|
||||||
@ -66,20 +66,20 @@
|
|||||||
<Unit5>
|
<Unit5>
|
||||||
<Filename Value="testsutility.pas"/>
|
<Filename Value="testsutility.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
<UnitName Value="testsutility"/>
|
|
||||||
</Unit5>
|
</Unit5>
|
||||||
<Unit6>
|
<Unit6>
|
||||||
<Filename Value="internaltests.pas"/>
|
<Filename Value="internaltests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="internaltests"/>
|
||||||
</Unit6>
|
</Unit6>
|
||||||
<Unit7>
|
<Unit7>
|
||||||
<Filename Value="formattests.pas"/>
|
<Filename Value="formattests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
<UnitName Value="formattests"/>
|
|
||||||
</Unit7>
|
</Unit7>
|
||||||
<Unit8>
|
<Unit8>
|
||||||
<Filename Value="colortests.pas"/>
|
<Filename Value="colortests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="colortests"/>
|
||||||
</Unit8>
|
</Unit8>
|
||||||
<Unit9>
|
<Unit9>
|
||||||
<Filename Value="fonttests.pas"/>
|
<Filename Value="fonttests.pas"/>
|
||||||
@ -106,15 +106,16 @@
|
|||||||
<Unit14>
|
<Unit14>
|
||||||
<Filename Value="emptycelltests.pas"/>
|
<Filename Value="emptycelltests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
<UnitName Value="emptycelltests"/>
|
|
||||||
</Unit14>
|
</Unit14>
|
||||||
<Unit15>
|
<Unit15>
|
||||||
<Filename Value="errortests.pas"/>
|
<Filename Value="errortests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="errortests"/>
|
||||||
</Unit15>
|
</Unit15>
|
||||||
<Unit16>
|
<Unit16>
|
||||||
<Filename Value="virtualmodetests.pas"/>
|
<Filename Value="virtualmodetests.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
|
<UnitName Value="virtualmodetests"/>
|
||||||
</Unit16>
|
</Unit16>
|
||||||
<Unit17>
|
<Unit17>
|
||||||
<Filename Value="insertdeletetests.pas"/>
|
<Filename Value="insertdeletetests.pas"/>
|
||||||
|
Reference in New Issue
Block a user