WIP: Experiment with using lit as a test runner
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1a0a621..df4badf 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -386,6 +386,12 @@
           # some native-dependent tests fail because of the lack of native
           # headers on emsdk-bundled clang
           test_targets: "other skip:other.test_native_link_error_message"
+  test-lit:
+    executor: bionic
+    steps:
+      - run: pip3 install -r requirements-dev.txt
+      - run-tests:
+          test_targets: "lit"
   test-browser-chrome:
     executor: bionic
     steps:
diff --git a/.flake8 b/.flake8
index 41ccb5b..66ff303 100644
--- a/.flake8
+++ b/.flake8
@@ -10,6 +10,8 @@
  ./tools/ffdb.py,
  ./system/lib/, # system libraries
  ./cache/, # download/built content
+ ./tests/lit/lit.cfg.py,
+ ./tests/lit/lit.site.cfg.py,
  .git
 # The ports plugins have a lot of unused imports becuase
 # they need to implement the specific plugin APIs
diff --git a/.gitignore b/.gitignore
index 0f0da2a..262c073 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,8 @@
 # Ignore only the root node_modules, but not any further down the path
 /node_modules/
 
+/tests/out/
+
 # Python coverage tool output
 .coverage
 .coverage.*
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 860d6ee..7b3780c 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -7,4 +7,6 @@
 flake8==3.9.0
 flake8-unused-arguments==0.0.6
 coverage==5.5
-sphinx==2.4.4
\ No newline at end of file
+sphinx==2.4.4
+filecheck==0.0.17
+lit==0.11.0.post1
diff --git a/tests/em-lit b/tests/em-lit
new file mode 100755
index 0000000..232f947
--- /dev/null
+++ b/tests/em-lit
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Copyright 2020 The Emscripten Authors.  All rights reserved.
+# Emscripten is available under two separate licenses, the MIT license and the
+# University of Illinois/NCSA Open Source License.  Both these licenses can be
+# found in the LICENSE file.
+#
+# Entry point for running python scripts on UNIX systems.
+#
+# Automatically generated by `create_entry_points.py`; DO NOT EDIT.
+#
+# To make modifications to this file, edit `tools/run_python.sh` and then run
+# `tools/create_entry_points.py`
+
+if [ -z "$PYTHON" ]; then
+  PYTHON=$EMSDK_PYTHON
+fi
+
+if [ -z "$PYTHON" ]; then
+  PYTHON=$(which python3 2> /dev/null)
+fi
+
+if [ -z "$PYTHON" ]; then
+  PYTHON=$(which python 2> /dev/null)
+fi
+
+if [ -z "$PYTHON" ]; then
+  echo 'unable to find python in $PATH'
+  exit 1
+fi
+
+exec "$PYTHON" "$0.py" "$@"
diff --git a/tests/em-lit.bat b/tests/em-lit.bat
new file mode 100644
index 0000000..f9ce949
--- /dev/null
+++ b/tests/em-lit.bat
@@ -0,0 +1,14 @@
+:: Entry point for running python scripts on windows systems.
+::
+:: Automatically generated by `create_entry_points.py`; DO NOT EDIT.
+::
+:: To make modifications to this file, edit `tools/run_python.bat` and then run
+:: `tools/create_entry_points.py`
+
+@setlocal
+@set EM_PY=%EMSDK_PYTHON%
+@if "%EM_PY%"=="" (
+  set EM_PY=python
+)
+
+@"%EM_PY%" "%~dp0\%~n0.py" %*
diff --git a/tests/em-lit.py b/tests/em-lit.py
new file mode 100755
index 0000000..ab96c80
--- /dev/null
+++ b/tests/em-lit.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+import os
+from lit.main import main
+
+# A simple wrapper around `lit` test running.   Loosely based on
+# llvm's llvm-lit script
+
+script_dir = os.path.dirname(os.path.abspath(__file__))
+
+builtin_parameters = {
+    'config_map': {
+        os.path.normcase(os.path.join(script_dir, 'lit/lit.cfg.py')):
+        os.path.normcase(os.path.join(script_dir, 'lit/lit.site.cfg.py'))
+    }
+}
+
+if __name__ == '__main__':
+    main(builtin_parameters)
diff --git a/tests/lit/lit.cfg.py b/tests/lit/lit.cfg.py
new file mode 100644
index 0000000..2940020
--- /dev/null
+++ b/tests/lit/lit.cfg.py
@@ -0,0 +1,26 @@
+import lit.formats
+import re
+
+config.name = "Emscripten lit tests"
+config.test_format = lit.formats.ShTest()
+
+config.suffixes = ['.cpp', '.c']
+
+config.test_source_root = config.src_root + '/tests/lit'
+config.test_exec_root = config.src_root + '/tests/out'
+
+# Replace all Binaryen tools with their absolute paths
+#bin_dir = os.path.join(config.binaryen_build_root, 'bin')
+#for tool_file in os.listdir(bin_dir):
+    #tool_path = config.binaryen_build_root + '/bin/' + tool_file
+    #tool = tool_file[:-4] if tool_file.endswith('.exe') else tool_file
+for tool in ('emcc', 'em++', 'emar'):
+    config.substitutions.append((re.escape(tool), config.src_root + '/' + tool))
+
+# Also make the `not` and `foreach` commands available
+for tool in ('not', 'foreach'):
+    tool_file = config.src_root + '/tests/lit/' + tool + '.py'
+    python = sys.executable.replace('\\', '/')
+    config.substitutions.append((tool, python + ' ' + tool_file))
+
+print(config.substitutions)
diff --git a/tests/lit/lit.site.cfg.py b/tests/lit/lit.site.cfg.py
new file mode 100644
index 0000000..383f863
--- /dev/null
+++ b/tests/lit/lit.site.cfg.py
@@ -0,0 +1,7 @@
+import os
+
+script_dir = os.path.dirname(os.path.abspath(__file__))
+config.src_root = os.path.dirname(os.path.dirname(script_dir))
+
+lit_config.load_config(
+    config, os.path.join(config.src_root, 'tests', 'lit', 'lit.cfg.py'))
diff --git a/tests/other/test_tsearch.c b/tests/lit/other/test_tsearch.c
similarity index 78%
rename from tests/other/test_tsearch.c
rename to tests/lit/other/test_tsearch.c
index 85bae1d..f07f180 100644
--- a/tests/other/test_tsearch.c
+++ b/tests/lit/other/test_tsearch.c
@@ -1,3 +1,18 @@
+// RUN: emcc %s -o %t.js
+// RUN: node %t.js | filecheck %s
+
+//      CHECK:     0
+// CHECK-NEXT:     1
+// CHECK-NEXT:     2
+// CHECK-NEXT:     3
+// CHECK-NEXT:     4
+// CHECK-NEXT:     5
+// CHECK-NEXT:     6
+// CHECK-NEXT:     7
+// CHECK-NEXT:     8
+// CHECK-NEXT:     9
+// CHECK-NEXT:    10
+
 #define _GNU_SOURCE
 
 #include <search.h>
