blob: 3f4eb88dd8154ce38023dd2b480e9b6b2b4e3b05 [file] [log] [blame]
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.formula.functions;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.formula.eval.AreaEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaError;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.jupiter.api.Test;
/**
* Tests for {@link Subtotal}
*/
final class TestSubtotal {
private static final int FUNCTION_AVERAGE = 1;
private static final int FUNCTION_COUNT = 2;
private static final int FUNCTION_MAX = 4;
private static final int FUNCTION_MIN = 5;
private static final int FUNCTION_PRODUCT = 6;
private static final int FUNCTION_STDEV = 7;
private static final int FUNCTION_SUM = 9;
private static final double[] TEST_VALUES0 = {
1, 2,
3, 4,
5, 6,
7, 8,
9, 10
};
private static void confirmSubtotal(int function, double expected) {
ValueEval[] values = new ValueEval[TEST_VALUES0.length];
for (int i = 0; i < TEST_VALUES0.length; i++) {
values[i] = new NumberEval(TEST_VALUES0[i]);
}
AreaEval arg1 = EvalFactory.createAreaEval("C1:D5", values);
ValueEval[] args = {new NumberEval(function), arg1};
ValueEval result = new Subtotal().evaluate(args, 0, 0);
assertEquals(NumberEval.class, result.getClass());
assertEquals(expected, ((NumberEval) result).getNumberValue(), 0.0);
}
@Test
void testBasics() {
confirmSubtotal(FUNCTION_SUM, 55.0);
confirmSubtotal(FUNCTION_AVERAGE, 5.5);
confirmSubtotal(FUNCTION_COUNT, 10.0);
confirmSubtotal(FUNCTION_MAX, 10.0);
confirmSubtotal(FUNCTION_MIN, 1.0);
confirmSubtotal(FUNCTION_PRODUCT, 3628800.0);
confirmSubtotal(FUNCTION_STDEV, 3.0276503540974917);
}
@Test
void testAvg() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(1,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue(1);
Cell a5 = sh.createRow(5).createCell(1);
a5.setCellValue(7);
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(1,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(1,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(1,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue(), 0);
assertEquals(8.0, a6.getNumericCellValue(), 0);
assertEquals(3.0, a7.getNumericCellValue(), 0);
assertEquals(3.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testSum() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(9,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue(1);
Cell a5 = sh.createRow(5).createCell(1);
a5.setCellValue(7);
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(9,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(9,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(9,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(4.0, a3.getNumericCellValue(), 0);
assertEquals(26.0, a6.getNumericCellValue(), 0);
assertEquals(12.0, a7.getNumericCellValue(), 0);
assertEquals(12.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testCount() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(2,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue("POI"); // A4 is string and not counted
/*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(2,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(2,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(2,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue(), 0);
assertEquals(6.0, a6.getNumericCellValue(), 0);
assertEquals(2.0, a7.getNumericCellValue(), 0);
assertEquals(2.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testCounta() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(3,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue("POI"); // A4 is string and not counted
/*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(3,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(3,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(3,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue(), 0);
assertEquals(8.0, a6.getNumericCellValue(), 0);
assertEquals(3.0, a7.getNumericCellValue(), 0);
assertEquals(3.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testMax() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(4,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue(1);
Cell a5 = sh.createRow(5).createCell(1);
a5.setCellValue(7);
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(4,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(4,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(4,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(3.0, a3.getNumericCellValue(), 0);
assertEquals(16.0, a6.getNumericCellValue(), 0);
assertEquals(7.0, a7.getNumericCellValue(), 0);
assertEquals(7.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testMin() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(5,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue(1);
Cell a5 = sh.createRow(5).createCell(1);
a5.setCellValue(7);
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(5,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(5,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(5,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(1.0, a3.getNumericCellValue(), 0);
assertEquals(4.0, a6.getNumericCellValue(), 0);
assertEquals(1.0, a7.getNumericCellValue(), 0);
assertEquals(1.0, a8.getNumericCellValue(), 0);
wb.close();
}
@Test
void testStdev() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellValue(3);
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(7,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue(1);
Cell a5 = sh.createRow(5).createCell(1);
a5.setCellValue(7);
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(7,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(7,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(7,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(1.41421, a3.getNumericCellValue(), 0.0001);
assertEquals(7.65685, a6.getNumericCellValue(), 0.0001);
assertEquals(2.82842, a7.getNumericCellValue(), 0.0001);
assertEquals(2.82842, a8.getNumericCellValue(), 0.0001);
wb.close();
}
@Test
void test50209() throws IOException {
Workbook wb = new HSSFWorkbook();
Sheet sh = wb.createSheet();
Cell a1 = sh.createRow(1).createCell(1);
a1.setCellValue(1);
Cell a2 = sh.createRow(2).createCell(1);
a2.setCellFormula("SUBTOTAL(9,B2)");
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(9,B2:B3)");
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
fe.evaluateAll();
assertEquals(1.0, a2.getNumericCellValue(), 0);
assertEquals(1.0, a3.getNumericCellValue(), 0);
wb.close();
}
private static void confirmExpectedResult(FormulaEvaluator evaluator, String msg, Cell cell, double expected) {
CellValue value = evaluator.evaluate(cell);
if (value.getErrorValue() != 0)
throw new RuntimeException(msg + ": " + value.formatAsString());
assertEquals(expected, value.getNumberValue(), 0, msg);
}
@Test
void testFunctionsFromTestSpreadsheet() throws IOException {
HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("SubtotalsNested.xls");
HSSFSheet sheet = workbook.getSheetAt(0);
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
assertEquals(10.0, sheet.getRow(1).getCell(1).getNumericCellValue(), 0, "B2");
assertEquals(20.0, sheet.getRow(2).getCell(1).getNumericCellValue(), 0, "B3");
//Test simple subtotal over one area
Cell cellA3 = sheet.getRow(3).getCell(1);
confirmExpectedResult(evaluator, "B4", cellA3, 30.0);
//Test existence of the second area
assertNotNull(sheet.getRow(1).getCell(2), "C2 must not be null");
assertEquals(7.0, sheet.getRow(1).getCell(2).getNumericCellValue(), 0, "C2");
Cell cellC1 = sheet.getRow(1).getCell(3);
Cell cellC2 = sheet.getRow(2).getCell(3);
Cell cellC3 = sheet.getRow(3).getCell(3);
//Test Functions SUM, COUNT and COUNTA calculation of SUBTOTAL
//a) areas A and B are used
//b) first 2 subtotals don't consider the value of nested subtotal in A3
confirmExpectedResult(evaluator, "SUBTOTAL(SUM;B2:B8;C2:C8)", cellC1, 37.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNT;B2:B8,C2:C8)", cellC2, 3.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA;B2:B8,C2:C8)", cellC3, 5.0);
// test same functions ignoring hidden rows over a copy of the same data
cellC1 = sheet.getRow(11).getCell(3);
cellC2 = sheet.getRow(12).getCell(3);
cellC3 = sheet.getRow(13).getCell(3);
confirmExpectedResult(evaluator, "SUBTOTAL(SUM NO HIDDEN;B22:B28;C22:C28)", cellC1, 17.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNT NO HIDDEN;B22:B28,C22:C28)", cellC2, 2.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA NO HIDDEN;B22:B28,C22:C28)", cellC3, 4.0);
workbook.close();
}
@Test
void testUnimplemented() throws IOException {
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a3 = sh.createRow(3).createCell(1);
// formula, throws NotImplemnted?
String[][] formulas = {
{ "SUBTOTAL(8,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(10,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(11,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(0,B2:B3)", null },
{ "SUBTOTAL(9)", FormulaParseException.class.getName() },
{ "SUBTOTAL()", FormulaParseException.class.getName() },
};
for (String[] f : formulas) {
Exception actualEx = null;
try {
a3.setCellFormula(f[0]);
fe.evaluateAll();
assertEquals(FormulaError.VALUE.getCode(), a3.getErrorCellValue(), f[0]);
} catch (Exception e) {
actualEx = e;
}
String msg =
"Check "+(f[1] == null ? "unexpected exception" : f[1])+" here, "+
"adjust these tests if it was actually implemented - "+f[0];
assertEquals(f[1], (actualEx == null ? null : actualEx.getClass().getName()), msg);
}
Subtotal subtotal = new Subtotal();
assertEquals(ErrorEval.VALUE_INVALID, subtotal.evaluate(new ValueEval[] {}, 0, 0));
wb.close();
}
}