[NO ISSUE][TEST] Canonicalize file outputs in test framework

Change-Id: Ib707c5426d2f5b9f8b285e19a2eacddc091ce57a
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/4463
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Murtadha Hubail <mhubail@apache.org>
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index 4d9ceeb..9e2d028 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.test.common;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.hyracks.util.file.FileUtil.canonicalize;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
@@ -223,7 +224,7 @@
 
     public void runScriptAndCompareWithResult(File scriptFile, File expectedFile, File actualFile,
             ComparisonEnum compare, Charset actualEncoding) throws Exception {
-        LOGGER.info("Expected results file: {} ", expectedFile);
+        LOGGER.info("Expected results file: {} ", canonicalize(expectedFile));
         boolean regex = false;
         try (BufferedReader readerExpected =
                 new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), UTF_8));
@@ -288,7 +289,11 @@
                 throw createLineChangedException(scriptFile, "<EOF>", lineActual, num);
             }
         } catch (Exception e) {
-            LOGGER.info("Actual results file: {} encoding: {}", actualFile, actualEncoding);
+            if (!actualEncoding.equals(UTF_8)) {
+                LOGGER.info("Actual results file: {} encoding: {}", canonicalize(actualFile), actualEncoding);
+            } else {
+                LOGGER.info("Actual results file: {}", canonicalize(actualFile));
+            }
             throw e;
         }
 
@@ -296,8 +301,8 @@
 
     private ComparisonException createLineChangedException(File scriptFile, String lineExpected, String lineActual,
             int num) {
-        return new ComparisonException("Result for " + scriptFile + " changed at line " + num + ":\nexpected < "
-                + truncateIfLong(lineExpected) + "\nactual   > " + truncateIfLong(lineActual));
+        return new ComparisonException("Result for " + canonicalize(scriptFile) + " changed at line " + num
+                + ":\nexpected < " + truncateIfLong(lineExpected) + "\nactual   > " + truncateIfLong(lineActual));
     }
 
     private String truncateIfLong(String string) {
@@ -434,7 +439,7 @@
             if (match && !negate || negate && !match) {
                 continue;
             }
-            throw new Exception("Result for " + scriptFile + ": expected pattern '" + expression
+            throw new Exception("Result for " + canonicalize(scriptFile) + ": expected pattern '" + expression
                     + "' not found in result: " + actual);
         }
     }
@@ -463,7 +468,8 @@
                 }
                 endOfMatch = matcher.end();
             }
-            throw new Exception("Result for " + scriptFile + ": actual file did not match expected result");
+            throw new Exception(
+                    "Result for " + canonicalize(scriptFile) + ": actual file did not match expected result");
         }
     }
 
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/sqlpp/ParserTestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/sqlpp/ParserTestExecutor.java
index 5dd41c3..5da8cb3 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/sqlpp/ParserTestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/sqlpp/ParserTestExecutor.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.test.sqlpp;
 
+import static org.apache.hyracks.util.file.FileUtil.canonicalize;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -79,7 +80,7 @@
                 try {
                     if (queryCount >= expectedResultFileCtxs.size()
                             && !cUnit.getOutputDir().getValue().equals("none")) {
-                        throw new ComparisonException("no result file for " + testFile.toString() + "; queryCount: "
+                        throw new ComparisonException("no result file for " + canonicalize(testFile) + "; queryCount: "
                                 + queryCount + ", filectxs.size: " + expectedResultFileCtxs.size());
                     }
 
@@ -93,21 +94,21 @@
                             "[TEST]: " + testCaseCtx.getTestCase().getFilePath() + "/" + cUnit.getName() + " PASSED ");
                     queryCount++;
                 } catch (Exception e) {
-                    System.err.println("testFile " + testFile.toString() + " raised an exception: " + e);
+                    System.err.println("testFile " + canonicalize(testFile) + " raised an exception: " + e);
                     if (cUnit.getExpectedError().isEmpty()) {
                         e.printStackTrace();
                         System.err.println("...Unexpected!");
                         if (failedGroup != null) {
                             failedGroup.getTestCase().add(testCaseCtx.getTestCase());
                         }
-                        throw new Exception("Test \"" + testFile + "\" FAILED!", e);
+                        throw new Exception("Test \"" + canonicalize(testFile) + "\" FAILED!", e);
                     } else {
                         // must compare with the expected failure message
                         if (e instanceof ComparisonException) {
                             throw e;
                         }
-                        LOGGER.info("[TEST]: " + testCaseCtx.getTestCase().getFilePath() + "/" + cUnit.getName()
-                                + " failed as expected: " + e.getMessage());
+                        LOGGER.info("[TEST]: " + canonicalize(testCaseCtx.getTestCase().getFilePath()) + "/"
+                                + cUnit.getName() + " failed as expected: " + e.getMessage());
                         System.err.println("...but that was expected.");
                     }
                 }