diff --git a/tests/other/test_tsearch.out b/tests/other/test_tsearch.out
deleted file mode 100644
index 32d3817..0000000
--- a/tests/other/test_tsearch.out
+++ /dev/null
@@ -1,11 +0,0 @@
-     0
-     1
-     2
-     3
-     4
-     5
-     6
-     7
-     8
-     9
-    10
diff --git a/tests/runner.py b/tests/runner.py
index c0fcc3d..d88027c 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -26,12 +26,14 @@
 import operator
 import os
 import random
+import subprocess
 import sys
 import unittest
 
 # Setup
 
-__rootpath__ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+__scriptdir__ = os.path.dirname(os.path.abspath(__file__))
+__rootpath__ = os.path.dirname(__scriptdir__)
 sys.path.append(__rootpath__)
 
 import jsrun
@@ -68,6 +70,7 @@
 # The non-core test modes
 non_core_test_modes = [
   'other',
+  'lit',
   'browser',
   'sanity',
   'sockets',
@@ -372,6 +375,11 @@
     return arg
 
   tests = [prepend_default(t) for t in options.tests]
+  if 'lit' in tests:
+    subprocess.check_call([os.path.join(__scriptdir__, 'em-lit'), os.path.join(__scriptdir__, 'lit')])
+    tests.remove('lit')
+    if not tests:
+      return 0
 
   modules = get_and_import_modules()
   all_tests = get_all_tests(modules)
diff --git a/tests/test_other.py b/tests/test_other.py
index 3c38a6c..3063260 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -428,9 +428,6 @@
     self.assertNotExists(test_file('twopart_main.o'))
     self.assertNotExists(test_file('twopart_side.o'))
 
-  def test_tsearch(self):
-    self.do_other_test('test_tsearch.c')
-
   def test_combining_object_files(self):
     # Compiling two files with -c will generate separate object files
     self.run_process([EMCC, test_file('twopart_main.cpp'), test_file('twopart_side.c'), '-c'])
diff --git a/tools/create_entry_points.py b/tools/create_entry_points.py
index 7991155..e03782c 100755
--- a/tools/create_entry_points.py
+++ b/tools/create_entry_points.py
@@ -40,6 +40,7 @@
 tools/file_packager
 tools/webidl_binder
 tests/runner
+tests/em-lit
 '''.split()