|
@@ -45,9 +45,12 @@ import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Iterator;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
import java.util.StringTokenizer;
|
|
|
import java.util.jar.JarFile;
|
|
|
import java.util.jar.Manifest;
|
|
@@ -1836,4 +1839,340 @@ public class TestContainerLaunch extends BaseContainerManagerTest {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ private static void assertOrderEnvByDependencies(
|
|
|
+ final Map<String, String> env,
|
|
|
+ final ContainerLaunch.ShellScriptBuilder sb) {
|
|
|
+ LinkedHashMap<String, String> copy = new LinkedHashMap<>();
|
|
|
+ copy.putAll(env);
|
|
|
+ Map<String, String> ordered = sb.orderEnvByDependencies(env);
|
|
|
+ // 1st, check that env and copy are the same
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Input env map has been altered because its size changed",
|
|
|
+ copy.size(), env.size()
|
|
|
+ );
|
|
|
+ final Iterator<Map.Entry<String, String>> ai = env.entrySet().iterator();
|
|
|
+ for (Map.Entry<String, String> e : copy.entrySet()) {
|
|
|
+ Map.Entry<String, String> a = ai.next();
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Keys have been reordered in input env map",
|
|
|
+ // env must not be altered at all, so we don't use String.equals
|
|
|
+ // copy and env must use the same String refs
|
|
|
+ e.getKey() == a.getKey()
|
|
|
+ );
|
|
|
+ Assert.assertTrue(
|
|
|
+ "Key "+e.getKey()+" does not longer points to its "
|
|
|
+ +"original value have been reordered in input env map",
|
|
|
+ // env must be altered at all, so we don't use String.equals
|
|
|
+ // copy and env must use the same String refs
|
|
|
+ e.getValue() == a.getValue()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ // 2nd, check the ordered version as the expected ordering
|
|
|
+ // and did not altered values
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Input env map and ordered env map must have the same size, env="+env+
|
|
|
+ ", ordered="+ordered, env.size(), ordered.size()
|
|
|
+ );
|
|
|
+ int iA = -1;
|
|
|
+ int iB = -1;
|
|
|
+ int iC = -1;
|
|
|
+ int iD = -1;
|
|
|
+ int icA = -1;
|
|
|
+ int icB = -1;
|
|
|
+ int icC = -1;
|
|
|
+ int i=0;
|
|
|
+ for (Map.Entry<String, String> e: ordered.entrySet()) {
|
|
|
+ if ("A".equals(e.getKey())) {
|
|
|
+ iA = i++;
|
|
|
+ } else if ("B".equals(e.getKey())) {
|
|
|
+ iB = i++;
|
|
|
+ } else if ("C".equals(e.getKey())) {
|
|
|
+ iC = i++;
|
|
|
+ } else if ("D".equals(e.getKey())) {
|
|
|
+ iD = i++;
|
|
|
+ } else if ("cyclic_A".equals(e.getKey())) {
|
|
|
+ icA = i++;
|
|
|
+ } else if ("cyclic_B".equals(e.getKey())) {
|
|
|
+ icB = i++;
|
|
|
+ } else if ("cyclic_C".equals(e.getKey())) {
|
|
|
+ icC = i++;
|
|
|
+ } else {
|
|
|
+ Assert.fail("Test need to ne fixed, got an unexpected env entry "+
|
|
|
+ e.getKey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // expected order : A<B<C<{D,cyclic_A,cyclic_B,cyclic_C}
|
|
|
+ // B depends on A, C depends on B so there are assertion on B>A and C>B
|
|
|
+ // but there is no assertion about C>A because B might be missing in some
|
|
|
+ // broken envs
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", B should be after A", iA<0 || iB<0 || iA<iB);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", C should be after B", iB<0 || iC<0 || iB<iC);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", D should be after A", iA<0 || iD<0 || iA<iD);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", D should be after B", iB<0 || iD<0 || iB<iD);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_A should be after C", iC<0 || icA<0 || icB<0 || icC<0 ||
|
|
|
+ iC<icA);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_B should be after C", iC<0 || icB<0 || icC<0 ||
|
|
|
+ iC<icB);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_C should be after C", iC<0 || icC<0 || iC<icC);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_A should be after cyclic_B if no cyclic_C", icC>=0 ||
|
|
|
+ icA<0 || icB<0 || icB<icA);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_B should be after cyclic_C if no cyclic_A", icA>=0 ||
|
|
|
+ icB<0 || icC<0 || icC<icB);
|
|
|
+ Assert.assertTrue("when reordering "+env+" into "+ordered+
|
|
|
+ ", cyclic_C should be after cyclic_A if no cyclic_B", icA>=0 ||
|
|
|
+ icC<0 || icA<0 || icA<icC);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test(timeout = 1000)
|
|
|
+ public void testGetEnvDependencies() {
|
|
|
+ final Set<String> expected = new HashSet<>();
|
|
|
+ final ContainerLaunch.ShellScriptBuilder bash =
|
|
|
+ ContainerLaunch.ShellScriptBuilder.create(Shell.OSType.OS_TYPE_LINUX);
|
|
|
+ String s;
|
|
|
+
|
|
|
+ s = null;
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "A";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "\\$A";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "$$";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "$1";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "handle \"'$A'\" simple quotes";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "$ crash test for StringArrayOutOfBoundException";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${ crash test for StringArrayOutOfBoundException";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${# crash test for StringArrayOutOfBoundException";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "crash test for StringArrayOutOfBoundException $";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "crash test for StringArrayOutOfBoundException ${";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "crash test for StringArrayOutOfBoundException ${#";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("A");
|
|
|
+ s = "$A";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${A}";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${#A[*]}";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "in the $A midlle";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("B");
|
|
|
+ s = "${A:-$B} var in var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${A}$B var outside var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("C");
|
|
|
+ s = "$A:$B:$C:pathlist var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+ s = "${A}/foo/bar:$B:${C}:pathlist var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ bash.getEnvDependencies(s));
|
|
|
+
|
|
|
+ ContainerLaunch.ShellScriptBuilder win =
|
|
|
+ ContainerLaunch.ShellScriptBuilder.create(Shell.OSType.OS_TYPE_WIN);
|
|
|
+ expected.clear();
|
|
|
+ s = null;
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "A";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%%%%%%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%%A%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A:";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("A");
|
|
|
+ s = "%A%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%%%A%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%%C%A%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A:~-1%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A%B%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A%%%%%B%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("B");
|
|
|
+ s = "%A%%B%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A%%%%B%";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+
|
|
|
+ expected.add("C");
|
|
|
+ s = "%A%:%B%:%C%:pathlist var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ s = "%A%\\foo\\bar:%B%:%C%:pathlist var";
|
|
|
+ Assert.assertEquals("failed to parse " + s, expected,
|
|
|
+ win.getEnvDependencies(s));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Set<String> asSet(String...str) {
|
|
|
+ final Set<String> set = new HashSet<>();
|
|
|
+ Collections.addAll(set, str);
|
|
|
+ return set;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test(timeout = 5000)
|
|
|
+ public void testOrderEnvByDependencies() {
|
|
|
+ final Map<String, Set<String>> fakeDeps = new HashMap<>();
|
|
|
+ fakeDeps.put("Aval",
|
|
|
+ Collections.<String>emptySet()); // A has no dependencies
|
|
|
+ fakeDeps.put("Bval", asSet("A")); // B depends on A
|
|
|
+ fakeDeps.put("Cval", asSet("B")); // C depends on B
|
|
|
+ fakeDeps.put("Dval", asSet("A", "B")); // C depends on B
|
|
|
+ fakeDeps.put("cyclic_Aval", asSet("cyclic_B"));
|
|
|
+ fakeDeps.put("cyclic_Bval", asSet("cyclic_C"));
|
|
|
+ fakeDeps.put("cyclic_Cval", asSet("cyclic_A", "C"));
|
|
|
+
|
|
|
+ final ContainerLaunch.ShellScriptBuilder sb =
|
|
|
+ new ContainerLaunch.ShellScriptBuilder() {
|
|
|
+ @Override public Set<String> getEnvDependencies(final String envVal) {
|
|
|
+ return fakeDeps.get(envVal);
|
|
|
+ }
|
|
|
+ @Override protected void mkdir(Path path) throws IOException {}
|
|
|
+ @Override public void listDebugInformation(Path output)
|
|
|
+ throws IOException {}
|
|
|
+ @Override protected void link(Path src, Path dst)
|
|
|
+ throws IOException {}
|
|
|
+ @Override public void env(String key, String value)
|
|
|
+ throws IOException {}
|
|
|
+ @Override public void copyDebugInformation(Path src, Path dst)
|
|
|
+ throws IOException {}
|
|
|
+ @Override public void command(List<String> command)
|
|
|
+ throws IOException {}
|
|
|
+ @Override public void setStdOut(Path stdout)
|
|
|
+ throws IOException {};
|
|
|
+ @Override public void setStdErr(Path stdout)
|
|
|
+ throws IOException {};
|
|
|
+ @Override public void echo(String echoStr)
|
|
|
+ throws IOException {};
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ Assert.assertNull("Ordering a null env map must return a null value.",
|
|
|
+ sb.orderEnvByDependencies(null));
|
|
|
+ } catch (Exception e) {
|
|
|
+ Assert.fail("null value is to be supported");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Assert.assertEquals(
|
|
|
+ "Ordering an empty env map must return an empty map.",
|
|
|
+ 0, sb.orderEnvByDependencies(Collections.<String, String>emptyMap())
|
|
|
+ .size()
|
|
|
+ );
|
|
|
+ } catch (Exception e) {
|
|
|
+ Assert.fail("Empty map is to be supported");
|
|
|
+ }
|
|
|
+
|
|
|
+ final Map<String, String> combination = new LinkedHashMap<>();
|
|
|
+ // to test all possible cases, we create all possible combinations and test
|
|
|
+ // each of them
|
|
|
+ class TestEnv {
|
|
|
+ private final String key;
|
|
|
+ private final String value;
|
|
|
+ private boolean used=false;
|
|
|
+ TestEnv(String key, String value) {
|
|
|
+ this.key = key;
|
|
|
+ this.value = value;
|
|
|
+ }
|
|
|
+ void generateCombinationAndTest(int nbItems,
|
|
|
+ final ArrayList<TestEnv> keylist) {
|
|
|
+ used = true;
|
|
|
+ combination.put(key, value);
|
|
|
+ try {
|
|
|
+ if (nbItems == 0) {
|
|
|
+ //LOG.info("Combo : " + combination);
|
|
|
+ assertOrderEnvByDependencies(combination, sb);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (TestEnv localEnv: keylist) {
|
|
|
+ if (!localEnv.used) {
|
|
|
+ localEnv.generateCombinationAndTest(nbItems - 1, keylist);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ combination.remove(key);
|
|
|
+ used=false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ final ArrayList<TestEnv> keys = new ArrayList<>();
|
|
|
+ for (String key : new String[] {"A", "B", "C", "D",
|
|
|
+ "cyclic_A", "cyclic_B", "cyclic_C"}) {
|
|
|
+ keys.add(new TestEnv(key, key+"val"));
|
|
|
+ }
|
|
|
+ for (int count=keys.size(); count > 0; count--) {
|
|
|
+ for (TestEnv env : keys) {
|
|
|
+ env.generateCombinationAndTest(count, keys);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|