You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestMatrixFormulasFromBinarySpreadsheet.java 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package org.apache.poi.hssf.usermodel;
  2. import static org.junit.Assert.assertEquals;
  3. import static org.junit.Assert.assertNotNull;
  4. import static org.junit.Assert.fail;
  5. import java.util.ArrayList;
  6. import java.util.Collection;
  7. import java.util.List;
  8. import java.util.Locale;
  9. import org.apache.poi.hssf.HSSFTestDataSamples;
  10. import org.apache.poi.ss.formula.eval.ErrorEval;
  11. import org.apache.poi.ss.formula.functions.TestMathX;
  12. import org.apache.poi.ss.usermodel.Cell;
  13. import org.apache.poi.ss.usermodel.CellType;
  14. import org.apache.poi.ss.usermodel.CellValue;
  15. import org.apache.poi.ss.usermodel.FormulaEvaluator;
  16. import org.apache.poi.ss.usermodel.Row;
  17. import org.apache.poi.ss.usermodel.Sheet;
  18. import org.apache.poi.util.LocaleUtil;
  19. import org.junit.AfterClass;
  20. import org.junit.Test;
  21. import org.junit.runner.RunWith;
  22. import org.junit.runners.Parameterized;
  23. import org.junit.runners.Parameterized.Parameter;
  24. import org.junit.runners.Parameterized.Parameters;
  25. import junit.framework.AssertionFailedError;
  26. @RunWith(Parameterized.class)
  27. public final class TestMatrixFormulasFromBinarySpreadsheet {
  28. private static HSSFWorkbook workbook;
  29. private static Sheet sheet;
  30. private static FormulaEvaluator evaluator;
  31. private static Locale userLocale;
  32. /*
  33. * Unlike TestFormulaFromSpreadsheet which this class is modified from, there is no
  34. * differentiation between operators and functions, if more functionality is implemented with
  35. * array formulas then it might be worth it to separate operators from functions
  36. *
  37. * Also, output matrices are statically 3x3, if larger matrices wanted to be tested
  38. * then adding matrix size parameter would be useful and parsing would be based off that.
  39. */
  40. private static interface Navigator {
  41. /**
  42. * Name of the test spreadsheet (found in the standard test data folder)
  43. */
  44. String FILENAME = "MatrixFormulaEvalTestData.xls";
  45. /**
  46. * Row (zero-based) in the spreadsheet where operations start
  47. */
  48. int START_OPERATORS_ROW_INDEX = 1;
  49. /**
  50. * Column (zero-based) in the spreadsheet where operations start
  51. */
  52. int START_OPERATORS_COL_INDEX = 0;
  53. /**
  54. * Column (zero-based) in the spreadsheet where evaluations start
  55. */
  56. int START_RESULT_COL_INDEX = 7;
  57. /**
  58. * Column separation in the spreadsheet between evaluations and expected results
  59. */
  60. int COL_OFF_EXPECTED_RESULT = 3;
  61. /**
  62. * Row separation in the spreadsheet between operations
  63. */
  64. int ROW_OFF_NEXT_OP = 4;
  65. /**
  66. * Used to indicate when there are no more operations left
  67. */
  68. String END_OF_TESTS = "<END>";
  69. }
  70. /* Parameters for test case */
  71. @Parameter(0)
  72. public String targetFunctionName;
  73. @Parameter(1)
  74. public int formulasRowIdx;
  75. @AfterClass
  76. public static void closeResource() throws Exception {
  77. LocaleUtil.setUserLocale(userLocale);
  78. workbook.close();
  79. }
  80. /* generating parameter instances */
  81. @Parameters(name="{0}")
  82. public static Collection<Object[]> data() throws Exception {
  83. // Function "Text" uses custom-formats which are locale specific
  84. // can't set the locale on a per-testrun execution, as some settings have been
  85. // already set, when we would try to change the locale by then
  86. userLocale = LocaleUtil.getUserLocale();
  87. LocaleUtil.setUserLocale(Locale.ROOT);
  88. workbook = HSSFTestDataSamples.openSampleWorkbook(Navigator.FILENAME);
  89. sheet = workbook.getSheetAt(0);
  90. evaluator = new HSSFFormulaEvaluator(workbook);
  91. List<Object[]> data = new ArrayList<Object[]>();
  92. processFunctionGroup(data, Navigator.START_OPERATORS_ROW_INDEX, null);
  93. return data;
  94. }
  95. /**
  96. * @param startRowIndex row index in the spreadsheet where the first function/operator is found
  97. * @param testFocusFunctionName name of a single function/operator to test alone.
  98. * Typically pass <code>null</code> to test all functions
  99. */
  100. private static void processFunctionGroup(List<Object[]> data, int startRowIndex, String testFocusFunctionName) {
  101. for (int rowIndex = startRowIndex; true; rowIndex += Navigator.ROW_OFF_NEXT_OP) {
  102. Row r = sheet.getRow(rowIndex);
  103. String targetFunctionName = getTargetFunctionName(r);
  104. assertNotNull("Test spreadsheet cell empty on row ("
  105. + (rowIndex) + "). Expected function name or '"
  106. + Navigator.END_OF_TESTS + "'", targetFunctionName);
  107. if(targetFunctionName.equals(Navigator.END_OF_TESTS)) {
  108. // found end of functions list
  109. break;
  110. }
  111. if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
  112. data.add(new Object[]{targetFunctionName, rowIndex});
  113. }
  114. }
  115. }
  116. @Test
  117. public void processFunctionRow() {
  118. int endColNum = Navigator.START_RESULT_COL_INDEX + Navigator.COL_OFF_EXPECTED_RESULT;
  119. for (int rowNum = formulasRowIdx; rowNum < formulasRowIdx + Navigator.ROW_OFF_NEXT_OP - 1; rowNum++) {
  120. for (int colNum = Navigator.START_RESULT_COL_INDEX; colNum < endColNum; colNum++) {
  121. Row r = sheet.getRow(rowNum);
  122. /* mainly to escape row failures on MDETERM which only returns a scalar */
  123. if (r == null) {
  124. continue;
  125. }
  126. Cell c = sheet.getRow(rowNum).getCell(colNum);
  127. if (c == null || c.getCellTypeEnum() != CellType.FORMULA) {
  128. continue;
  129. }
  130. CellValue actValue = evaluator.evaluate(c);
  131. Cell expValue = sheet.getRow(rowNum).getCell(colNum + Navigator.COL_OFF_EXPECTED_RESULT);
  132. String msg = String.format(Locale.ROOT, "Function '%s': Formula: %s @ %d:%d"
  133. , targetFunctionName, c.getCellFormula(), rowNum, colNum);
  134. assertNotNull(msg + " - Bad setup data expected value is null", expValue);
  135. assertNotNull(msg + " - actual value was null", actValue);
  136. final CellType cellType = expValue.getCellTypeEnum();
  137. switch (cellType) {
  138. case BLANK:
  139. assertEquals(msg, CellType.BLANK, actValue.getCellTypeEnum());
  140. break;
  141. case BOOLEAN:
  142. assertEquals(msg, CellType.BOOLEAN, actValue.getCellTypeEnum());
  143. assertEquals(msg, expValue.getBooleanCellValue(), actValue.getBooleanValue());
  144. break;
  145. case ERROR:
  146. assertEquals(msg, CellType.ERROR, actValue.getCellTypeEnum());
  147. assertEquals(msg, ErrorEval.getText(expValue.getErrorCellValue()), ErrorEval.getText(actValue.getErrorValue()));
  148. break;
  149. case FORMULA: // will never be used, since we will call method after formula evaluation
  150. fail("Cannot expect formula as result of formula evaluation: " + msg);
  151. case NUMERIC:
  152. assertEquals(msg, CellType.NUMERIC, actValue.getCellTypeEnum());
  153. TestMathX.assertEquals(msg, expValue.getNumericCellValue(), actValue.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
  154. break;
  155. case STRING:
  156. assertEquals(msg, CellType.STRING, actValue.getCellTypeEnum());
  157. assertEquals(msg, expValue.getRichStringCellValue().getString(), actValue.getStringValue());
  158. break;
  159. default:
  160. fail("Unexpected cell type: " + cellType);
  161. }
  162. }
  163. }
  164. }
  165. /**
  166. * @return <code>null</code> if cell is missing, empty or blank
  167. */
  168. private static String getTargetFunctionName(Row r) {
  169. if(r == null) {
  170. System.err.println("Warning - given null row, can't figure out function name");
  171. return null;
  172. }
  173. Cell cell = r.getCell(Navigator.START_OPERATORS_COL_INDEX);
  174. System.err.println(String.valueOf(Navigator.START_OPERATORS_COL_INDEX));
  175. if(cell == null) {
  176. System.err.println("Warning - Row " + r.getRowNum() + " has no cell " + Navigator.START_OPERATORS_COL_INDEX + ", can't figure out function name");
  177. return null;
  178. }
  179. if(cell.getCellTypeEnum() == CellType.BLANK) {
  180. return null;
  181. }
  182. if(cell.getCellTypeEnum() == CellType.STRING) {
  183. return cell.getRichStringCellValue().getString();
  184. }
  185. throw new AssertionFailedError("Bad cell type for 'function name' column: ("
  186. + cell.getCellTypeEnum() + ") row (" + (r.getRowNum() +1) + ")");
  187. }
  188. }