240 lines
10 KiB
Java
240 lines
10 KiB
Java
/* ====================================================================
|
|
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.Assert.assertEquals;
|
|
import static org.junit.Assert.assertNotEquals;
|
|
import static org.junit.Assert.assertNotNull;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import org.apache.poi.hssf.HSSFTestDataSamples;
|
|
import org.apache.poi.hssf.usermodel.HSSFCell;
|
|
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
import org.apache.poi.hssf.util.CellReference;
|
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
|
import org.apache.poi.ss.usermodel.CellType;
|
|
import org.apache.poi.ss.usermodel.CellValue;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.Parameterized;
|
|
import org.junit.runners.Parameterized.Parameter;
|
|
|
|
@RunWith(Parameterized.class)
|
|
public abstract class BaseTestFunctionsFromSpreadsheet {
|
|
|
|
/**
|
|
* This class defines constants for navigating around the test data spreadsheet used for these tests.
|
|
*/
|
|
interface SS {
|
|
/** Name of the test spreadsheet (found in the standard test data folder) */
|
|
|
|
/** Name of the first sheet in the spreadsheet (contains comments) */
|
|
String README_SHEET_NAME = "Read Me";
|
|
|
|
/** Row (zero-based) in each sheet where the evaluation cases start. */
|
|
int START_TEST_CASES_ROW_INDEX = 4; // Row '5'
|
|
/** Index of the column that contains the function names */
|
|
int COLUMN_INDEX_MARKER = 0; // Column 'A'
|
|
int COLUMN_INDEX_EVALUATION = 1; // Column 'B'
|
|
int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C'
|
|
int COLUMN_ROW_COMMENT = 3; // Column 'D'
|
|
|
|
/** Used to indicate when there are no more test cases on the current sheet */
|
|
String TEST_CASES_END_MARKER = "<end>";
|
|
/** Used to indicate that the test on the current row should be ignored */
|
|
String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>";
|
|
|
|
}
|
|
|
|
@Parameter(value = 0)
|
|
public String testName;
|
|
@Parameter(value = 1)
|
|
public String filename;
|
|
@Parameter(value = 2)
|
|
public HSSFSheet sheet;
|
|
@Parameter(value = 3)
|
|
public int formulasRowIdx;
|
|
@Parameter(value = 4)
|
|
public HSSFFormulaEvaluator evaluator;
|
|
|
|
|
|
|
|
protected static Collection<Object[]> data(Class<? extends BaseTestFunctionsFromSpreadsheet> clazz, String filename) throws Exception {
|
|
HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(filename);
|
|
confirmReadMeSheet(workbook, clazz);
|
|
|
|
List<Object[]> data = new ArrayList<Object[]>();
|
|
|
|
int nSheets = workbook.getNumberOfSheets();
|
|
for(int sheetIdx=1; sheetIdx< nSheets; sheetIdx++) {
|
|
HSSFSheet sheet = workbook.getSheetAt(sheetIdx);
|
|
processFunctionGroup(data, sheet, SS.START_TEST_CASES_ROW_INDEX, null, filename);
|
|
}
|
|
|
|
workbook.close();
|
|
|
|
return data;
|
|
}
|
|
|
|
private static void processFunctionGroup(List<Object[]> data, HSSFSheet sheet, final int startRowIndex, String testFocusFunctionName, String filename) {
|
|
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet.getWorkbook());
|
|
|
|
String currentGroupComment = "";
|
|
final int maxRows = sheet.getLastRowNum()+1;
|
|
for(int rowIndex=startRowIndex; rowIndex<maxRows; rowIndex++) {
|
|
HSSFRow r = sheet.getRow(rowIndex);
|
|
if(r == null) {
|
|
continue;
|
|
}
|
|
String newMarkerValue = getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker");
|
|
if(SS.TEST_CASES_END_MARKER.equalsIgnoreCase(newMarkerValue)) {
|
|
// normal exit point
|
|
return;
|
|
}
|
|
if(SS.SKIP_CURRENT_TEST_CASE_MARKER.equalsIgnoreCase(newMarkerValue)) {
|
|
// currently disabled test case row
|
|
continue;
|
|
}
|
|
if(newMarkerValue != null) {
|
|
currentGroupComment = newMarkerValue;
|
|
}
|
|
HSSFCell evalCell = r.getCell(SS.COLUMN_INDEX_EVALUATION);
|
|
if (evalCell == null || evalCell.getCellTypeEnum() != CellType.FORMULA) {
|
|
continue;
|
|
}
|
|
String rowComment = getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment");
|
|
|
|
String testName = (currentGroupComment+'\n'+rowComment).replace("null", "").trim().replace("\n", " - ");
|
|
if ("".equals(testName)) {
|
|
testName = evalCell.getCellFormula();
|
|
}
|
|
|
|
data.add(new Object[]{testName, filename, sheet, rowIndex, evaluator});
|
|
}
|
|
fail("Missing end marker '" + SS.TEST_CASES_END_MARKER + "' on sheet '" + sheet.getSheetName() + "'");
|
|
}
|
|
|
|
@Test
|
|
public void processFunctionRow() throws Exception {
|
|
HSSFRow r = sheet.getRow(formulasRowIdx);
|
|
HSSFCell evalCell = r.getCell(SS.COLUMN_INDEX_EVALUATION);
|
|
HSSFCell expectedCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
|
|
|
|
CellReference cr = new CellReference(sheet.getSheetName(), formulasRowIdx, evalCell.getColumnIndex(), false, false);
|
|
String msg = String.format(Locale.ROOT, "In %s %s {=%s} '%s'"
|
|
, filename, cr.formatAsString(), evalCell.getCellFormula(), testName);
|
|
|
|
CellValue actualValue = evaluator.evaluate(evalCell);
|
|
|
|
assertNotNull(msg + " - Bad setup data expected value is null", expectedCell);
|
|
assertNotNull(msg + " - actual value was null", actualValue);
|
|
|
|
if (expectedCell.getCellTypeEnum() == CellType.ERROR) {
|
|
int expectedErrorCode = expectedCell.getErrorCellValue();
|
|
assertEquals(msg, CellType.ERROR, actualValue.getCellTypeEnum());
|
|
assertEquals(msg, ErrorEval.getText(expectedErrorCode), actualValue.formatAsString());
|
|
assertEquals(msg, expectedErrorCode, actualValue.getErrorValue());
|
|
assertEquals(msg, ErrorEval.getText(expectedErrorCode), ErrorEval.getText(actualValue.getErrorValue()));
|
|
return;
|
|
}
|
|
|
|
// unexpected error
|
|
assertNotEquals(msg, CellType.ERROR, actualValue.getCellTypeEnum());
|
|
assertNotEquals(msg, formatValue(expectedCell), ErrorEval.getText(actualValue.getErrorValue()));
|
|
|
|
// wrong type error
|
|
assertEquals(msg, expectedCell.getCellTypeEnum(), actualValue.getCellTypeEnum());
|
|
|
|
final CellType expectedCellType = expectedCell.getCellTypeEnum();
|
|
switch (expectedCellType) {
|
|
case BOOLEAN:
|
|
assertEquals(msg, expectedCell.getBooleanCellValue(), actualValue.getBooleanValue());
|
|
break;
|
|
case FORMULA: // will never be used, since we will call method after formula evaluation
|
|
fail("Cannot expect formula as result of formula evaluation: " + msg);
|
|
case NUMERIC:
|
|
assertEquals(expectedCell.getNumericCellValue(), actualValue.getNumberValue(), 0.0);
|
|
break;
|
|
case STRING:
|
|
assertEquals(msg, expectedCell.getRichStringCellValue().getString(), actualValue.getStringValue());
|
|
break;
|
|
default:
|
|
fail("Unexpected cell type: " + expectedCellType);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that the 'read me' comment page exists, and has this class' name in one of the
|
|
* cells. This back-link is to make it easy to find this class if a reader encounters the
|
|
* spreadsheet first.
|
|
*/
|
|
private static void confirmReadMeSheet(HSSFWorkbook workbook, Class<? extends BaseTestFunctionsFromSpreadsheet> clazz) {
|
|
String firstSheetName = workbook.getSheetName(0);
|
|
assertTrue("First sheet's name was '" + firstSheetName + "' but expected '" + SS.README_SHEET_NAME + "'",
|
|
firstSheetName.equalsIgnoreCase(SS.README_SHEET_NAME));
|
|
HSSFSheet sheet = workbook.getSheetAt(0);
|
|
String specifiedClassName = sheet.getRow(2).getCell(0).getRichStringCellValue().getString();
|
|
assertEquals("Test class name in spreadsheet comment", clazz.getName(), specifiedClassName);
|
|
}
|
|
|
|
/**
|
|
* @return <code>null</code> if cell is missing, empty or blank
|
|
*/
|
|
private static String getCellTextValue(HSSFRow r, int colIndex, String columnName) {
|
|
if(r == null) {
|
|
return null;
|
|
}
|
|
HSSFCell cell = r.getCell(colIndex);
|
|
if(cell == null) {
|
|
return null;
|
|
}
|
|
if(cell.getCellTypeEnum() == CellType.BLANK) {
|
|
return null;
|
|
}
|
|
if(cell.getCellTypeEnum() == CellType.STRING) {
|
|
return cell.getRichStringCellValue().getString();
|
|
}
|
|
|
|
fail("Bad cell type for '" + columnName + "' column: ("
|
|
+ cell.getCellTypeEnum() + ") row (" + (r.getRowNum() +1) + ")");
|
|
return "";
|
|
}
|
|
|
|
private static String formatValue(HSSFCell expecedCell) {
|
|
switch (expecedCell.getCellTypeEnum()) {
|
|
case BLANK: return "<blank>";
|
|
case BOOLEAN: return Boolean.toString(expecedCell.getBooleanCellValue());
|
|
case NUMERIC: return Double.toString(expecedCell.getNumericCellValue());
|
|
case STRING: return expecedCell.getRichStringCellValue().getString();
|
|
default: fail("Unexpected cell type of expected value (" + expecedCell.getCellTypeEnum() + ")");
|
|
}
|
|
return "";
|
|
}
|
|
|
|
|
|
}
|