FPSpreadsheet: Add more unit test cases for COUNTIF and SUMIF.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9605 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2025-01-26 23:31:57 +00:00
parent 35839d3bc3
commit e66040bd5c
3 changed files with 331 additions and 61 deletions

View File

@ -855,10 +855,11 @@ function TokenName(AToken: TsTokenType): String;
function ResultTypeName(AResult: TsResultType): String; function ResultTypeName(AResult: TsResultType): String;
function CharToResultType(C: Char): TsResultType; function CharToResultType(C: Char): TsResultType;
function BuiltinIdentifiers: TsBuiltInExpressionManager; function BuiltinIdentifiers: TsBuiltInExpressionManager;
function ArgToBoolean(Arg: TsExpressionResult): Boolean; function ArgToBoolean(Arg: TsExpressionResult; Strict: Boolean): Boolean;
function ArgToCell(Arg: TsExpressionResult): PCell; function ArgToCell(Arg: TsExpressionResult): PCell;
function ArgToDateTime(Arg: TsExpressionResult): TDateTime; function ArgToDateTime(Arg: TsExpressionResult): TDateTime;
function ArgToInt(Arg: TsExpressionResult): Integer; function ArgToInt(Arg: TsExpressionResult): Integer;
function ArgToError(Arg: TsExpressionResult): TsErrorValue;
function ArgToFloat(Arg: TsExpressionResult): TsExprFloat; function ArgToFloat(Arg: TsExpressionResult): TsExprFloat;
function ArgToFloatOrNaN(Arg: TsExpressionResult): TsExprFloat; function ArgToFloatOrNaN(Arg: TsExpressionResult): TsExprFloat;
function ArgToString(Arg: TsExpressionResult): String; function ArgToString(Arg: TsExpressionResult): String;
@ -4786,7 +4787,7 @@ end;
{ Conversion of arguments to simple data types } { Conversion of arguments to simple data types }
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
function ArgToBoolean(Arg: TsExpressionResult): Boolean; function ArgToBoolean(Arg: TsExpressionResult; Strict: Boolean): Boolean;
var var
cell: PCell; cell: PCell;
x: Double; x: Double;
@ -4804,10 +4805,14 @@ begin
cctBool: cctBool:
Result := cell^.BoolValue; Result := cell^.BoolValue;
else else
if not Strict then
begin
x := ArgToFloatOrNaN(Arg); x := ArgToFloatOrNaN(Arg);
Result := not IsNaN(x) and (x <> 0.0); Result := not IsNaN(x) and (x <> 0.0);
end; end;
end;
end else end else
if not Strict then
begin begin
x := ArgToFloatOrNaN(Arg); x := ArgToFloatOrNaN(Arg);
Result := not IsNaN(x) and (x <> 0.0); Result := not IsNaN(x) and (x <> 0.0);
@ -4848,6 +4853,23 @@ begin
end; end;
end; end;
function ArgToError(Arg: TsExpressionResult): TsErrorValue;
var
cell: PCell;
begin
Result := errOK;
case Arg.ResultType of
rtError:
Result := Arg.ResError;
rtCell:
begin
cell := ArgToCell(Arg);
if Assigned(cell) and (cell^.ContentType = cctError) then
Result := cell^.ErrorValue;
end;
end;
end;
{ Utility function for the built-in math functions. Accepts also integers and { Utility function for the built-in math functions. Accepts also integers and
other data types in place of floating point arguments. To be called in other data types in place of floating point arguments. To be called in
builtins or user-defined callbacks having float results or arguments. } builtins or user-defined callbacks having float results or arguments. }

View File

