Merge "Enable dx tests to run in parallel by default"
diff --git a/dx/tests/run-all-tests b/dx/tests/run-all-tests
index baf79e5..1c6be02 100755
--- a/dx/tests/run-all-tests
+++ b/dx/tests/run-all-tests
@@ -33,85 +33,141 @@
 progdir=`pwd`
 prog="${progdir}"/`basename "${prog}"`
 
+# Command-line options
+skip_tests=""
+sequential="no"
+usage="no"
+while [[ "$1" == "-"* ]]; do
+  case $1 in
+    --seq) sequential="yes" ;;
+    --skip) skip_tests="$2 $skip_tests"
+            shift ;;
+    *) usage="yes" ;;
+  esac
+  shift
+done
+
+if [ $usage = "yes" ]; then
+    prog=`basename $prog`
+    cat 1>&2 <<END_USAGE
+Usage:
+  $prog [options]   Run all tests with given options.
+Options:
+  --seq             Run tests sequentially (default: parallel)
+  --skip <test>     Skip running specified test
+END_USAGE
+    exit 1
+fi
+
 # Globals for tracking numbers of successes and failures and their names.
-passed=0
-surprised=0
-surprisedNames=""
-ignored=0
-ignoredNames=""
-failed=0
-failedNames=""
+passed=()
+surprised=()
+ignored=()
+failed=()
+skipped=()
 
 # Tests failing and require attention (e.g. 115-merge)
-knownBad="100-local-mismatch 115-merge 119-merge-conflict"
+known_bad="100-local-mismatch 115-merge 119-merge-conflict"
 
 function display_results {
   printf    "\n\nTest Results\n"
   printf -- "----------------------------\n"
-  printf    "Pass:                   % 3d\n" $passed
-  printf    "Surprise pass:          % 3d\n" $surprised
-  printf    "Known failures:         % 3d\n" $ignored
-  printf    "Failures:               % 3d\n" $failed
+  printf    "Pass:                   % 4d\n" ${#passed[@]}
+  printf    "Surprise pass:          % 4d\n" ${#surprised[@]}
+  printf    "Known failures:         % 4d\n" ${#ignored[@]}
+  printf    "Failures:               % 4d\n" ${#failed[@]}
+  printf    "Skipped:                % 4d\n" ${#skipped[@]}
   printf -- "----------------------------\n"
-  printf    "Elapsed time(s):        % 3d\n" $SECONDS
+  printf    "Elapsed time(s):        % 4d\n" $SECONDS
 
-  list_files "Unexpected successes" "$surprisedNames"
-  list_files "Known failures" "$ignoredNames"
-  list_files "Failures" "$failedNames"
+  list_files "Unexpected successes" ${surprised[@]}
+  list_files "Known failures" ${ignored[@]}
+  list_files "Failures" ${failed[@]}
+  list_files "Skipped" ${skipped[@]}
 
-  needingAttention=$((failed + surprised))
-  exit $needingAttention
+  needing_attention=$(( ${#failed[@]} + ${#surprised[@]} ))
+  exit ${needing_attention}
 }
 
 function list_files {
-  title=$1
-  if [[ "$2" = "" ]]; then
-    names="NONE"
-  else
-    names=$2
+  # Arguments: Title test_name0 test_name1 ... test_nameN
+  echo "$1:"
+  shift
+  if [[ "$1" = "" ]]; then
+    echo "  NONE"
+    return
   fi
-
-  echo
-  echo "$title:"
-  for i in $names; do
-    echo "  $i"
+  while [[ "$1" != "" ]]; do
+    echo "  $1"
+    shift
   done
 }
 
-function run_tests {
-  for i in *; do
-    if [ -d "$i" -a -r "$i" ]; then
-      if [[ "$knownBad" == *"$i"* ]]; then
-          expectFail=1
-      else
-        expectFail=0
-      fi
+function update_result {
+  test_name=$1
+  output=$2
+  result=$3
 
-      output=/tmp/$$/$i
-      ./run-test --output_dir "$output" "$i"
-      if [ $? = 0 ]; then
-        if [[ $expectFail = 0 ]]; then
-            ((passed++))
-        else
-          echo "Failing on unexpected success of $i"
-          ((surprised++))
-          surprisedNames="$surprisedNames $i"
-        fi
-      else
-        if [[ $expectFail = 0 ]]; then
-          ((failed++))
-          failedNames="$failedNames $i"
-        else
-          echo "Ignoring expected failure of $i"
-          ((ignored++))
-          ignoredNames="$ignoredNames $i"
-          # Clean up when we expect a test to fail.
-          # run-test only does this on success.
-          rm -rf "$output"
-        fi
-      fi
+  if [[ "$known_bad" == *"$test_name"* ]]; then
+    expectFail=1
+  else
+    expectFail=0
+  fi
+  if [ $result = 0 ]; then
+    if [[ $expectFail = 0 ]]; then
+      passed+=(${test_name})
+    else
+      echo "Failing on unexpected success of $test_name"
+      surprised+=(${test_name})
     fi
-  done
+  else
+    if [[ $expectFail = 0 ]]; then
+      failed+=(${test_name})
+    else
+      echo "Ignoring expected failure of $test_name"
+      ignored+=(${test_name})
+      # Clean up when we expect a test to fail.
+      # run-test only does this on success.
+      rm -rf "$output"
+    fi
+  fi
+}
+
+function run_tests {
+  if [[ "$sequential" = "yes" ]]; then
+    for test_name in *; do
+      if [[ "$skip_tests" = *"$test_name"* ]]; then
+          skipped+=(${test_name})
+          continue
+      fi
+      if [ -d "$test_name" -a -r "$test_name" ]; then
+        output=/tmp/$$/$test_name
+        ./run-test --output_dir "$output" "$test_name"
+        update_result $test_name $output $?
+      fi
+    done
+  else
+    i=0
+    for test_name in *; do
+      if [[ "$skip_tests" = *"$test_name"* ]]; then
+          skipped+=(${test_name})
+          continue
+      fi
+      if [ -d "$test_name" -a -r "$test_name" ]; then
+          output=/tmp/$$/$test_name
+          ./run-test --output_dir "$output" "$test_name" &
+          test_pids[i]=$!
+          test_names[test_pids[i]]="$test_name"
+          test_outputs[test_pids[i]]="output"
+          let i+=1
+      fi
+    done
+
+    for pid in ${test_pids[@]}; do
+      wait $pid
+      update_result ${test_names[$pid]} ${test_outputs[$pid]} $?
+    done
+  fi
 }
 
 function handle_interrupt {