123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /* ====================================================================
- 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.xssf.usermodel;
-
- import java.io.InputStream;
- import java.io.PrintStream;
- import java.util.Collection;
-
- import junit.framework.Assert;
- import junit.framework.AssertionFailedError;
- import junit.framework.TestCase;
-
- import org.apache.poi.hssf.HSSFTestDataSamples;
- import org.apache.poi.ss.formula.eval.TestFormulasFromSpreadsheet;
- import org.apache.poi.ss.formula.functions.TestMathX;
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.CellValue;
- import org.apache.poi.ss.usermodel.FormulaEvaluator;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.openxml4j.opc.OPCPackage;
- import org.apache.poi.util.POILogFactory;
- import org.apache.poi.util.POILogger;
-
- /**
- * Tests formulas for multi sheet reference (i.e. SUM(Sheet1:Sheet5!A1))
- */
- public final class TestMultiSheetFormulaEvaluatorOnXSSF extends TestCase {
- private static final POILogger logger = POILogFactory.getLogger(TestFormulasFromSpreadsheet.class);
-
- private static final class Result {
- public static final int SOME_EVALUATIONS_FAILED = -1;
- public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
- public static final int NO_EVALUATIONS_FOUND = 0;
- }
-
- /**
- * This class defines constants for navigating around the test data spreadsheet used for these tests.
- */
- private static final class SS {
-
- /**
- * Name of the test spreadsheet (found in the standard test data folder)
- */
- public final static String FILENAME = "FormulaSheetRange.xlsx";
- /**
- * Row (zero-based) in the test spreadsheet where the function examples start.
- */
- public static final int START_FUNCTIONS_ROW_INDEX = 10; // Row '11'
- /**
- * Index of the column that contains the function names
- */
- public static final int COLUMN_INDEX_FUNCTION_NAME = 0; // Column 'A'
- /**
- * Index of the column that contains the test names
- */
- public static final int COLUMN_INDEX_TEST_NAME = 1; // Column 'B'
- /**
- * Used to indicate when there are no more functions left
- */
- public static final String FUNCTION_NAMES_END_SENTINEL = "<END>";
-
- /**
- * Index of the column where the test expected value is present
- */
- public static final short COLUMN_INDEX_EXPECTED_VALUE = 2; // Column 'C'
- /**
- * Index of the column where the test actual value is present
- */
- public static final short COLUMN_INDEX_ACTUAL_VALUE = 3; // Column 'D'
- /**
- * Test sheet name (sheet with all test formulae)
- */
- public static final String TEST_SHEET_NAME = "test";
- }
-
- private XSSFWorkbook workbook;
- private Sheet sheet;
- // Note - multiple failures are aggregated before ending.
- // If one or more functions fail, a single AssertionFailedError is thrown at the end
- private int _functionFailureCount;
- private int _functionSuccessCount;
- private int _evaluationFailureCount;
- private int _evaluationSuccessCount;
-
- private static void confirmExpectedResult(String msg, Cell expected, CellValue actual) {
- if (expected == null) {
- throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
- }
- if(actual == null) {
- throw new AssertionFailedError(msg + " - actual value was null");
- }
-
- switch (expected.getCellType()) {
- case Cell.CELL_TYPE_BLANK:
- assertEquals(msg, Cell.CELL_TYPE_BLANK, actual.getCellType());
- break;
- case Cell.CELL_TYPE_BOOLEAN:
- assertEquals(msg, Cell.CELL_TYPE_BOOLEAN, actual.getCellType());
- assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
- break;
- case Cell.CELL_TYPE_ERROR:
- assertEquals(msg, Cell.CELL_TYPE_ERROR, actual.getCellType());
- if(false) { // TODO: fix ~45 functions which are currently returning incorrect error values
- assertEquals(msg, expected.getErrorCellValue(), actual.getErrorValue());
- }
- break;
- case Cell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
- throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
- case Cell.CELL_TYPE_NUMERIC:
- assertEquals(msg, Cell.CELL_TYPE_NUMERIC, actual.getCellType());
- TestMathX.assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
- // double delta = Math.abs(expected.getNumericCellValue()-actual.getNumberValue());
- // double pctExpected = Math.abs(0.00001*expected.getNumericCellValue());
- // assertTrue(msg, delta <= pctExpected);
- break;
- case Cell.CELL_TYPE_STRING:
- assertEquals(msg, Cell.CELL_TYPE_STRING, actual.getCellType());
- assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
- break;
- }
- }
-
-
- protected void setUp() throws Exception {
- if (workbook == null) {
- InputStream is = HSSFTestDataSamples.openSampleFileStream(SS.FILENAME);
- OPCPackage pkg = OPCPackage.open(is);
- workbook = new XSSFWorkbook( pkg );
- sheet = workbook.getSheet( SS.TEST_SHEET_NAME );
- }
- _functionFailureCount = 0;
- _functionSuccessCount = 0;
- _evaluationFailureCount = 0;
- _evaluationSuccessCount = 0;
- }
-
- public void testFunctionsFromTestSpreadsheet() {
-
- processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, null);
-
- // confirm results
- String successMsg = "There were "
- + _evaluationSuccessCount + " successful evaluation(s) and "
- + _functionSuccessCount + " function(s) without error";
- if(_functionFailureCount > 0) {
- String msg = _functionFailureCount + " function(s) failed in "
- + _evaluationFailureCount + " evaluation(s). " + successMsg;
- throw new AssertionFailedError(msg);
- }
- logger.log(POILogger.INFO, getClass().getName() + ": " + successMsg);
- }
-
- /**
- * @param startRowIndex row index in the spreadsheet where the first function/operator is found
- * @param testFocusFunctionName name of a single function/operator to test alone.
- * Typically pass <code>null</code> to test all functions
- */
- private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) {
- FormulaEvaluator evaluator = new XSSFFormulaEvaluator(workbook);
-
- int rowIndex = startRowIndex;
- while (true) {
- Row r = sheet.getRow(rowIndex);
-
- // only evaluate non empty row
- if( r != null )
- {
- String targetFunctionName = getTargetFunctionName(r);
- String targetTestName = getTargetTestName(r);
- if(targetFunctionName == null) {
- throw new AssertionFailedError("Test spreadsheet cell empty on row ("
- + (rowIndex+1) + "). Expected function name or '"
- + SS.FUNCTION_NAMES_END_SENTINEL + "'");
- }
- if(targetFunctionName.equals(SS.FUNCTION_NAMES_END_SENTINEL)) {
- // found end of functions list
- break;
- }
- if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
-
- // expected results are on the row below
- Cell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_VALUE);
- if(expectedValueCell == null) {
- int missingRowNum = rowIndex + 1;
- throw new AssertionFailedError("Missing expected values cell for function '"
- + targetFunctionName + ", test" + targetTestName + " (row " +
- missingRowNum + ")");
- }
-
- switch(processFunctionRow(evaluator, targetFunctionName, targetTestName, r, expectedValueCell)) {
- case Result.ALL_EVALUATIONS_SUCCEEDED: _functionSuccessCount++; break;
- case Result.SOME_EVALUATIONS_FAILED: _functionFailureCount++; break;
- default:
- throw new RuntimeException("unexpected result");
- case Result.NO_EVALUATIONS_FOUND: // do nothing
- break;
- }
- }
- }
- rowIndex ++;
- }
- }
-
- /**
- *
- * @return a constant from the local Result class denoting whether there were any evaluation
- * cases, and whether they all succeeded.
- */
- private int processFunctionRow(FormulaEvaluator evaluator, String targetFunctionName,
- String targetTestName, Row formulasRow, Cell expectedValueCell) {
-
- int result = Result.NO_EVALUATIONS_FOUND; // so far
-
- Cell c = formulasRow.getCell(SS.COLUMN_INDEX_ACTUAL_VALUE);
- if (c == null || c.getCellType() != Cell.CELL_TYPE_FORMULA) {
- return result;
- }
-
- CellValue actualValue = evaluator.evaluate(c);
-
- try {
- confirmExpectedResult("Function '" + targetFunctionName + "': Test: '" + targetTestName + "' Formula: " + c.getCellFormula()
- + " @ " + formulasRow.getRowNum() + ":" + SS.COLUMN_INDEX_ACTUAL_VALUE,
- expectedValueCell, actualValue);
- _evaluationSuccessCount ++;
- if(result != Result.SOME_EVALUATIONS_FAILED) {
- result = Result.ALL_EVALUATIONS_SUCCEEDED;
- }
- } catch (AssertionFailedError e) {
- _evaluationFailureCount ++;
- printShortStackTrace(System.err, e);
- result = Result.SOME_EVALUATIONS_FAILED;
- }
-
- return result;
- }
-
- /**
- * Useful to keep output concise when expecting many failures to be reported by this test case
- */
- private static void printShortStackTrace(PrintStream ps, AssertionFailedError e) {
- StackTraceElement[] stes = e.getStackTrace();
-
- int startIx = 0;
- // skip any top frames inside junit.framework.Assert
- while(startIx<stes.length) {
- if(!stes[startIx].getClassName().equals(Assert.class.getName())) {
- break;
- }
- startIx++;
- }
- // skip bottom frames (part of junit framework)
- int endIx = startIx+1;
- while(endIx < stes.length) {
- if(stes[endIx].getClassName().equals(TestCase.class.getName())) {
- break;
- }
- endIx++;
- }
- if(startIx >= endIx) {
- // something went wrong. just print the whole stack trace
- e.printStackTrace(ps);
- }
- endIx -= 4; // skip 4 frames of reflection invocation
- ps.println(e.toString());
- for(int i=startIx; i<endIx; i++) {
- ps.println("\tat " + stes[i].toString());
- }
- }
-
- /**
- * @return <code>null</code> if cell is missing, empty or blank
- */
- private static String getTargetFunctionName(Row r) {
- if(r == null) {
- System.err.println("Warning - given null row, can't figure out function name");
- return null;
- }
- Cell cell = r.getCell(SS.COLUMN_INDEX_FUNCTION_NAME);
- if(cell == null) {
- System.err.println("Warning - Row " + r.getRowNum() + " has no cell " + SS.COLUMN_INDEX_FUNCTION_NAME + ", can't figure out function name");
- return null;
- }
- if(cell.getCellType() == Cell.CELL_TYPE_BLANK) {
- return null;
- }
- if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
- return cell.getRichStringCellValue().getString();
- }
-
- throw new AssertionFailedError("Bad cell type for 'function name' column: ("
- + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");
- }
- /**
- * @return <code>null</code> if cell is missing, empty or blank
- */
- private static String getTargetTestName(Row r) {
- if(r == null) {
- System.err.println("Warning - given null row, can't figure out test name");
- return null;
- }
- Cell cell = r.getCell(SS.COLUMN_INDEX_TEST_NAME);
- if(cell == null) {
- System.err.println("Warning - Row " + r.getRowNum() + " has no cell " + SS.COLUMN_INDEX_TEST_NAME + ", can't figure out test name");
- return null;
- }
- if(cell.getCellType() == Cell.CELL_TYPE_BLANK) {
- return null;
- }
- if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
- return cell.getRichStringCellValue().getString();
- }
-
- throw new AssertionFailedError("Bad cell type for 'test name' column: ("
- + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");
- }
-
- }
|