@ -75,7 +75,7 @@ end;
type type
TsFuncType = (ftCountIF, ftCountIFS, ftSumIF, ftSUMIFS, ftAverageIF, ftAverageIFS); TsFuncType = (ftCountIF, ftCountIFS, ftSumIF, ftSUMIFS, ftAverageIF, ftAverageIFS);
TsCompareType = (ctNumber, ctString, ctEmpty); TsCompareType = (ctNumber, ctString, ctBoolean, ctEmpty, ctError);
{ Helper class for calculating COUNTIF(S) or SUMIF(S) or AVERAGEIF(S) formulas. { Helper class for calculating COUNTIF(S) or SUMIF(S) or AVERAGEIF(S) formulas.
@ -101,14 +101,19 @@ type
FCompareOperation: TsCompareOperation; FCompareOperation: TsCompareOperation;
FCompareType: TsCompareType; FCompareType: TsCompareType;
FCompareNumber: Double; FCompareNumber: Double;
FCompareBoolean: Boolean;
FCompareError: TsErrorValue;
FCompareString: String; FCompareString: String;
FFormatSettings: TFormatSettings; FFormatSettings: TFormatSettings;
FError: TsErrorValue;
protected protected
function CompareArg(ArgIndex: Integer): Boolean; function CompareArg(ArgIndex: Integer): Boolean;
function CompareBoolean(AValue: Boolean): Boolean;
function CompareCell(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Boolean; function CompareCell(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Boolean;
function CompareEmpty(AEmpty: Boolean): Boolean;
function CompareError(AError: TsErrorValue): Boolean;
function CompareNumber(ANumber: Double): Boolean; function CompareNumber(ANumber: Double): Boolean;
function CompareString(AString: String): Boolean; function CompareString(AString: String): Boolean;
function CompareEmpty(AEmpty: Boolean): Boolean;
function GetArgValue(ArgIndex: Integer): Double; function GetArgValue(ArgIndex: Integer): Double;
function GetCellValue(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Double; function GetCellValue(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Double;
procedure GetCompareParams(ArgIndex: Integer); procedure GetCompareParams(ArgIndex: Integer);
@ -136,6 +141,21 @@ begin
FFuncType := AFuncType; FFuncType := AFuncType;
end; end;
function TsFuncComparer.CompareBoolean(AValue: Boolean): Boolean;
var
val: Double;
begin
Result := false;
case FCompareOperation of
coEqual : if AValue = FCompareBoolean then Result := true;
coLess : if AValue < FCompareBoolean then Result := true;
coGreater : if AValue > FCompareBoolean then Result := true;
coLessEqual : if AValue <= FCompareBoolean then Result := true;
coGreaterEqual : if AValue >= FCompareBoolean then Result := true;
coNotEqual : if AValue <> FCompareBoolean then Result := true;
end;
end;
function TsFuncComparer.CompareNumber(ANumber: Double): Boolean; function TsFuncComparer.CompareNumber(ANumber: Double): Boolean;
begin begin
Result := false; Result := false;
@ -172,12 +192,23 @@ begin
end; end;
end; end;
function TsFuncComparer.CompareError(AError: TsErrorValue): Boolean;
begin
Result := false;
case FCompareOperation of
coEqual : Result := AError = FCompareError;
coNotEqual: Result := AError <> FCompareError;
end;
end;
function TsFuncComparer.CompareArg(ArgIndex: Integer): Boolean; function TsFuncComparer.CompareArg(ArgIndex: Integer): Boolean;
begin begin
case FCompareType of case FCompareType of
ctNumber : Result := CompareNumber(ArgToFloat(FArgs[ArgIndex])); ctNumber : Result := CompareNumber(ArgToFloat(FArgs[ArgIndex]));
ctString : Result := CompareString(ArgToString(FArgs[ArgIndex])); ctString : Result := CompareString(ArgToString(FArgs[ArgIndex]));
ctBoolean: Result := CompareBoolean(ArgToBoolean(FArgs[ArgIndex], true));
ctEmpty : Result := CompareEmpty((ArgToString(FArgs[ArgIndex])) = ''); ctEmpty : Result := CompareEmpty((ArgToString(FArgs[ArgIndex])) = '');
ctError : Result := CompareError(ArgToError(FArgs[ArgIndex]));
else Result := false; else Result := false;
end end
end; end;
@ -185,6 +216,7 @@ end;
function TsFuncComparer.CompareCell(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Boolean; function TsFuncComparer.CompareCell(ASheet: TsBasicWorksheet; ARow, ACol: Integer): Boolean;
var var
cell: PCell; cell: PCell;
value: Double;
begin begin
Result := false; Result := false;
cell := TsWorksheet(ASheet).FindCell(ARow, ACol); cell := TsWorksheet(ASheet).FindCell(ARow, ACol);
@ -203,7 +235,17 @@ begin
cctDateTime: cctDateTime:
Result := CompareNumber(cell^.DateTimeValue); Result := CompareNumber(cell^.DateTimeValue);
cctBool: cctBool:
Result := CompareNumber(IfThen(cell^.Boolvalue, 1, 0)); if FFuncType <> ftCountIF then
Result := CompareBoolean(cell^.Boolvalue);
cctUTF8String:
begin
if TryStrToFloat(cell^.UTF8StringValue, value) then
Result := CompareNumber(value);
{
if not TryStrToFloat(cell^.UTF8StringValue, value) then value := 0.0;
Result := CompareNumber(value);
}
end;
end; end;
end; end;
ctString: ctString:
@ -215,8 +257,20 @@ begin
if (cell^.ContentType = cctUTF8String) then if (cell^.ContentType = cctUTF8String) then
Result := CompareString(cell^.Utf8StringValue); Result := CompareString(cell^.Utf8StringValue);
end; end;
ctBoolean:
if (FCompareOperation = coNotEqual) and ((cell = nil) or (cell^.ContentType <> cctBool)) then
Result := true
else
if (cell <> nil) and (cell^.ContentType = cctBool) then
Result := CompareBoolean(cell^.BoolValue);
ctEmpty: ctEmpty:
Result := CompareEmpty((cell = nil) or ((cell <> nil) and (cell^.ContentType = cctEmpty))); Result := CompareEmpty((cell = nil) or ((cell <> nil) and (cell^.ContentType = cctEmpty)));
ctError:
if (FCompareOperation = coNotEqual) and ((cell = nil) or (cell^.ContentType <> cctError)) then
Result := true
else
if (cell <> nil) and (cell^.ContentType = cctError) then
REsult := CompareError(cell^.ErrorValue);
end; end;
end; end;
@ -231,9 +285,10 @@ var
valueSheet: TsBasicWorksheet; valueSheet: TsBasicWorksheet;
matches: Boolean; matches: Boolean;
count: Integer; count: Integer;
sum: Double; val, sum: Double;
begin begin
Result := ErrorResult(errArgError); Result := ErrorResult(errArgError);
FError := errOK;
if not ValidParams(Result) then if not ValidParams(Result) then
exit; exit;
@ -304,7 +359,16 @@ begin
inc(count); inc(count);
case FArgs[FValueRangeIndex].ResultType of case FArgs[FValueRangeIndex].ResultType of
rtCell: sum := sum + GetArgValue(FValueRangeIndex); rtCell: sum := sum + GetArgValue(FValueRangeIndex);
rtCellRange: sum := sum + GetCellValue(valuesheet, r, c); rtCellRange:
begin
val := GetCellValue(valuesheet, r, c);
if not (FFuncType in [ftCountIF, ftCountIFS]) and (FError <> errOK) then
begin
Result := ErrorResult(FError);
exit;
end;
sum := sum + val;
end;
end; end;
end; end;
end; // for c end; // for c
@ -342,6 +406,8 @@ begin
cctNumber : Result := cell^.NumberValue; cctNumber : Result := cell^.NumberValue;
cctDateTime: Result := cell^.DateTimeValue; cctDateTime: Result := cell^.DateTimeValue;
cctBool : if cell^.BoolValue then Result := 1.0; cctBool : if cell^.BoolValue then Result := 1.0;
cctUTF8String : if not TryStrToFloat(cell^.UTF8StringValue, Result) then Result := 0.0;
cctError: FError := cell^.ErrorValue;
end; end;
end; end;
end; end;
@ -377,8 +443,8 @@ begin
end; end;
cctBool: cctBool:
begin begin
if cell^.BoolValue then FCompareNumber := 1.0 else FCompareNumber := 0.0; FCompareBoolean := cell^.BoolValue;
FCompareType := ctNumber; FCompareType := ctBoolean;
end; end;
cctUTF8String: cctUTF8String:
begin begin
@ -390,7 +456,10 @@ begin
FCompareType := ctEmpty; FCompareType := ctEmpty;
end; end;
cctError: cctError:
; // what to do here? begin
FCompareError := cell^.ErrorValue;
FCompareType := ctError;
end;
end; end;
end else end else
begin begin
@ -400,6 +469,16 @@ begin
if s = '' then if s = '' then
FCompareType := ctEmpty FCompareType := ctEmpty
else else
if (FArgs[ArgIndex].ResultType = rtError) then
begin
FCompareError := FArgs[ArgIndex].ResError;
FCompareType := ctError;
end else
if (FArgs[ArgIndex].ResultType = rtBoolean) then
begin
FCompareBoolean := FArgs[ArgIndex].ResBoolean;
FCompareType := ctBoolean;
end else
if TryStrToInt(s, n) then if TryStrToInt(s, n) then
begin begin
FCompareNumber := n; FCompareNumber := n;
@ -1965,7 +2044,7 @@ begin
Result := ErrorResult(errWrongType); Result := ErrorResult(errWrongType);
exit; exit;
end; end;
if not ArgToBoolean(Args[i]) then begin if not ArgToBoolean(Args[i], false) then begin
b := false; b := false;
break; break;
end; end;
@ -1997,13 +2076,13 @@ begin
begin begin
if IsError(Args[2], Result) then if IsError(Args[2], Result) then
exit; exit;
if ArgToBoolean(Args[0]) then if ArgToBoolean(Args[0], false) then
Result := Args[1] Result := Args[1]
else else
Result := Args[2]; Result := Args[2];
end else end else
begin begin
if ArgToBoolean(Args[0]) then if ArgToBoolean(Args[0], false) then
Result := Args[1] Result := Args[1]
else else
Result.ResBoolean := false; Result.ResBoolean := false;
@ -2027,7 +2106,7 @@ begin
Result := ErrorResult(errWrongType); Result := ErrorResult(errWrongType);
exit; exit;
end; end;
if ArgToBoolean(Args[i]) then if ArgToBoolean(Args[i], false) then
begin begin
Result := Args[i+1]; Result := Args[i+1];
break; break;
@ -2044,7 +2123,7 @@ begin
if (Args[0].ResultType = rtString) then if (Args[0].ResultType = rtString) then
Result := ErrorResult(errWrongType) Result := ErrorResult(errWrongType)
else else
Result.ResBoolean := not ArgToBoolean(Args[0]); Result.ResBoolean := not ArgToBoolean(Args[0], false);
end; end;
// OR( condition1, [condition2], ... ) // OR( condition1, [condition2], ... )
@ -2064,7 +2143,7 @@ begin
Result := ErrorResult(errWrongType); Result := ErrorResult(errWrongType);
exit; exit;
end; end;
if ArgToBoolean(Args[i]) then begin if ArgToBoolean(Args[i], false) then begin
b := true; b := true;
break; break;
end; end;
@ -2345,8 +2424,10 @@ var
begin begin
if IsError(Args[0], AResult) then if IsError(Args[0], AResult) then
exit; exit;
{ Excel still calculates the formula if the "condition" argument contains an error
if IsError(Args[1], AResult) then if IsError(Args[1], AResult) then
exit; exit;
}
if (Length(Args) = 3) and IsError(Args[0], AResult) then if (Length(Args) = 3) and IsError(Args[0], AResult) then
exit; exit;
@ -3018,7 +3099,7 @@ begin
A1Dialect := true; A1Dialect := true;
if (Length(Args) > 3) and (Args[3].ResultType <> rtMissingArg) then if (Length(Args) > 3) and (Args[3].ResultType <> rtMissingArg) then
A1Dialect := ArgToBoolean(Args[3]); A1Dialect := ArgToBoolean(Args[3], false);
sheet := ''; sheet := '';
if Length(Args) > 4 then if Length(Args) > 4 then

View File

@ -27,6 +27,7 @@ type
procedure Test_COUNT; procedure Test_COUNT;
procedure Test_COUNTA; procedure Test_COUNTA;
procedure Test_COUNTBLANK; procedure Test_COUNTBLANK;
procedure Test_COUNTIF;
procedure Test_DATE; procedure Test_DATE;
procedure Test_ERRORTYPE; procedure Test_ERRORTYPE;
procedure Test_EVEN; procedure Test_EVEN;
@ -53,6 +54,7 @@ type
procedure Test_STDEV; procedure Test_STDEV;
procedure Test_STDEVP; procedure Test_STDEVP;
procedure Test_SUM; procedure Test_SUM;
procedure Test_SUMIF;
procedure Test_SUMSQ; procedure Test_SUMSQ;
procedure Test_TIME; procedure Test_TIME;
procedure Test_VAR; procedure Test_VAR;
@ -87,10 +89,10 @@ begin
CheckEquals(10, FWorksheet.ReadAsNumber(0, 1), 'Formula ABS(-10) result mismatch'); CheckEquals(10, FWorksheet.ReadAsNumber(0, 1), 'Formula ABS(-10) result mismatch');
// Error propagation // Error propagation
FWorksheet.WriteFormula(0, 0, '=1/0'); FWorksheet.WriteErrorValue(0, 0, errIllegalRef);
FWorksheet.WriteFormula(0, 1, 'ABS(A1)'); FWorksheet.WriteFormula(0, 1, 'ABS(A1)');
FWorksheet.CalcFormulas; FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula ABS(1/0) result mismatch'); CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 1), 'Formula ABS(1/0) result mismatch');
// Empty argument // Empty argument
FWorksheet.WriteBlank(0, 0); FWorksheet.WriteBlank(0, 0);
@ -139,9 +141,13 @@ begin
FWorksheet.CalcFormulas; FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_WRONG_TYPE, FWorksheet.ReadAsText(cell), 'Formula #9 AND("abc",TRUE) result mismatch'); CheckEquals(STR_ERR_WRONG_TYPE, FWorksheet.ReadAsText(cell), 'Formula #9 AND("abc",TRUE) result mismatch');
cell := FWorksheet.WriteFormula(0, 1, 'AND(1/0,1/0)'); cell := FWorksheet.WriteFormula(0, 1, 'AND(1/0,0)');
FWorksheet.CalcFormulas; FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(cell), 'Formula #10 AND(1/0,1/0) result mismatch'); CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(cell), 'Formula #10 AND(1/0,0) result mismatch');
cell := FWorksheet.WriteFormula(0, 1, 'AND(#REF!,#DIV/0!)');
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(cell), 'Formula #11 AND(#REF!,#DIV/0!) result mismatch');
end; end;
procedure TCalcFormulaTests.Test_AVEDEV; procedure TCalcFormulaTests.Test_AVEDEV;
@ -538,6 +544,74 @@ begin
CheckEquals(1, FWorksheet.ReadAsNumber(4, 1), 'Formula #6 COUNTBLANK(A1:A4) result mismatch'); CheckEquals(1, FWorksheet.ReadAsNumber(4, 1), 'Formula #6 COUNTBLANK(A1:A4) result mismatch');
end; end;
procedure TCalcFormulaTests.Test_COUNTIF;
begin
// Test data, range A1:B5
FWorksheet.WriteNumber (0, 0, 10); FWorksheet.WriteFormula(0, 1, '=SQRT(-1)'); // --> #NUM!
FWorksheet.WriteNumber (1, 0, -20); FWorksheet.WriteBlank (1, 1);
FWorksheet.WriteFormula(2, 0, '=(1=1)'); FWorksheet.WriteNumber (2, 1, 0);
FWorksheet.WriteText (3, 0, ''); FWorksheet.WriteText (3, 1, '5');
FWorksheet.WriteText (4, 0, 'abc'); FWorksheet.WriteText (4, 1, 'ABC');
FWorksheet.WriteBoolValue(5, 0, false); FWorksheet.WriteErrorValue(5, 1, errOverflow); // --> #NUM!
// Counts the elements in A1:B6 which are equal to "abc" (case-insensitive)
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,"abc")');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #1 COUNTIF(A1:B6,"abc") result mismatch');
// Counts the elements in A1:B6 which are < 0
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,"<0")');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #2 COUNTIF(A1:B6,"<0") result mismatch');
// Counts empty elements in A1:B6
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,"")');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #3 COUNTIF(A1:B6,"") result mismatch');
// Counts the elements in A1:B6 which are equal to 0
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,0)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #4 COUNTIF(A1:B6,0) result mismatch');
// Counts the elements in A1:B6 which are TRUE
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,TRUE)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #5 COUNTIF(A1:B6,TRUE) result mismatch');
// Counts the elements in A1:B6 which are FALSE
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,FALSE)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #4 COUNTIF(A1:B6,FALSE) result mismatch');
// Counts the elements in A1:B5 which are #NUM!
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,#NUM!)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #6 COUNTIF(A1:B6,#NUM!) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (empty)
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #7 COUNTIF(A1:B6,A15) (A15 empty) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (value 10)
FWorksheet.WriteNumber(14, 0, 10);
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 2), 'Formula #8 COUNTIF(A1:B6,A15) (A15 = 10) result mismatch');
// Count the elements in A1:B6 which are < cell A15 (value 10)
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,"<"&A15)');
FWorksheet.CalcFormulas;
CheckEquals(3, FWorksheet.ReadAsNumber(0, 2), 'Formula #9 COUNTIF(A1:B6,"<"&A15) (A15 = 10) result mismatch');
// Count the elements in A1:B6 which are equal to cell A15 (error value #NUM!)
FWorksheet.WriteErrorValue(14, 0, errOverflow);
FWorksheet.WriteFormula(0, 2, '=COUNTIF(A1:B6,A15)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(0, 2), 'Formula #10 COUNTIF(A1:B6,A15) (A15 = #NUM!) result mismatch');
end;
procedure TCalcFormulaTests.Test_DATE; procedure TCalcFormulaTests.Test_DATE;
var var
actualDate, expectedDate: TDate; actualDate, expectedDate: TDate;
@ -1843,43 +1917,7 @@ begin
FWorksheet.CalcFormulas; FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 PRODUCT(A:A6) result mismatch'); CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 PRODUCT(A:A6) result mismatch');
end; end;
(*
procedure TCalcFormulaTests.Test_PRODUCT;
begin
FWorksheet.WriteFormula(0, 1, '=PRODUCT(1)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(0, 1), 'Formula #1 PRODUCT(1) result mismatch');
FWorksheet.WriteFormula(0, 1, '=PRODUCT(1,2,3,4)');
FWorksheet.CalcFormulas;
CheckEquals(24, FWorksheet.ReadAsNumber(0, 1), 'Formula #2 PRODUCT(1,2,3,4) result mismatch');
FWorksheet.WriteNumber(0, 0, 1);
FWorksheet.WriteNumber(1, 0, 2);
FWorksheet.WriteNumber(2, 0, 3);
FWorksheet.WriteNumber(3, 0, 4);
FWorksheet.WriteFormula(4, 0, '=PRODUCT(A1)');
FWorksheet.CalcFormulas;
CheckEquals(1, FWorksheet.ReadAsNumber(4, 0), 'Formula #3 PRODUCT(A1) result mismatch');
FWorksheet.WriteFormula(4, 0, '=PRODUCT(A1,A2)');
FWorksheet.CalcFormulas;
CheckEquals(2, FWorksheet.ReadAsNumber(4, 0), 'Formula #4 PRODUCT(A1,A2) result mismatch');
FWorksheet.WriteFormula(4, 0, '=PRODUCT(A1:A4)');
FWorksheet.CalcFormulas;
CheckEquals(24, FWorksheet.ReadAsNumber(4, 0), 'Formula #5 PRODUCT(A1:A4) result mismatch');
FWorksheet.WriteFormula(4, 0, '=PRODUCT(A1,A2:A4)');
FWorksheet.CalcFormulas;
CheckEquals(24, FWorksheet.ReadAsNumber(4, 0), 'Formula #6 PRODUCT(A1,A2:A4) result mismatch');
FWorksheet.WriteFormula(4, 0, '=PRODUCT(A1, 1/0, A2)');
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(4, 0), 'Formula #7 PRODUCT(A1, 1/0, A2) result mismatch');
end;
*)
procedure TCalcFormulaTests.Test_ROUND; procedure TCalcFormulaTests.Test_ROUND;
begin begin
// Round positive value. // Round positive value.
@ -2188,8 +2226,137 @@ begin
CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 SUM(A:A6) result mismatch'); CheckEquals(STR_ERR_DIVIDE_BY_ZERO, FWorksheet.ReadAsText(0, 1), 'Formula #16 SUM(A:A6) result mismatch');
end; end;
{ Counts the sum of numeric elements. procedure TCalcFormulaTests.Test_SUMIF;
Cases checked with Excel } begin
// Test data, range A1:B5
FWorksheet.WriteNumber (0, 0, 10); FWorksheet.WriteNumber (0, 1, -1);
FWorksheet.WriteNumber (1, 0, 20); FWorksheet.WriteNumber (1, 1, -2);
FWorksheet.WriteNumber (2, 0, 40); FWorksheet.WriteNumber (2, 1, 6);
FWorksheet.WriteText (3, 0, '-40'); FWorksheet.WriteText (3, 1, '5');
FWorksheet.WriteText (4, 0, 'abc'); FWorksheet.WriteText (4, 1, 'ABC');
// Work data, range A8:B12
FWorksheet.WriteNumber ( 7, 0, 100); FWorksheet.WriteNumber ( 7, 1, -100);
FWorksheet.WriteNumber ( 8, 0, 200); FWorksheet.WriteNumber ( 8, 1, -200);
FWorksheet.WriteNumber ( 9, 0, 300); FWorksheet.WriteNumber ( 9, 1, -300);
FWorksheet.WriteText (10, 0, '400'); FWorksheet.WriteText (10, 1, '-500');
FWorksheet.WriteText (11, 0, 'xyz'); FWorksheet.WriteText (11, 1, 'XYZ');
// *** Range contains numbers only ***
// Calculate sum of the elements in A1:B3 which are equal to 0
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,0)');
FWorksheet.CalcFormulas;
CheckEquals(0, FWorksheet.ReadAsNumber(0, 2), 'Formula #1 SUMIF(A1:B3,0) result mismatch');
// Calculate sum of the elements in A1:B3 which are < 0
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,"<0")');
FWorksheet.CalcFormulas;
CheckEquals(-3, FWorksheet.ReadAsNumber(0, 2), 'Formula #2 SUMIF(A1:B3,"<0") result mismatch');
// Calculate sum of the elements in A8:B10 for which the elements in A1:B3 are equal to 10
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,10,A8:B10)');
FWorksheet.CalcFormulas;
CheckEquals(100, FWorksheet.ReadAsNumber(0, 2), 'Formula #3 SUMIF(A1:B3,10,A8:B10) result mismatch');
// Compare cell A15
FWorksheet.WriteNumber( 14, 0, 10);
// Calculate sum of the elements in A1:B3 which are equal to cell A15 (value 10)
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,A15)');
FWorksheet.CalcFormulas;
CheckEquals(10, FWorksheet.ReadAsNumber(0, 2), 'Formula #4 SUMIF(A1:B3,A15) result mismatch');
// Calculate sum of the elements in A1:B3 which are < cell A15
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,"<"&A15)');
FWorksheet.CalcFormulas;
CheckEquals(3, FWorksheet.ReadAsNumber(0, 2), 'Formula #5 SUMIF(A1:B3,"<"&A15) result mismatch');
// Calculate sum of the elements in A8:B10 for which the elements in A1:B3 are equal to 10
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B3,"<"&A15,A8:B10)');
FWorksheet.CalcFormulas;
CheckEquals(-600, FWorksheet.ReadAsNumber(0, 2), 'Formula #6 SUMIF(A1:B3,"<"&A15,A8:B10) result mismatch');
// *** Range contains also numeric strings ***
// Calculate sum of the elements in A1:B4 which are equal to -40
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,-40)');
FWorksheet.CalcFormulas;
CheckEquals(-40, FWorksheet.ReadAsNumber(0, 2), 'Formula #7 SUMIF(A1:B4,-40) result mismatch');
// Calculate sum of the elements in A1:B4 which are < 0
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,"<0")');
FWorksheet.CalcFormulas;
CheckEquals(-43, FWorksheet.ReadAsNumber(0, 2), 'Formula #8 SUMIF(A1:B4,"<0") result mismatch');
// Calculate sum of the elements in A8:B11 for which the elements in A1:B4 are equal to -40
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,-40,A8:B11)');
FWorksheet.CalcFormulas;
CheckEquals(400, FWorksheet.ReadAsNumber(0, 2), 'Formula #9 SUMIF(A1:B4,-40,A8:B11) result mismatch');
// Compare cell A15
FWorksheet.WriteNumber( 14, 0, -40);
// Calculate sum of the elements in A1:B4 which are equal to cell A15
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,A15)');
FWorksheet.CalcFormulas;
CheckEquals(-40, FWorksheet.ReadAsNumber(0, 2), 'Formula #10 SUMIF(A1:B4,A15) result mismatch');
// Calculate sum of the elements in A1:B4 which are equal <= cell A15
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,"<="&A15)');
FWorksheet.CalcFormulas;
CheckEquals(-40, FWorksheet.ReadAsNumber(0, 2), 'Formula #11 SUMIF(A1:B4,"<="&A15) result mismatch');
// Calculate sum of the elements in A8:B11 for which the elements in A1:B4 are equal to cell A15
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B4,A15,A8:B11)');
FWorksheet.CalcFormulas;
CheckEquals(400, FWorksheet.ReadAsNumber(0, 2), 'Formula #12 SUMIF(A1:B4,A15,A8:B11) result mismatch');
// *** Range contains also non-numeric strings ***
// Calculate sum of the elements in A1:B5 which are equal to -40
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,-40)');
FWorksheet.CalcFormulas;
CheckEquals(-40, FWorksheet.ReadAsNumber(0, 2), 'Formula #13 SUMIF(A1:B5,-40) result mismatch');
// Calculate sum of the elements in A1:B5 which are < 0
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,"<0")');
FWorksheet.CalcFormulas;
CheckEquals(-43, FWorksheet.ReadAsNumber(0, 2), 'Formula #14 SUMIF(A1:B5,"<0") result mismatch');
// Calculate sum of the elements in A8:B12 for which the elements in A1:B5 are equal to -40
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,-40,A8:B12)');
FWorksheet.CalcFormulas;
CheckEquals(400, FWorksheet.ReadAsNumber(0, 2), 'Formula #15 SUMIF(A1:B5,-40,A8:B12) result mismatch');
// *** Range contains also error cells ***
// Calculate sum of the elements in A1:B5 which are equal to -40 --> error cell must be ignored
FWorksheet.WriteErrorValue(0, 0, errIllegalRef); // add error to A1
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,-40)');
FWorksheet.CalcFormulas;
CheckEquals(-40, FWorksheet.ReadAsNumber(0, 2), 'Formula #16 SUMIF(A1:B5,-40) result mismatch');
// Calculate sum of the elements in A8:B13 for which the elements in A1:B6 are equal to 40
FWorksheet.WriteErrorValue(9, 0, errIllegalRef); // The value corresponding to 40 is an error now
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,40,A8:B12)');
FWorksheet.CalcFormulas;
CheckEquals(STR_ERR_ILLEGAL_REF, FWorksheet.ReadAsText(0, 2), 'Formula #17 SUMIF(A1:B5,40,A8:B12) result mismatch');
// *** Compare cell contains an error (A15)
FWorksheet.WriteFormula( 14, 0, '=1/0');
// Calculate sum of the elements in A1:B5 which are equal to cell A15 (containing #DIV/0!)
FWorksheet.WriteFormula(0, 2, '=SUMIF(A1:B5,A15)');
FWorksheet.CalcFormulas;
CheckEquals(0, FWorksheet.ReadAsNumber(0, 2), 'Formula #18 SUMIF(A1:B5,A15) result mismatch');
end;
procedure TCalcFormulaTests.Test_SUMSQ; procedure TCalcFormulaTests.Test_SUMSQ;
begin begin
// Test data // Test data