You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ep...@apache.org on 2019/03/07 16:40:58 UTC
[hadoop] branch branch-2.9 updated: YARN-5714. ContainerExecutor
does not order environment map. Contributed by Remi Catherinot and Jim
Brennan.
This is an automated email from the ASF dual-hosted git repository.
epayne pushed a commit to branch branch-2.9
in repository https://gitbox.apache.org/repos/asf/hadoop.git
The following commit(s) were added to refs/heads/branch-2.9 by this push:
new 796147d YARN-5714. ContainerExecutor does not order environment map. Contributed by Remi Catherinot and Jim Brennan.
796147d is described below
commit 796147dd33e0e439e8e8f9f4a9d8d84517355ccd
Author: Eric E Payne <er...@verizonmedia.com>
AuthorDate: Thu Mar 7 16:22:36 2019 +0000
YARN-5714. ContainerExecutor does not order environment map. Contributed by Remi Catherinot and Jim Brennan.
(cherry picked from commit 6e6ebc368c2fec178107c55a93356f56c58def22)
---
.../yarn/server/nodemanager/ContainerExecutor.java | 3 +-
.../containermanager/launcher/ContainerLaunch.java | 194 +++++++++++-
.../launcher/TestContainerLaunch.java | 339 +++++++++++++++++++++
3 files changed, 533 insertions(+), 3 deletions(-)
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
index 9454da4..1851a1d 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
@@ -340,7 +340,8 @@ public abstract class ContainerExecutor implements Configurable {
if (environment != null) {
sb.echo("Setting up env variables");
- for (Map.Entry<String, String> env : environment.entrySet()) {
+ for (Map.Entry<String, String> env :
+ sb.orderEnvByDependencies(environment).entrySet()) {
sb.env(env.getKey(), env.getValue());
}
}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
index 7ff0baf..2482c88 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
@@ -30,11 +30,16 @@ import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -977,8 +982,14 @@ public class ContainerLaunch implements Callable<Integer> {
public static abstract class ShellScriptBuilder {
public static ShellScriptBuilder create() {
- return Shell.WINDOWS ? new WindowsShellScriptBuilder() :
- new UnixShellScriptBuilder();
+ return create(Shell.osType);
+ }
+
+ @VisibleForTesting
+ public static ShellScriptBuilder create(Shell.OSType osType) {
+ return (osType == Shell.OSType.OS_TYPE_WIN) ?
+ new WindowsShellScriptBuilder() :
+ new UnixShellScriptBuilder();
}
private static final String LINE_SEPARATOR =
@@ -1104,6 +1115,72 @@ public class ContainerLaunch implements Callable<Integer> {
return redirectStdErr;
}
+ /**
+ * Parse an environment value and returns all environment keys it uses.
+ * @param envVal an environment variable's value
+ * @return all environment variable names used in <code>envVal</code>.
+ */
+ public Set<String> getEnvDependencies(final String envVal) {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns a dependency ordered version of <code>envs</code>. Does not alter
+ * input <code>envs</code> map.
+ * @param envs environment map
+ * @return a dependency ordered version of <code>envs</code>
+ */
+ public final Map<String, String> orderEnvByDependencies(
+ Map<String, String> envs) {
+ if (envs == null || envs.size() < 2) {
+ return envs;
+ }
+ final Map<String, String> ordered = new LinkedHashMap<String, String>();
+ class Env {
+ private boolean resolved = false;
+ private final Collection<Env> deps = new ArrayList<>();
+ private final String name;
+ private final String value;
+ Env(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+ void resolve() {
+ resolved = true;
+ for (Env dep : deps) {
+ if (!dep.resolved) {
+ dep.resolve();
+ }
+ }
+ ordered.put(name, value);
+ }
+ }
+ final Map<String, Env> singletons = new HashMap<>();
+ for (Map.Entry<String, String> e : envs.entrySet()) {
+ Env env = singletons.get(e.getKey());
+ if (env == null) {
+ env = new Env(e.getKey(), e.getValue());
+ singletons.put(env.name, env);
+ }
+ for (String depStr : getEnvDependencies(env.value)) {
+ if (!envs.containsKey(depStr)) {
+ continue;
+ }
+ Env depEnv = singletons.get(depStr);
+ if (depEnv == null) {
+ depEnv = new Env(depStr, envs.get(depStr));
+ singletons.put(depStr, depEnv);
+ }
+ env.deps.add(depEnv);
+ }
+ }
+ for (Env env : singletons.values()) {
+ if (!env.resolved) {
+ env.resolve();
+ }
+ }
+ return ordered;
+ }
}
private static final class UnixShellScriptBuilder extends ShellScriptBuilder {
@@ -1195,6 +1272,79 @@ public class ContainerLaunch implements Callable<Integer> {
public void setExitOnFailure() {
line("set -o pipefail -e");
}
+
+ /**
+ * Parse <code>envVal</code> using bash-like syntax to extract env variables
+ * it depends on.
+ */
+ @Override
+ public Set<String> getEnvDependencies(final String envVal) {
+ if (envVal == null || envVal.isEmpty()) {
+ return Collections.emptySet();
+ }
+ final Set<String> deps = new HashSet<>();
+ // env/whitelistedEnv dump values inside double quotes
+ boolean inDoubleQuotes = true;
+ char c;
+ int i = 0;
+ final int len = envVal.length();
+ while (i < len) {
+ c = envVal.charAt(i);
+ if (c == '"') {
+ inDoubleQuotes = !inDoubleQuotes;
+ } else if (c == '\'' && !inDoubleQuotes) {
+ i++;
+ // eat until closing simple quote
+ while (i < len) {
+ c = envVal.charAt(i);
+ if (c == '\\') {
+ i++;
+ }
+ if (c == '\'') {
+ break;
+ }
+ i++;
+ }
+ } else if (c == '\\') {
+ i++;
+ } else if (c == '$') {
+ i++;
+ if (i >= len) {
+ break;
+ }
+ c = envVal.charAt(i);
+ if (c == '{') { // for ${... bash like syntax
+ i++;
+ if (i >= len) {
+ break;
+ }
+ c = envVal.charAt(i);
+ if (c == '#') { // for ${#... bash array syntax
+ i++;
+ if (i >= len) {
+ break;
+ }
+ }
+ }
+ final int start = i;
+ while (i < len) {
+ c = envVal.charAt(i);
+ if (c != '$' && (
+ (i == start && Character.isJavaIdentifierStart(c)) ||
+ (i > start && Character.isJavaIdentifierPart(c)))) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ if (i > start) {
+ deps.add(envVal.substring(start, i));
+ }
+ }
+ i++;
+ }
+ return deps;
+ }
}
private static final class WindowsShellScriptBuilder
@@ -1276,6 +1426,46 @@ public class ContainerLaunch implements Callable<Integer> {
String.format("@echo \"dir:\" > \"%s\"", output.toString()));
lineWithLenCheck(String.format("dir >> \"%s\"", output.toString()));
}
+
+ /**
+ * Parse <code>envVal</code> using cmd/bat-like syntax to extract env
+ * variables it depends on.
+ */
+ public Set<String> getEnvDependencies(final String envVal) {
+ if (envVal == null || envVal.isEmpty()) {
+ return Collections.emptySet();
+ }
+ final Set<String> deps = new HashSet<>();
+ final int len = envVal.length();
+ int i = 0;
+ while (i < len) {
+ i = envVal.indexOf('%', i); // find beginning of variable
+ if (i < 0 || i == (len - 1)) {
+ break;
+ }
+ i++;
+ // 3 cases: %var%, %var:...% or %%
+ final int j = envVal.indexOf('%', i); // find end of variable
+ if (j == i) {
+ // %% case, just skip it
+ i++;
+ continue;
+ }
+ if (j < 0) {
+ break; // even %var:...% syntax ends with a %, so j cannot be negative
+ }
+ final int k = envVal.indexOf(':', i);
+ if (k >= 0 && k < j) {
+ // %var:...% syntax
+ deps.add(envVal.substring(i, k));
+ } else {
+ // %var% syntax
+ deps.add(envVal.substring(i, j));
+ }
+ i = j + 1;
+ }
+ return deps;
+ }
}
private static void putEnvIfNotNull(
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
index c27cca6..7f51ccd 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
@@ -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);
+ }
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org