blob: 6d977db3dadc12aada3101d984b4ab513dcca5a7 [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.xssf.streaming;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.SheetUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Tests the auto-sizing behaviour of {@link SXSSFSheet} when not all
* rows fit into the memory window size etc.
*
* see Bug #57450 which reported the original misbehaviour
*/
class TestAutoSizeColumnTracker {
private SXSSFSheet sheet;
private SXSSFWorkbook workbook;
private AutoSizeColumnTracker tracker;
private static final SortedSet<Integer> columns;
static {
SortedSet<Integer>_columns = new TreeSet<>();
_columns.add(0);
_columns.add(1);
_columns.add(3);
columns = Collections.unmodifiableSortedSet(_columns);
}
private final static String SHORT_MESSAGE = "short";
private final static String LONG_MESSAGE = "This is a test of a long message! This is a test of a long message!";
@BeforeEach
void setUpSheetAndWorkbook() {
workbook = new SXSSFWorkbook();
sheet = workbook.createSheet();
tracker = new AutoSizeColumnTracker(sheet);
}
@AfterEach
void tearDownSheetAndWorkbook() throws IOException {
if (sheet != null) {
sheet.dispose();
}
if (workbook != null) {
workbook.close();
}
}
@Test
void trackAndUntrackColumn() {
assumeTrue(tracker.getTrackedColumns().isEmpty());
tracker.trackColumn(0);
Set<Integer> expected = new HashSet<>();
expected.add(0);
assertEquals(expected, tracker.getTrackedColumns());
tracker.untrackColumn(0);
assertTrue(tracker.getTrackedColumns().isEmpty());
}
@Test
void trackAndUntrackColumns() {
assumeTrue(tracker.getTrackedColumns().isEmpty());
tracker.trackColumns(columns);
assertEquals(columns, tracker.getTrackedColumns());
tracker.untrackColumn(3);
tracker.untrackColumn(0);
tracker.untrackColumn(1);
assertTrue(tracker.getTrackedColumns().isEmpty());
tracker.trackColumn(0);
tracker.trackColumns(columns);
tracker.untrackColumn(4);
assertEquals(columns, tracker.getTrackedColumns());
tracker.untrackColumns(columns);
assertTrue(tracker.getTrackedColumns().isEmpty());
}
@Test
void trackAndUntrackAllColumns() {
createColumnsAndTrackThemAll();
tracker.untrackAllColumns();
assertTrue(tracker.getTrackedColumns().isEmpty());
}
@Test
void isColumnTracked() {
assumeFalse(tracker.isColumnTracked(0));
tracker.trackColumn(0);
assertTrue(tracker.isColumnTracked(0));
tracker.untrackColumn(0);
assertFalse(tracker.isColumnTracked(0));
}
@Test
void isColumnTrackedAndTrackAllColumns() {
createColumnsAndTrackThemAll();
tracker.untrackColumn(0);
SortedSet<Integer> _newColumns = new TreeSet<>();
_newColumns.add(1);
_newColumns.add(3);
SortedSet<Integer> newColumns = Collections.unmodifiableSortedSet(_newColumns);
assertEquals(newColumns, tracker.getTrackedColumns());
assertFalse(tracker.isColumnTracked(0));
}
@Test
void getTrackedColumns() {
assumeTrue(tracker.getTrackedColumns().isEmpty());
for (int column : columns) {
tracker.trackColumn(column);
}
assertEquals(3, tracker.getTrackedColumns().size());
assertEquals(columns, tracker.getTrackedColumns());
}
@Test
void isAllColumnsTracked() {
assertFalse(tracker.isAllColumnsTracked());
tracker.trackAllColumns();
assertTrue(tracker.isAllColumnsTracked());
tracker.untrackAllColumns();
assertFalse(tracker.isAllColumnsTracked());
}
@Test
void updateColumnWidths_and_getBestFitColumnWidth() {
tracker.trackAllColumns();
Row row1 = sheet.createRow(0);
Row row2 = sheet.createRow(1);
// A1, B1, D1
for (int column : columns) {
row1.createCell(column).setCellValue(LONG_MESSAGE);
row2.createCell(column+1).setCellValue(SHORT_MESSAGE);
}
tracker.updateColumnWidths(row1);
tracker.updateColumnWidths(row2);
assertEquals(0, sheet.addMergedRegion(CellRangeAddress.valueOf("D1:E1")));
assumeRequiredFontsAreInstalled(workbook, row1.getCell(columns.iterator().next()));
// Excel 2013 and LibreOffice 4.2.8.2 both treat columns with merged regions as blank
/* A B C D E
* 1 LONG LONG LONGMERGE
* 2 SHORT SHORT SHORT
*/
// measured in Excel 2013. Sizes may vary.
final int longMsgWidth = (int) (57.43*256);
final int shortMsgWidth = (int) (4.86*256);
checkColumnWidth(longMsgWidth, 0, true);
checkColumnWidth(longMsgWidth, 0, false);
checkColumnWidth(longMsgWidth, 1, true);
checkColumnWidth(longMsgWidth, 1, false);
checkColumnWidth(shortMsgWidth, 2, true);
checkColumnWidth(shortMsgWidth, 2, false);
checkColumnWidth(-1, 3, true);
checkColumnWidth(longMsgWidth, 3, false);
checkColumnWidth(shortMsgWidth, 4, true); //but is it really? shouldn't autosizing column E use "" from E1 and SHORT from E2?
checkColumnWidth(shortMsgWidth, 4, false);
}
private void checkColumnWidth(int expectedWidth, int column, boolean useMergedCells) {
final int bestFitWidth = tracker.getBestFitColumnWidth(column, useMergedCells);
if (bestFitWidth < 0 && expectedWidth < 0) return;
final double abs_error = Math.abs(bestFitWidth-expectedWidth);
final double rel_error = abs_error / expectedWidth;
assertTrue(rel_error <= 0.25, "check column width: " + rel_error + ", " + abs_error + ", " + expectedWidth + ", " + bestFitWidth);
}
private static void assumeRequiredFontsAreInstalled(final Workbook workbook, final Cell cell) {
// autoSize will fail if required fonts are not installed, skip this test then
Font font = workbook.getFontAt(cell.getCellStyle().getFontIndex());
assumeTrue(SheetUtil.canComputeColumnWidth(font),
"Cannot verify autoSizeColumn() because the necessary Fonts are not installed on this machine: " + font);
}
private void createColumnsAndTrackThemAll() {
assumeTrue(tracker.getTrackedColumns().isEmpty());
tracker.trackAllColumns();
assertTrue(tracker.getTrackedColumns().isEmpty());
Row row = sheet.createRow(0);
for (int column : columns) {
row.createCell(column);
}
// implicitly track the columns
tracker.updateColumnWidths(row);
assertEquals(columns, tracker.getTrackedColumns());
}
}