From f8c4a3e2115c644c3cb1c597d450a96046d9b7f1 Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Thu, 17 Mar 2016 14:26:43 -0700
Subject: [PATCH] add unit tests for installing/loading GnuR recommended
 packages

---
 .../truffle/r/test/ExpectedTestOutput.test    |  30 ++++
 .../r/test/rpackages/TestRFFIPackage.java     |   8 +-
 .../r/test/rpackages/TestRPackages.java       | 150 ++++++++++--------
 .../rpackages/TestRecommendedPackages.java    |  71 +++++++++
 .../r/test/rpackages/TestS4TestPackage.java   |  11 +-
 .../r/test/rpackages/TestVanillaPackage.java  |   4 +-
 6 files changed, 200 insertions(+), 74 deletions(-)
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRecommendedPackages.java

diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 9220e2268c..ca25e1438b 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -105089,6 +105089,36 @@ NULL
 NULL
 
 
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(MASS, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:MASS"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(boot, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:boot"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(class, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:class"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(cluster, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:cluster"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(codetools, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:codetools"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(foreign, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:foreign"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(lattice, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:lattice"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(nnet, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:nnet"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(spatial, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:spatial"); }
+
+##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad
+#{ library(survival, lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); detach("package:survival"); }
+
 ##com.oracle.truffle.r.test.rpackages.TestS4TestPackage.testS4Execute
 #{ library("tests4", lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); r<-print(tests4:::inspect.vehicle(new("Car"), new("Inspector"))); detach("package:tests4"); unloadNamespace("tests4"); r }
 Looking for rust
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
index c1677d5856..6a63ecc887 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRFFIPackage.java
@@ -48,25 +48,25 @@ public class TestRFFIPackage extends TestRPackages {
     @Test
     public void testLoadTestRFFICall() {
         assertEval(TestBase.template("{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L);  detach(\"package:testrffi\"); list(r1) }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
         assertEval(TestBase.template(
                         "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L); r2 <- rffi.addDouble(2, 3); v <- rffi.populateIntVector(5); v2 <- rffi.dotCModifiedArguments(c(0,1,2,3)); "
                                         + "v3<-rffi.isRString(character(0)); detach(\"package:testrffi\"); list(r1, r2, v, v2, v3) }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
     }
 
     @Test
     public void testLoadTestRFFIExternal() {
         assertEval(TestBase.template(
                         "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.dotExternalAccessArgs(1L, 3, c(1,2,3), c('a', 'b'), 'b', TRUE, as.raw(12)); detach(\"package:testrffi\"); list(r1) }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
     }
 
     @Test
     public void testLoadTestRFFIExternalWithNames() {
         assertEval(TestBase.template(
                         "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.dotExternalAccessArgs(x=1L, 3, c(1,2,3), y=c('a', 'b'), 'b', TRUE, as.raw(12)); detach(\"package:testrffi\"); list(r1) }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
     }
 
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
index 9d0a1da58e..fdf408e402 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
@@ -27,12 +27,9 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystems;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashMap;
 import java.util.Map;
 
 import com.oracle.truffle.r.runtime.FastROptions;
@@ -42,63 +39,42 @@ import com.oracle.truffle.r.test.TestBase;
 
 /**
  * Tests related to the loading, etc. of R packages.
+ *
+ * THis class should be subclassed by a test that wishes to install one or more packages and
+ * possibly run additional tests after installation using a pattern of the form
+ * {@code library(pkg, lib.loc="%0"); sometest()}. Note the use of the {@code %0}, which must be
+ * satisfied by passing the value of {@code libLoc()}. This is required because the test VM is not
+ * aware of the test install location so it must be explicitly specified. The use of the {@code %0}
+ * parameter mechanism also requires the use of {@link TestBase#template} in the test itself.
+ *
+ * A subclass must provide {@code @BeforeClass} and {@code @AfterClass}methods that call
+ * {@link #setupInstallTestPackages} and {@link #tearDownUninstallTestPackages}, respectively, to
+ * install/remove the specific set of packages relevant to the test.
+ *
+ * N.B. The same directory is used when generating expected output with GnuR, and running FastR, to
+ * keep the {@code lib_loc} argument the same in the test string. So the install is destructive, but
+ * ok as there is never a clash.
+ *
+ * The install directory is cleaned on every call to {@link #setupInstallTestPackages} in case a
+ * previous install failed to complete {@link #tearDownUninstallTestPackages} successfully.
  */
-public class TestRPackages extends TestBase {
+public abstract class TestRPackages extends TestBase {
 
     /**
-     * Create {@link Path}s to needed folders. N.B. The same directory is used when generating
-     * expected output with GnuR, and running FastR, to keep the {@code lib_loc} argument the same
-     * in the test string. So the install is destructive, but ok as there is never a clash.
+     * Create {@link Path}s to needed folders.
      *
      */
     protected static final class PackagePaths {
         /**
-         * The path containing the package distributions as tar files. These are built in the
-         * {@code com.oracle.truffle.r.test.native} project in the {@code packages} directory.
-         */
-        private final Path rpackagesDists;
-        /**
-         * The path to where the package will be installed (R_LIBS_USER).
+         * The path containing the package distributions as tar files.
          */
-        protected final Path rpackagesLibs;
-
-        private PackagePaths() {
-            Path rpackages = Paths.get(REnvVars.rHome(), "com.oracle.truffle.r.test", "rpackages");
-            rpackagesLibs = TestBase.relativize(rpackages.resolve("testrlibs_user"));
-            // Empty it in case of failure that didn't clean up
-            if (rpackagesLibs.toFile().exists()) {
-                try {
-                    Files.walkFileTree(rpackagesLibs, new SimpleFileVisitor<Path>() {
-
-                        @Override
-                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-                            Files.delete(file);
-                            return FileVisitResult.CONTINUE;
-                        }
+        private final Path packagePath;
 
-                        @Override
-                        public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
-                            if (e == null) {
-                                Files.delete(dir);
-                                return FileVisitResult.CONTINUE;
-                            } else {
-                                // directory iteration failed
-                                throw e;
-                            }
-                        }
-
-                    });
-                } catch (IOException e) {
-                    assert false;
-                }
-            }
-
-            rpackagesLibs.toFile().mkdirs();
-            rpackagesDists = Paths.get(REnvVars.rHome(), "com.oracle.truffle.r.test.native", "packages");
+        private PackagePaths(Path rpackagesDists) {
+            this.packagePath = rpackagesDists;
         }
 
-        protected boolean installPackage(String packageName) {
-            Path packagePath = rpackagesDists.resolve(packageName).resolve("lib").resolve(packageName + ".tar");
+        protected boolean installPackage() {
             String[] cmds = new String[4];
             if (generatingExpected()) {
                 // use GnuR
@@ -112,7 +88,7 @@ public class TestRPackages extends TestBase {
             cmds[cmds.length - 1] = packagePath.toString();
             ProcessBuilder pb = new ProcessBuilder(cmds);
             Map<String, String> env = pb.environment();
-            env.put("R_LIBS_USER", rpackagesLibs.toString());
+            env.put("R_LIBS_USER", installDir().toString());
             if (!generatingExpected()) {
                 env.put("R_INSTALL_TAR", RContext.getInstance().stateREnvVars.get("TAR"));
             }
@@ -147,25 +123,73 @@ public class TestRPackages extends TestBase {
             }
         }
 
-        protected boolean uninstallPackage(String packageName) {
-            Path packageDir = rpackagesLibs.resolve(packageName);
-            try {
-                deleteDir(packageDir);
-            } catch (Exception e) {
-                e.printStackTrace();
-                return false;
-            }
-            return true;
+    }
+
+    /**
+     * The path to the install directory. This is fixed across all tests.
+     */
+    private static Path installDir;
+
+    /**
+     * Map from package name to info on its location.
+     */
+    private static final Map<String, PackagePaths> packageMap = new HashMap<>();
+
+    private static Path installDir() {
+        if (installDir == null) {
+            installDir = TestBase.relativize(Paths.get(REnvVars.rHome(), "com.oracle.truffle.r.test", "rpackages", "testrlibs_user"));
         }
+        return installDir;
+    }
 
+    protected static String libLoc() {
+        return installDir().toString();
     }
 
-    protected static final PackagePaths packagePaths = new PackagePaths();
+    private static boolean uninstallPackage(String packageName) {
+        Path packageDir = installDir().resolve(packageName);
+        try {
+            deleteDir(packageDir);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Pass a custom subclass of this class to override the actual location of the package tar file.
+     */
+    protected static class Resolver {
+        Path getPath(String p) {
+            return testNativePath().resolve(p).resolve("lib").resolve(p + ".tar");
+        }
+    }
+
+    private static Path testNativePath() {
+        return Paths.get(REnvVars.rHome(), "com.oracle.truffle.r.test.native", "packages");
+    }
+
+    private static PackagePaths getPackagePaths(String pkg, Path path) {
+        PackagePaths result = packageMap.get(pkg);
+        if (result == null) {
+            result = new PackagePaths(path);
+            packageMap.put(pkg, result);
+        }
+        return result;
+    }
 
     protected static void setupInstallTestPackages(String[] testPackages) {
+        setupInstallTestPackages(testPackages, new Resolver());
+    }
+
+    protected static void setupInstallTestPackages(String[] testPackages, Resolver resolver) {
         if (!checkOnly()) {
+            TestBase.deleteDir(installDir());
+            installDir().toFile().mkdirs();
             for (String p : testPackages) {
-                if (!packagePaths.installPackage(p)) {
+                PackagePaths packagePaths = getPackagePaths(p, resolver.getPath(p));
+                if (!packagePaths.installPackage()) {
                     throw new AssertionError();
                 }
             }
@@ -175,7 +199,7 @@ public class TestRPackages extends TestBase {
     protected static void tearDownUninstallTestPackages(String[] testPackages) {
         if (!checkOnly()) {
             for (String p : testPackages) {
-                if (!packagePaths.uninstallPackage(p)) {
+                if (!uninstallPackage(p)) {
                     throw new AssertionError();
                 }
             }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRecommendedPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRecommendedPackages.java
new file mode 100644
index 0000000000..e52b79a15a
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRecommendedPackages.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.rpackages;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.oracle.truffle.r.runtime.REnvVars;
+import com.oracle.truffle.r.runtime.RVersionNumber;
+import com.oracle.truffle.r.test.TestBase;
+
+/**
+ * Test the installation of the "recommended" packages that come with GnuR. N.B. There are no
+ * specific tests beyond install/load as that is handled separately in the package testing
+ * framework. We are primarily concerned with detecting installation regressions.
+ *
+ */
+public class TestRecommendedPackages extends TestRPackages {
+    private static final String[] OK_PACKAGES = new String[]{"MASS", "boot", "class", "cluster", "codetools", "foreign", "lattice", "nnet", "spatial", "survival"};
+    @SuppressWarnings("unused") private static final String[] PROBLEM_PACKAGES = new String[]{"KernSmooth", "Matrix", "nlme", "rpart"};
+
+    private static Path getRecommendedPath() {
+        return Paths.get(REnvVars.rHome(), "com.oracle.truffle.r.native", "gnur", RVersionNumber.R_HYPHEN_FULL, "src", "library", "Recommended");
+    }
+
+    @BeforeClass
+    public static void setupInstallMyTestPackages() {
+        setupInstallTestPackages(OK_PACKAGES, new Resolver() {
+            @Override
+            Path getPath(String p) {
+                return getRecommendedPath().resolve(p + ".tgz");
+            }
+        });
+    }
+
+    @AfterClass
+    public static void tearDownUninstallMyTestPackages() {
+        tearDownUninstallTestPackages(OK_PACKAGES);
+    }
+
+    @Test
+    public void testLoad() {
+        assertEval(TestBase.template("{ library(%1, lib.loc = \"%0\"); detach(\"package:%1\"); }",
+                        new String[]{TestRPackages.libLoc()}, OK_PACKAGES));
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestS4TestPackage.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestS4TestPackage.java
index 13144ae4d4..6cb8a5c1a3 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestS4TestPackage.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestS4TestPackage.java
@@ -47,23 +47,24 @@ public class TestS4TestPackage extends TestRPackages {
 
     @Test
     public void testS4Load() {
-        assertEval(TestBase.template("{ library(\"tests4\", lib.loc = \"%0\"); detach(\"package:tests4\"); unloadNamespace(\"tests4\") }", new String[]{packagePaths.rpackagesLibs.toString()}));
+        assertEval(TestBase.template("{ library(\"tests4\", lib.loc = \"%0\"); detach(\"package:tests4\"); unloadNamespace(\"tests4\") }",
+                        new String[]{TestRPackages.libLoc()}));
     }
 
     @Test
     public void testS4Execute() {
         assertEval(TestBase.template(
                         "{ library(\"tests4\", lib.loc = \"%0\"); r<-print(tests4:::inspect.vehicle(new(\"Car\"), new(\"Inspector\"))); detach(\"package:tests4\"); unloadNamespace(\"tests4\"); r }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
         assertEval(TestBase.template(
                         "{ library(\"tests4\", lib.loc = \"%0\"); r<-print(tests4:::inspect.vehicle(new(\"Truck\"), new(\"Inspector\"))); detach(\"package:tests4\"); unloadNamespace(\"tests4\"); r }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
         assertEval(TestBase.template(
                         "{ library(\"tests4\", lib.loc = \"%0\"); r<-print(tests4:::inspect.vehicle(new(\"Car\"), new(\"StateInspector\"))); detach(\"package:tests4\"); unloadNamespace(\"tests4\"); r }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
         assertEval(TestBase.template(
                         "{ library(\"tests4\", lib.loc = \"%0\"); r<-print(tests4:::inspect.vehicle(new(\"Truck\"), new(\"StateInspector\"))); detach(\"package:tests4\"); unloadNamespace(\"tests4\"); r }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
     }
 
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestVanillaPackage.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestVanillaPackage.java
index 3c94ad8785..8f9d0c5541 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestVanillaPackage.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestVanillaPackage.java
@@ -47,13 +47,13 @@ public class TestVanillaPackage extends TestRPackages {
 
     @Test
     public void testLoadVanilla() {
-        assertEval(TestBase.template("{ library(\"vanilla\", lib.loc = \"%0\"); r <- vanilla(); detach(\"package:vanilla\"); r }", new String[]{packagePaths.rpackagesLibs.toString()}));
+        assertEval(TestBase.template("{ library(\"vanilla\", lib.loc = \"%0\"); r <- vanilla(); detach(\"package:vanilla\"); r }", new String[]{TestRPackages.libLoc()}));
     }
 
     @Test
     public void testSimpleFunction() {
         assertEval(TestBase.template("{ library(\"vanilla\", lib.loc = \"%0\"); r <- functionTest(c(1,2,3,4,5,6),8:10); detach(\"package:vanilla\"); r }",
-                        new String[]{packagePaths.rpackagesLibs.toString()}));
+                        new String[]{TestRPackages.libLoc()}));
     }
 
 }
-- 
GitLab