test(functions): add math, aggregate and stats tests
This commit is contained in:
1 parent
e0d5adb91c
commit
c31cea00f7
3 files changed
+255
No files matched your search
@@ -0,0 +1,97 @@
|
|||||||
|
import 'package:cellar/cellar.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
double num(CellValue value) => (value as NumberValue).value;
|
||||||
|
|
||||||
|
Sheet withGrid() {
|
||||||
|
final sheet = Sheet();
|
||||||
|
final rows = [
|
||||||
|
['North', 'A', '100'],
|
||||||
|
['South', 'A', '150'],
|
||||||
|
['North', 'B', '200'],
|
||||||
|
['South', 'B', '50'],
|
||||||
|
['North', 'A', '75'],
|
||||||
|
];
|
||||||
|
for (var r = 0; r < rows.length; r++) {
|
||||||
|
sheet.setValue('A${r + 1}', rows[r][0]);
|
||||||
|
sheet.setValue('B${r + 1}', rows[r][1]);
|
||||||
|
sheet.setValue('C${r + 1}', rows[r][2]);
|
||||||
|
}
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('aggregate functions', () {
|
||||||
|
test('SUM SUMSQ PRODUCT MIN MAX', () {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('A1', '2');
|
||||||
|
sheet.setValue('A2', '3');
|
||||||
|
sheet.setValue('A3', '4');
|
||||||
|
sheet.setValue('B1', '=SUM(A1:A3)');
|
||||||
|
sheet.setValue('B2', '=SUMSQ(A1:A3)');
|
||||||
|
sheet.setValue('B3', '=PRODUCT(A1:A3)');
|
||||||
|
sheet.setValue('B4', '=MIN(A1:A3)');
|
||||||
|
sheet.setValue('B5', '=MAX(A1:A3)');
|
||||||
|
expect(num(sheet.valueOf('B1')), 9);
|
||||||
|
expect(num(sheet.valueOf('B2')), 29);
|
||||||
|
expect(num(sheet.valueOf('B3')), 24);
|
||||||
|
expect(num(sheet.valueOf('B4')), 2);
|
||||||
|
expect(num(sheet.valueOf('B5')), 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SUMPRODUCT multiplies element wise', () {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('A1', '1');
|
||||||
|
sheet.setValue('A2', '2');
|
||||||
|
sheet.setValue('A3', '3');
|
||||||
|
sheet.setValue('B1', '4');
|
||||||
|
sheet.setValue('B2', '5');
|
||||||
|
sheet.setValue('B3', '6');
|
||||||
|
sheet.setValue('C1', '=SUMPRODUCT(A1:A3, B1:B3)');
|
||||||
|
expect(num(sheet.valueOf('C1')), 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SUMIF with a comparison criterion and a separate sum range', () {
|
||||||
|
final sheet = withGrid();
|
||||||
|
sheet.setValue('E1', '=SUMIF(C1:C5, ">100")');
|
||||||
|
sheet.setValue('E2', '=SUMIF(A1:A5, "North", C1:C5)');
|
||||||
|
expect(num(sheet.valueOf('E1')), 350);
|
||||||
|
expect(num(sheet.valueOf('E2')), 375);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('COUNTIF and AVERAGEIF', () {
|
||||||
|
final sheet = withGrid();
|
||||||
|
sheet.setValue('E1', '=COUNTIF(A1:A5, "North")');
|
||||||
|
sheet.setValue('E2', '=AVERAGEIF(A1:A5, "South", C1:C5)');
|
||||||
|
expect(num(sheet.valueOf('E1')), 3);
|
||||||
|
expect(num(sheet.valueOf('E2')), 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('COUNTIF with a wildcard', () {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('A1', 'apple');
|
||||||
|
sheet.setValue('A2', 'apricot');
|
||||||
|
sheet.setValue('A3', 'banana');
|
||||||
|
sheet.setValue('B1', '=COUNTIF(A1:A3, "ap*")');
|
||||||
|
expect(num(sheet.valueOf('B1')), 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SUMIFS AVERAGEIFS COUNTIFS with two conditions', () {
|
||||||
|
final sheet = withGrid();
|
||||||
|
sheet.setValue('E1', '=SUMIFS(C1:C5, A1:A5, "North", B1:B5, "A")');
|
||||||
|
sheet.setValue('E2', '=COUNTIFS(A1:A5, "North", B1:B5, "A")');
|
||||||
|
sheet.setValue('E3', '=AVERAGEIFS(C1:C5, A1:A5, "North", B1:B5, "A")');
|
||||||
|
expect(num(sheet.valueOf('E1')), 175);
|
||||||
|
expect(num(sheet.valueOf('E2')), 2);
|
||||||
|
expect(num(sheet.valueOf('E3')), 87.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('MAXIFS and MINIFS', () {
|
||||||
|
final sheet = withGrid();
|
||||||
|
sheet.setValue('E1', '=MAXIFS(C1:C5, A1:A5, "North")');
|
||||||
|
sheet.setValue('E2', '=MINIFS(C1:C5, A1:A5, "North")');
|
||||||
|
expect(num(sheet.valueOf('E1')), 200);
|
||||||
|
expect(num(sheet.valueOf('E2')), 75);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import 'package:cellar/cellar.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
CellValue ev(String formula) {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('Z1', formula);
|
||||||
|
return sheet.valueOf('Z1');
|
||||||
|
}
|
||||||
|
|
||||||
|
double num(CellValue value) => (value as NumberValue).value;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('math functions', () {
|
||||||
|
test('rounding family', () {
|
||||||
|
expect(num(ev('=ROUND(3.14159, 2)')), 3.14);
|
||||||
|
expect(num(ev('=ROUNDUP(3.141, 2)')), 3.15);
|
||||||
|
expect(num(ev('=ROUNDDOWN(3.149, 2)')), 3.14);
|
||||||
|
expect(num(ev('=ROUNDUP(-3.141, 2)')), -3.15);
|
||||||
|
expect(num(ev('=MROUND(17, 5)')), 15);
|
||||||
|
expect(num(ev('=TRUNC(9.99)')), 9);
|
||||||
|
expect(num(ev('=INT(-1.1)')), -2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('modulo and quotient', () {
|
||||||
|
expect(num(ev('=MOD(10, 3)')), 1);
|
||||||
|
expect(num(ev('=MOD(-10, 3)')), 2);
|
||||||
|
expect(num(ev('=QUOTIENT(17, 5)')), 3);
|
||||||
|
expect(ev('=QUOTIENT(1, 0)'), const ErrorValue(ErrorKind.divZero));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('powers, roots and logs', () {
|
||||||
|
expect(num(ev('=POWER(2, 10)')), 1024);
|
||||||
|
expect(num(ev('=SQRT(144)')), 12);
|
||||||
|
expect(num(ev('=EXP(0)')), 1);
|
||||||
|
expect(num(ev('=LN(EXP(3))')), closeTo(3, 1e-9));
|
||||||
|
expect(num(ev('=LOG10(1000)')), closeTo(3, 1e-9));
|
||||||
|
expect(num(ev('=LOG(8, 2)')), closeTo(3, 1e-9));
|
||||||
|
expect(ev('=SQRT(-1)'), const ErrorValue(ErrorKind.numError));
|
||||||
|
expect(ev('=LN(0)'), const ErrorValue(ErrorKind.numError));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sign, even, odd', () {
|
||||||
|
expect(num(ev('=SIGN(-7)')), -1);
|
||||||
|
expect(num(ev('=EVEN(3)')), 4);
|
||||||
|
expect(num(ev('=EVEN(-1)')), -2);
|
||||||
|
expect(num(ev('=ODD(2)')), 3);
|
||||||
|
expect(num(ev('=ODD(-4)')), -5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('floor and ceil with significance', () {
|
||||||
|
expect(num(ev('=FLOOR(7.8)')), 7);
|
||||||
|
expect(num(ev('=FLOOR(23, 5)')), 20);
|
||||||
|
expect(num(ev('=CEILING(23, 5)')), 25);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('gcd, lcm, factorial, combinations', () {
|
||||||
|
expect(num(ev('=GCD(24, 36)')), 12);
|
||||||
|
expect(num(ev('=LCM(4, 6)')), 12);
|
||||||
|
expect(num(ev('=FACT(5)')), 120);
|
||||||
|
expect(num(ev('=COMBIN(5, 2)')), 10);
|
||||||
|
expect(ev('=FACT(-1)'), const ErrorValue(ErrorKind.numError));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('trigonometry and conversions', () {
|
||||||
|
expect(num(ev('=SIN(0)')), 0);
|
||||||
|
expect(num(ev('=COS(0)')), 1);
|
||||||
|
expect(num(ev('=DEGREES(PI())')), closeTo(180, 1e-9));
|
||||||
|
expect(num(ev('=RADIANS(180)')), closeTo(3.14159265, 1e-6));
|
||||||
|
expect(num(ev('=ATAN2(1, 1)')), closeTo(0.785398, 1e-5));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('RANDBETWEEN stays within bounds', () {
|
||||||
|
for (var i = 0; i < 50; i++) {
|
||||||
|
final value = num(ev('=RANDBETWEEN(3, 7)'));
|
||||||
|
expect(value, inInclusiveRange(3, 7));
|
||||||
|
expect(value, value.roundToDouble());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:cellar/cellar.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
double num(CellValue value) => (value as NumberValue).value;
|
||||||
|
|
||||||
|
Sheet withColumn(List<String> values) {
|
||||||
|
final sheet = Sheet();
|
||||||
|
for (var i = 0; i < values.length; i++) {
|
||||||
|
sheet.setValue('A${i + 1}', values[i]);
|
||||||
|
}
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('statistics functions', () {
|
||||||
|
test('MEDIAN', () {
|
||||||
|
final odd = withColumn(['3', '1', '2']);
|
||||||
|
odd.setValue('B1', '=MEDIAN(A1:A3)');
|
||||||
|
expect(num(odd.valueOf('B1')), 2);
|
||||||
|
final even = withColumn(['1', '2', '3', '4']);
|
||||||
|
even.setValue('B1', '=MEDIAN(A1:A4)');
|
||||||
|
expect(num(even.valueOf('B1')), 2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('MODE finds the most frequent value', () {
|
||||||
|
final sheet = withColumn(['2', '4', '4', '7', '4', '2']);
|
||||||
|
sheet.setValue('B1', '=MODE(A1:A6)');
|
||||||
|
expect(num(sheet.valueOf('B1')), 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('COUNT COUNTA COUNTBLANK', () {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('A1', '10');
|
||||||
|
sheet.setValue('A2', 'text');
|
||||||
|
sheet.setValue('A4', '5');
|
||||||
|
sheet.setValue('C1', '=COUNT(A1:A4)');
|
||||||
|
sheet.setValue('C2', '=COUNTA(A1:A4)');
|
||||||
|
sheet.setValue('C3', '=COUNTBLANK(A1:A4)');
|
||||||
|
expect(num(sheet.valueOf('C1')), 2);
|
||||||
|
expect(num(sheet.valueOf('C2')), 3);
|
||||||
|
expect(num(sheet.valueOf('C3')), 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('variance and standard deviation', () {
|
||||||
|
final sheet = withColumn(['2', '4', '4', '4', '5', '5', '7', '9']);
|
||||||
|
sheet.setValue('B1', '=VARP(A1:A8)');
|
||||||
|
sheet.setValue('B2', '=STDEVP(A1:A8)');
|
||||||
|
sheet.setValue('B3', '=VAR(A1:A8)');
|
||||||
|
expect(num(sheet.valueOf('B1')), closeTo(4, 1e-9));
|
||||||
|
expect(num(sheet.valueOf('B2')), closeTo(2, 1e-9));
|
||||||
|
expect(num(sheet.valueOf('B3')), closeTo(4.5714286, 1e-6));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('LARGE SMALL RANK', () {
|
||||||
|
final sheet = withColumn(['5', '1', '9', '3']);
|
||||||
|
sheet.setValue('B1', '=LARGE(A1:A4, 1)');
|
||||||
|
sheet.setValue('B2', '=SMALL(A1:A4, 2)');
|
||||||
|
sheet.setValue('B3', '=RANK(9, A1:A4)');
|
||||||
|
sheet.setValue('B4', '=RANK(1, A1:A4, 1)');
|
||||||
|
expect(num(sheet.valueOf('B1')), 9);
|
||||||
|
expect(num(sheet.valueOf('B2')), 3);
|
||||||
|
expect(num(sheet.valueOf('B3')), 1);
|
||||||
|
expect(num(sheet.valueOf('B4')), 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('PERCENTILE interpolates', () {
|
||||||
|
final sheet = withColumn(['1', '2', '3', '4']);
|
||||||
|
sheet.setValue('B1', '=PERCENTILE(A1:A4, 0.5)');
|
||||||
|
expect(num(sheet.valueOf('B1')), 2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('AVERAGE of an empty selection is #DIV/0!', () {
|
||||||
|
final sheet = Sheet();
|
||||||
|
sheet.setValue('B1', '=AVERAGE(A1:A3)');
|
||||||
|
expect(sheet.valueOf('B1'), const ErrorValue(ErrorKind.divZero));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in new issue
Block a user