diff options
Diffstat (limited to 'poi')
-rw-r--r-- | poi/src/main/java/org/apache/poi/util/Reproducibility.java | 78 | ||||
-rw-r--r-- | poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java | 83 |
2 files changed, 96 insertions, 65 deletions
diff --git a/poi/src/main/java/org/apache/poi/util/Reproducibility.java b/poi/src/main/java/org/apache/poi/util/Reproducibility.java new file mode 100644 index 0000000000..9ece2bc269 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/util/Reproducibility.java @@ -0,0 +1,78 @@ +/* ==================================================================== + 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.util; + +import java.io.IOException; + +import org.apache.commons.io.function.IORunnable; + +/** + * Helper class for allowing to produce so called + * "reproducible" output. + * + * I.e. multiple runs of the same steps should + * produce the same byte-by-byte result. + * + * This usually means that among other "randomness" + * timestamp should be avoided. + * + * This class provides a few useful bits to allow Apache POI to produce + * reproducible binary files. + * + * See https://reproducible-builds.org/ for more details. + */ +public class Reproducibility { + // Add some support for reproducible output files + // if SOURCE_DATE_EPOCH is set, we use timestamp "0" for + // entries in Zip files + // See https://reproducible-builds.org/docs/source-date-epoch/ + // for the specification of SOURCE_DATE_EPOCH + private static boolean IS_SOURCE_DATE_EPOCH = + System.getenv("SOURCE_DATE_EPOCH") != null; + + /** + * Check if the environment variable SOURCE_DATE_EPOCH is set. + * + * @return True if set, false otherwise + */ + public static boolean isSourceDateEpoch() { + return IS_SOURCE_DATE_EPOCH; + } + + /** + * Execute a runnable with SOURCE_DATE_EPOCH set. + * + * This is mostly only used in tests to check reproducibility + * of documents. + * + * @param r A runnable which executes the wanted steps with + * SOURCE_DATE_EPOCH defined + * + * @throws IOException if executing the runnable throws an IOException + * @throws RuntimeException if executing the runnable throws a RuntimeException + */ + public static void runWithSourceDateEpoch(IORunnable r) throws IOException { + boolean before = IS_SOURCE_DATE_EPOCH; + IS_SOURCE_DATE_EPOCH = true; + try { + r.run(); + } finally { + IS_SOURCE_DATE_EPOCH = before; + } + } +} diff --git a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java index a356c995a7..1a410e6f12 100644 --- a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java +++ b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java @@ -42,7 +42,6 @@ import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.FileMagic; import org.apache.poi.ss.ITestDataProvider; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.FormulaParseException; @@ -51,6 +50,7 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.PaneInformation; import org.apache.poi.ss.util.SheetUtil; +import org.apache.poi.util.Reproducibility; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -1815,70 +1815,23 @@ public abstract class BaseTestBugzillaIssues { cell.setCellValue("Ernie & Bert are cool!"); cell.setCellFormula("A1 & \" are cool!\""); - try (UnsynchronizedByteArrayOutputStream out1 = UnsynchronizedByteArrayOutputStream.builder().get(); - UnsynchronizedByteArrayOutputStream out2 = UnsynchronizedByteArrayOutputStream.builder().get()) { - wb.write(out1); - wb.write(out2); - - out1.flush(); - out2.flush(); - - // to avoid flaky tests if the documents are written at slightly different timestamps - // we clear some bytes which contain timestamps - assertArrayEquals( - removeTimestamp(out1.toByteArray()), - removeTimestamp(out2.toByteArray())); - } + Reproducibility.runWithSourceDateEpoch( + () -> { + try (UnsynchronizedByteArrayOutputStream out1 = UnsynchronizedByteArrayOutputStream.builder().get(); + UnsynchronizedByteArrayOutputStream out2 = UnsynchronizedByteArrayOutputStream.builder().get()) { + wb.write(out1); + wb.write(out2); + + out1.flush(); + out2.flush(); + + // to avoid flaky tests if the documents are written at slightly different timestamps + // we clear some bytes which contain timestamps + assertArrayEquals( + out1.toByteArray(), + out2.toByteArray()); + } + }); } } - - private byte[] removeTimestamp(byte[] bytes) { - if (FileMagic.valueOf(bytes) == FileMagic.OOXML) { - // This removes the timestamp in the header of the ZIP-Format - // see "Local file header" at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - bytes[10] = 0; - bytes[11] = 0; - bytes[12] = 0; - bytes[13] = 0; - - // there is a timestamp for every entry, so try to replace a few more byte-positions - // to reduce flakiness of this test, however we likely do not yet cover all entries - bytes[390] = 0; - bytes[391] = 0; - bytes[674] = 0; - bytes[676] = 0; - bytes[883] = 0; - bytes[1207] = 0; - bytes[1208] = 0; - bytes[1432] = 0; - bytes[1433] = 0; - bytes[1434] = 0; - bytes[1817] = 0; - bytes[1818] = 0; - bytes[2098] = 0; - bytes[2099] = 0; - bytes[2762] = 0; - bytes[2763] = 0; - bytes[2382] = 0; - bytes[2383] = 0; - bytes[2827] = 0; - bytes[2828] = 0; - bytes[2884] = 0; - bytes[2885] = 0; - bytes[2946] = 0; - bytes[2947] = 0; - bytes[3009] = 0; - bytes[3010] = 0; - bytes[3075] = 0; - bytes[3076] = 0; - bytes[3134] = 0; - bytes[3135] = 0; - bytes[3195] = 0; - bytes[3196] = 0; - bytes[3267] = 0; - bytes[3268] = 0; - } - - return bytes; - } } |