@@ -153,7 +154,7 @@
             runScriptAndCompareWithResult(queryFile, expectedFile, actualResultFile, ComparisonEnum.TEXT,
                     StandardCharsets.UTF_8);
         } catch (Exception e) {
-            GlobalConfig.ASTERIX_LOGGER.warn("Failed while testing file " + queryFile);
+            GlobalConfig.ASTERIX_LOGGER.warn("Failed while testing file " + canonicalize(queryFile));
             throw e;
         } finally {
             writer.close();
diff --git a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/file/FileUtil.java b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/file/FileUtil.java
index fe60653..7d162ee 100644
--- a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/file/FileUtil.java
+++ b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/file/FileUtil.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.file.Path;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.io.FileUtils;
@@ -31,6 +32,7 @@
 public class FileUtil {
 
     private static final Logger LOGGER = LogManager.getLogger();
+    private static final Pattern PARENT_DIR = Pattern.compile("([/\\\\]|^)[^./\\\\]+[/\\\\]\\.\\.([/\\\\]|$)");
     private static final Object LOCK = new Object();
     private static final int MAX_COPY_ATTEMPTS = 3;
 
@@ -95,4 +97,22 @@
             raf.getChannel().force(true);
         }
     }
+
+    public static String canonicalize(CharSequence path) {
+        String newPath = path.toString();
+        Matcher matcher = PARENT_DIR.matcher(newPath);
+        while (matcher.find()) {
+            // TODO(mblow): use StringBuilder once Java 8 is no longer supported (requires >=9)
+            StringBuffer sb = new StringBuffer();
+            matcher.appendReplacement(sb, matcher.group(2).isEmpty() ? "" : matcher.group(1).replace("\\", "\\\\"));
+            matcher.appendTail(sb);
+            newPath = sb.toString();
+            matcher.reset(newPath);
+        }
+        return newPath;
+    }
+
+    public static File canonicalize(File file) {
+        return new File(canonicalize(file.getPath()));
+    }
 }
diff --git a/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/file/FileUtilTest.java b/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/file/FileUtilTest.java
index 8d6c631..87c730c 100644
--- a/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/file/FileUtilTest.java
+++ b/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/file/FileUtilTest.java
@@ -41,4 +41,27 @@
                 joinPath('\\', "\\\\myserver\\tmp\\\\far\\baz\\\\\\\\lala"));
         Assert.assertEquals("C:\\temp\\far\\baz\\lala", joinPath('\\', "C:\\temp\\\\far\\baz\\\\\\\\lala\\"));
     }
+
+    @Test
+    public void testCanonicalize() {
+        Assert.assertEquals("bat.txt", FileUtil.canonicalize("foo/../bat.txt"));
+        Assert.assertEquals("bat.txt", FileUtil.canonicalize("foo/bar/../../bat.txt"));
+        Assert.assertEquals("foo/", FileUtil.canonicalize("foo/bar/../"));
+        Assert.assertEquals("foo", FileUtil.canonicalize("foo/bar/.."));
+        Assert.assertEquals("../bat.txt", FileUtil.canonicalize("../bat.txt"));
+        Assert.assertEquals("/bat.txt", FileUtil.canonicalize("/foo/bar/../../bat.txt"));
+        Assert.assertEquals("/bar/bat.txt", FileUtil.canonicalize("/foo/../bar/bat.txt"));
+    }
+
+    @Test
+    public void testCanonicalizeWindoze() {
+        Assert.assertEquals("bat.txt", FileUtil.canonicalize("foo\\..\\bat.txt"));
+        Assert.assertEquals("bat.txt", FileUtil.canonicalize("foo\\bar\\..\\..\\bat.txt"));
+        Assert.assertEquals("foo\\", FileUtil.canonicalize("foo\\bar\\..\\"));
+        Assert.assertEquals("foo", FileUtil.canonicalize("foo\\bar\\.."));
+        Assert.assertEquals("..\\bat.txt", FileUtil.canonicalize("..\\bat.txt"));
+        Assert.assertEquals("\\bat.txt", FileUtil.canonicalize("\\foo\\bar\\..\\..\\bat.txt"));
+        Assert.assertEquals("\\bar\\bat.txt", FileUtil.canonicalize("\\foo\\..\\bar\\bat.txt"));
+        Assert.assertEquals("C:\\bar\\bat.txt", FileUtil.canonicalize("C:\\foo\\..\\bar\\bat.txt"));
+    }
 }