Explorar el Código

[CI] General cleanup for tests (#10598)

* [tests] Refactor System utility functions

Reorder some imports

* [tests] Use finals

* [tests] Change python script to follow pep8

* [tests] Make downloads more convenient for local runs

- Do not reinstall packages that have already been installed
- Download everything to a single directory in the home folder
- Check if `apt-get` exists before running it

* [tests] Update readme

* [tests] Add lua to local sys test hxml

* Fix some quotes

* [CI] Remove duplicate  jobs

* [CI] Prevent workflow script from adding whitespaces

* [tests] Use function for misc test dirs
tobil4sk hace 3 años
padre
commit
8f668bef42

+ 58 - 144
.github/workflows/main.yml

@@ -31,23 +31,7 @@ jobs:
         with:
           submodules: recursive
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
-        run: |
-          set -ex
-      
-          curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
-          tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
-          NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
-          sudo mkdir -p /usr/local/bin
-          sudo mkdir -p /usr/local/lib/neko
-          sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools}  /usr/local/bin/
-          sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
-          sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
-          echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         shell: pwsh
         run: |
           Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
@@ -55,10 +39,10 @@ jobs:
           $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
           echo "$NEKOPATH" >> $env:GITHUB_PATH
           echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       - name: choco install nsis
         uses: nick-invision/retry@v1
         with:
@@ -71,7 +55,7 @@ jobs:
       - name: Prepend Chocolatey path
         shell: pwsh
         run: Write-Host "::add-path::C:\ProgramData\chocolatey\bin"
-      
+
       - name: Install OCaml and OCaml libraries
         shell: pwsh
         run: |
@@ -93,16 +77,16 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
-      
+
       - name: Expose mingw dll files
         shell: pwsh
         run: Write-Host "::add-path::${env:CYG_ROOT}/usr/$($env:MINGW_ARCH)-w64-mingw32/sys-root/mingw/bin"
-      
+
       - name: Set ADD_REVISION=1 for non-release
         if: ${{ !startsWith(github.ref, 'refs/tags/') }}
         shell: pwsh
         run: echo "ADD_REVISION=1" >> $Env:GITHUB_ENV
-      
+
       - name: Build Haxe
         shell: pwsh
         run: |
@@ -114,13 +98,13 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxe.exe')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxelib.exe')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && ls ./out')
-      
+
       - name: Upload artifact
         uses: actions/[email protected]
         with:
           name: win${{env.ARCH}}Binaries
           path: out
-      
+
 
   windows-build:
     runs-on: windows-latest
@@ -138,23 +122,7 @@ jobs:
         with:
           submodules: recursive
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
-        run: |
-          set -ex
-      
-          curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
-          tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
-          NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
-          sudo mkdir -p /usr/local/bin
-          sudo mkdir -p /usr/local/lib/neko
-          sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools}  /usr/local/bin/
-          sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
-          sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
-          echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         shell: pwsh
         run: |
           Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
@@ -162,10 +130,10 @@ jobs:
           $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
           echo "$NEKOPATH" >> $env:GITHUB_PATH
           echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       - name: choco install nsis
         uses: nick-invision/retry@v1
         with:
@@ -178,7 +146,7 @@ jobs:
       - name: Prepend Chocolatey path
         shell: pwsh
         run: Write-Host "::add-path::C:\ProgramData\chocolatey\bin"
-      
+
       - name: Install OCaml and OCaml libraries
         shell: pwsh
         run: |
@@ -200,16 +168,16 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
-      
+
       - name: Expose mingw dll files
         shell: pwsh
         run: Write-Host "::add-path::${env:CYG_ROOT}/usr/$($env:MINGW_ARCH)-w64-mingw32/sys-root/mingw/bin"
-      
+
       - name: Set ADD_REVISION=1 for non-release
         if: ${{ !startsWith(github.ref, 'refs/tags/') }}
         shell: pwsh
         run: echo "ADD_REVISION=1" >> $Env:GITHUB_ENV
-      
+
       - name: Build Haxe
         shell: pwsh
         run: |
@@ -221,13 +189,13 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxe.exe')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxelib.exe')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && ls ./out')
-      
+
       - name: Upload artifact
         uses: actions/[email protected]
         with:
           name: win${{env.ARCH}}Binaries
           path: out
-      
+
 
   linux-amd64:
     runs-on: ubuntu-18.04
@@ -333,11 +301,10 @@ jobs:
         with:
           submodules: recursive
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         run: |
           set -ex
-      
+
           curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
           tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
           NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
@@ -347,20 +314,10 @@ jobs:
           sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
           sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
           echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
-        shell: pwsh
-        run: |
-          Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
-          Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP
-          $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
-          echo "$NEKOPATH" >> $env:GITHUB_PATH
-          echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       - name: Install dependencies
         env:
           ZLIB_VERSION: 1.2.11
@@ -389,8 +346,8 @@ jobs:
           cd pcre2-$PCRE_VERSION
           ./configure --enable-utf8 --enable-pcre2-8 --enable-pcre2-16 --enable-pcre2-32 --enable-unicode-properties --enable-pcregrep-libz --enable-pcregrep-libbz2 --enable-jit
           make && make install
-      
-      
+
+
       - name: Install OCaml libraries
         run: |
           set -ex
@@ -404,11 +361,11 @@ jobs:
           opam install haxe --deps-only --assume-depexts
           opam list
           ocamlopt -v
-      
+
       - name: Set ADD_REVISION=1 for non-release
         if: ${{ !startsWith(github.ref, 'refs/tags/') }}
         run: echo "ADD_REVISION=1" >> $GITHUB_ENV
-      
+
       - name: Build Haxe
         run: |
           set -ex
@@ -419,13 +376,13 @@ jobs:
           ls -l out
           otool -L ./haxe
           otool -L ./haxelib
-      
+
       - name: Upload artifact
         uses: actions/[email protected]
         with:
           name: macBinaries
           path: out
-      
+
 
   windows64-test:
     needs: windows64-build
@@ -449,23 +406,7 @@ jobs:
         with:
           name: win${{env.ARCH}}Binaries
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
-        run: |
-          set -ex
-      
-          curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
-          tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
-          NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
-          sudo mkdir -p /usr/local/bin
-          sudo mkdir -p /usr/local/lib/neko
-          sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools}  /usr/local/bin/
-          sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
-          sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
-          echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         shell: pwsh
         run: |
           Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
@@ -473,17 +414,17 @@ jobs:
           $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
           echo "$NEKOPATH" >> $env:GITHUB_PATH
           echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       # - name: Quick test
       #   shell: pwsh
       #   run: |
       #     $DOWNLOADDIR="./win$($env:ARCH)Binaries"
       #     new-item -Name $DOWNLOADDIR -ItemType directory
       #     Invoke-WebRequest https://build.haxe.org/builds/haxe/$env:PLATFORM/haxe_latest.zip -OutFile $DOWNLOADDIR/haxe_bin.zip
-      
+
       - name: Setup Haxe
         shell: pwsh
         run: |
@@ -493,11 +434,11 @@ jobs:
           $HAXEPATH = Get-ChildItem $DOWNLOADDIR/haxe_*_* -Directory
           Write-Host "::add-path::$HAXEPATH"
           Write-Host "::set-env name=HAXELIB_ROOT::$HAXEPATH\lib"
-      
+
       - name: Print Haxe version
         shell: pwsh
         run: haxe -version
-      
+
       - name: "Make Python 3 be available as python3 in the cmdline"
         shell: pwsh
         run: |
@@ -506,7 +447,7 @@ jobs:
           $py3path = $pypath.replace("python.exe","python3.exe")
           cmd /c mklink $py3path $pypath
           python3 -V
-      
+
       - name: Install hererocks
         if: matrix.target == 'lua'
         shell: cmd
@@ -514,18 +455,18 @@ jobs:
           pip install hererocks
           hererocks lua53 -l5.3 -rlatest
           call lua53/bin/activate
-      
+
       - name: Setup haxelib
         shell: pwsh
         run: |
           mkdir "$env:HAXELIB_ROOT"
           haxelib setup "$env:HAXELIB_ROOT"
-      
+
       - name: Test
         shell: pwsh
         run: haxe RunCi.hxml
         working-directory: ${{github.workspace}}/tests
-      
+
 
   windows-test:
     needs: windows-build
@@ -550,23 +491,7 @@ jobs:
         with:
           name: win${{env.ARCH}}Binaries
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
-        run: |
-          set -ex
-      
-          curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
-          tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
-          NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
-          sudo mkdir -p /usr/local/bin
-          sudo mkdir -p /usr/local/lib/neko
-          sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools}  /usr/local/bin/
-          sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
-          sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
-          echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         shell: pwsh
         run: |
           Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
@@ -574,17 +499,17 @@ jobs:
           $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
           echo "$NEKOPATH" >> $env:GITHUB_PATH
           echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       # - name: Quick test
       #   shell: pwsh
       #   run: |
       #     $DOWNLOADDIR="./win$($env:ARCH)Binaries"
       #     new-item -Name $DOWNLOADDIR -ItemType directory
       #     Invoke-WebRequest https://build.haxe.org/builds/haxe/$env:PLATFORM/haxe_latest.zip -OutFile $DOWNLOADDIR/haxe_bin.zip
-      
+
       - name: Setup Haxe
         shell: pwsh
         run: |
@@ -594,11 +519,11 @@ jobs:
           $HAXEPATH = Get-ChildItem $DOWNLOADDIR/haxe_*_* -Directory
           Write-Host "::add-path::$HAXEPATH"
           Write-Host "::set-env name=HAXELIB_ROOT::$HAXEPATH\lib"
-      
+
       - name: Print Haxe version
         shell: pwsh
         run: haxe -version
-      
+
       - name: "Make Python 3 be available as python3 in the cmdline"
         shell: pwsh
         run: |
@@ -607,7 +532,7 @@ jobs:
           $py3path = $pypath.replace("python.exe","python3.exe")
           cmd /c mklink $py3path $pypath
           python3 -V
-      
+
       - name: Install hererocks
         if: matrix.target == 'lua'
         shell: cmd
@@ -615,18 +540,18 @@ jobs:
           pip install hererocks
           hererocks lua53 -l5.3 -rlatest
           call lua53/bin/activate
-      
+
       - name: Setup haxelib
         shell: pwsh
         run: |
           mkdir "$env:HAXELIB_ROOT"
           haxelib setup "$env:HAXELIB_ROOT"
-      
+
       - name: Test
         shell: pwsh
         run: haxe RunCi.hxml
         working-directory: ${{github.workspace}}/tests
-      
+
 
   mac-test:
     needs: mac-build
@@ -650,11 +575,10 @@ jobs:
         with:
           name: macBinaries
 
-      - name: Install Neko using snapshot from S3 (Unix)
-        if: ${{ !startsWith(env.PLATFORM, 'windows') }}
+      - name: Install Neko from S3
         run: |
           set -ex
-      
+
           curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz
           tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP
           NEKOPATH=`echo $RUNNER_TEMP/neko-*-*`
@@ -664,25 +588,15 @@ jobs:
           sudo ln -s $NEKOPATH/libneko.*                      /usr/local/lib/
           sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
           echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
-      
-      - name: Install Neko using snapshot from S3 (Windows)
-        if: ${{ startsWith(env.PLATFORM, 'windows') }}
-        shell: pwsh
-        run: |
-          Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
-          Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP
-          $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
-          echo "$NEKOPATH" >> $env:GITHUB_PATH
-          echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-      
+
       - name: Print Neko version
         run: neko -version 2>&1
-      
+
       - name: Setup Haxe
         run: |
           # mkdir ./macBinaries
           # curl -sSL https://build.haxe.org/builds/haxe/mac/haxe_latest.tar.gz -o ./macBinaries/haxe_bin.tar.gz
-      
+
           set -ex
           tar -xf macBinaries/*_bin.tar.gz -C macBinaries --strip-components=1
           sudo mkdir -p /usr/local/bin/
@@ -690,27 +604,27 @@ jobs:
           sudo ln -s `pwd`/macBinaries/haxe /usr/local/bin/haxe
           sudo ln -s `pwd`/macBinaries/haxelib /usr/local/bin/haxelib
           sudo ln -s `pwd`/macBinaries/std /usr/local/share/haxe/std
-      
+
       - name: Print Haxe version
         run: haxe -version
-      
+
       - name: Setup haxelib
         run: |
           set -ex
           mkdir ~/haxelib
           haxelib setup ~/haxelib
-      
+
       - name: Install homebrew packages
         if: matrix.BREW_PACKAGES
         run: brew install ${{matrix.BREW_PACKAGES}}
-      
+
       - name: Test
         run: |
           # disable invalid Unicode filenames on APFS
           echo "" > sys/compile-fs.hxml
           haxe RunCi.hxml
         working-directory: ${{github.workspace}}/tests
-      
+
 
   deploy:
     if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request'

+ 2 - 1
extra/github-actions/Main.hx

@@ -24,7 +24,8 @@ class Main {
 				final path = reg.matched(2);
 				final template = File.getContent('./$path');
 				final lines = template.split("\n");
-				for (i in 0...lines.length) lines[i] = spaces + lines[i];
+				for (i in 0...lines.length)
+					lines[i] = (spaces + lines[i]).rtrim();
 				lines.join("\n");
 			});
 

+ 1 - 12
extra/github-actions/install-neko.yml → extra/github-actions/install-neko-unix.yml

@@ -1,5 +1,4 @@
-- name: Install Neko using snapshot from S3 (Unix)
-  if: ${{ !startsWith(env.PLATFORM, 'windows') }}
+- name: Install Neko from S3
   run: |
     set -ex
 
@@ -13,15 +12,5 @@
     sudo ln -s $NEKOPATH/*.ndll                         /usr/local/lib/neko/
     echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV
 
-- name: Install Neko using snapshot from S3 (Windows)
-  if: ${{ startsWith(env.PLATFORM, 'windows') }}
-  shell: pwsh
-  run: |
-    Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
-    Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP
-    $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
-    echo "$NEKOPATH" >> $env:GITHUB_PATH
-    echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
-
 - name: Print Neko version
   run: neko -version 2>&1

+ 11 - 0
extra/github-actions/install-neko-windows.yml

@@ -0,0 +1,11 @@
+- name: Install Neko from S3
+  shell: pwsh
+  run: |
+    Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip
+    Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP
+    $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-*
+    echo "$NEKOPATH" >> $env:GITHUB_PATH
+    echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV
+
+- name: Print Neko version
+  run: neko -version 2>&1

+ 6 - 6
extra/github-actions/workflows/main.yml

@@ -30,7 +30,7 @@ jobs:
         with:
           submodules: recursive
 
-      @import install-neko.yml
+      @import install-neko-windows.yml
       @import build-windows.yml
 
   windows-build:
@@ -49,7 +49,7 @@ jobs:
         with:
           submodules: recursive
 
-      @import install-neko.yml
+      @import install-neko-windows.yml
       @import build-windows.yml
 
   linux-amd64:
@@ -156,7 +156,7 @@ jobs:
         with:
           submodules: recursive
 
-      @import install-neko.yml
+      @import install-neko-unix.yml
       @import build-mac.yml
 
   windows64-test:
@@ -181,7 +181,7 @@ jobs:
         with:
           name: win${{env.ARCH}}Binaries
 
-      @import install-neko.yml
+      @import install-neko-windows.yml
       @import test-windows.yml
 
   windows-test:
@@ -207,7 +207,7 @@ jobs:
         with:
           name: win${{env.ARCH}}Binaries
 
-      @import install-neko.yml
+      @import install-neko-windows.yml
       @import test-windows.yml
 
   mac-test:
@@ -232,7 +232,7 @@ jobs:
         with:
           name: macBinaries
 
-      @import install-neko.yml
+      @import install-neko-unix.yml
       @import test-mac.yml
 
   deploy:

+ 3 - 4
tests/README.md

@@ -4,14 +4,13 @@ We have a number of test suites, which are placed in their own folders in this d
 
 "RunCi.hx" is the script used by our CIs to run all the test suites.
 
-### Local testing
+## Local testing
 
 It is possible to run it in local machines too:
 
  1. Change to this directory.
- 2. Compile the script: `haxe RunCi.hxml`.
- 3. Define the test target by `export TEST=$TARGET` (or `set "TEST=$TARGET"` on Windows), where `$TARGET` should be a comma-seperated list of targets, e.g. `neko,macro`. Possible targets are `macro`, `neko`, `js`, `lua`, `php`, `cpp`, `flash9`, `java`, `cs`, `python`, and `third-party`. However, `flash9` and `third-party` are not likely to work on local machines (TODO).
- 4. Run it: `neko RunCi.n`.
+ 2. Define the test target by `export TEST=$TARGET` (or `set "TEST=$TARGET"` on Windows), where `$TARGET` should be a comma-seperated list of targets, e.g. `neko,macro`. Possible targets are `macro`, `neko`, `js`, `lua`, `php`, `cpp`, `cppia`, `flash9`, `java`, `jvm`, `cs`, `python`, and `hl`.  However, `flash9` is not likely to work on local machines (TODO).
+ 3. Run the script: `haxe RunCi.hxml`.
 
 Note that the script will try to look for test dependencies and install them if they are not found. Look at the `getXXXDependencies` functions for the details.
 

+ 9 - 14
tests/RunCi.hx

@@ -27,6 +27,10 @@ class RunCi {
 			changeDirectory(cwd);
 		}
 
+		final downloadPath = getDownloadPath();
+		if (!sys.FileSystem.exists(downloadPath))
+			sys.FileSystem.createDirectory(downloadPath);
+
 		for (test in tests) {
 			switch (systemName) {
 				case "Windows":
@@ -40,7 +44,6 @@ class RunCi {
 			var echoServer = new sys.io.Process('nekotools', ['server', '-d', 'echoServer/www/', '-p', '20200']);
 
 			infoMsg('test $test');
-			var success = true;
 			try {
 				changeDirectory(unitDir);
 				haxelibInstallGit("haxe-utest", "utest", "master");
@@ -82,25 +85,17 @@ class RunCi {
 					case t:
 						throw new Exception("unknown target: " + t);
 				}
-			} catch(f:Failure) {
-				success = false;
-			}
-
-			if (success) {
-				successMsg('test ${test} succeeded');
-			} else {
+			} catch(f:CommandFailure) {
 				failMsg('test ${test} failed');
-				break;
+				Sys.exit(f.exitCode);
 			}
 
+			successMsg('test ${test} succeeded');
+
 			echoServer.kill();
 			echoServer.close();
 		}
 
-		if (success) {
-			deploy();
-		} else {
-			Sys.exit(1);
-		}
+		deploy();
 	}
 }

+ 13 - 10
tests/runci/Config.hx

@@ -1,6 +1,7 @@
 package runci;
 
 import sys.FileSystem;
+import haxe.io.Path;
 
 enum Ci {
 	GithubActions;
@@ -10,15 +11,17 @@ class Config {
 	static public final systemName = Sys.systemName();
 	static public final cwd = Sys.getCwd();
 	static public final repoDir = FileSystem.fullPath("..") + "/";
-	static public final unitDir = cwd + "unit/";
-	static public final sysDir = cwd + "sys/";
+	static public final unitDir = Path.join([cwd, "unit"]);
+	static public final sysDir = Path.join([cwd, "sys"]);
 	static public final optDir = cwd + "optimization/";
-	static public final miscDir = cwd + "misc/";
-	static public final displayDir = cwd + "display/";
-	static public final serverDir = cwd + "server/";
-	static public final sourcemapsDir = cwd + "sourcemaps/";
-	static public final nullSafetyDir = cwd + "nullsafety/";
-	static public final threadsDir = cwd + "threads/";
+	static public final displayDir = Path.join([cwd, "display"]);
+	static public final serverDir = Path.join([cwd, "server"]);
+	static public final sourcemapsDir = Path.join([cwd, "sourcemaps"]);
+	static public final nullSafetyDir = Path.join([cwd, "nullsafety"]);
+	static public final threadsDir = Path.join([cwd, "threads"]);
+
+	static public function getMiscSubDir(...subDir:String)
+		return Path.join([cwd, "misc"].concat(subDir.toArray()));
 
 	static public final ci:Null<Ci> =
 		if (Sys.getEnv("GITHUB_ACTIONS") == "true")
@@ -26,8 +29,8 @@ class Config {
 		else
 			null;
 
-	static public function isCi():Bool {
-		return ci != null;
+	static public macro function isCi() {
+		return macro $v{ci != null};
 	}
 
 	static public final colorSupported = switch [ci, systemName] {

+ 14 - 5
tests/runci/Linux.hx

@@ -6,7 +6,7 @@ using StringTools;
 
 class Linux {
 	static public var arch(get, null):Arch;
-	
+
 	static function get_arch() {
 		if(arch == null)
 			arch = switch commandResult('arch', []).stdout.replace('\n', '') {
@@ -15,12 +15,21 @@ class Linux {
 			}
 		return arch;
 	}
-	
+
 	static public function isAptPackageInstalled(aptPackage:String):Bool {
 		return commandSucceed("dpkg-query", ["-W", "-f='${Status}'", aptPackage]);
 	}
-	
+
+	static inline function hasAptGet() {
+		// CI always runs on ubuntu, otherwise check for apt-get
+		return Config.isCi() || commandSucceed("type", ["apt-get"]);
+	}
+
 	static public function requireAptPackages(packages:Array<String>):Void {
+		if (!hasAptGet()){
+			infoMsg("System does not have apt-get installed.");
+			return;
+		}
 		var notYetInstalled = [for (p in packages) if (!isAptPackageInstalled(p)) p];
 		if (notYetInstalled.length > 0) {
 			var aptCacheDir = Sys.getEnv("APT_CACHE_DIR");
@@ -29,7 +38,7 @@ class Linux {
 			} else {
 				["apt-get", "install", "-qqy"];
 			};
-			runCommand("sudo", baseCommand.concat(notYetInstalled), true);
+			runNetworkCommand("sudo", baseCommand.concat(notYetInstalled));
 		}
 	}
 }
@@ -37,4 +46,4 @@ class Linux {
 enum abstract Arch(Int) {
 	final Arm64;
 	final Amd64;
-}
+}

+ 102 - 76
tests/runci/System.hx

@@ -6,14 +6,15 @@ import runci.Config.*;
 
 using StringTools;
 
-enum Failure {
-	Fail;
+class CommandFailure extends haxe.Exception {
+	public final exitCode:Int;
+	public function new(exitCode:Int = 1) {
+		super("Command failed to run: " + Std.string(exitCode));
+		this.exitCode = exitCode;
+	}
 }
 
 class System {
-	static public var success(default, null) = true;
-
-
 	static public function successMsg(msg:String):Void {
 		Sys.println(colorSupported ? '\x1b[32m' + msg + '\x1b[0m' : msg);
 	}
@@ -26,8 +27,8 @@ class System {
 
 	static public function commandSucceed(cmd:String, args:Array<String>):Bool {
 		return try {
-			var p = new Process(cmd, args);
-			var succeed = p.exitCode() == 0;
+			final p = new Process(cmd, args);
+			final succeed = p.exitCode() == 0;
 			p.close();
 			succeed;
 		} catch(e:Dynamic) false;
@@ -38,8 +39,8 @@ class System {
 		stderr:String,
 		exitCode:Int
 	} {
-		var p = new Process(cmd, args);
-		var out = {
+		final p = new Process(cmd, args);
+		final out = {
 			stdout: p.stdout.readAll().toString(),
 			stderr: p.stderr.readAll().toString(),
 			exitCode: p.exitCode()
@@ -48,42 +49,30 @@ class System {
 		return out;
 	}
 
+	static inline function getDisplayCmd(cmd:String, ?args:Array<String>) {
+		return cmd + (args == null ? '' : ' $args');
+	}
+
 	/**
 		Run a command using `Sys.command()`.
-		If the command exits with non-zero code, exit the whole script with the same code.
-
-		If `useRetry` is `true`, the command will be re-run if it exits with non-zero code (3 trials).
-		It is useful for running network-dependent commands.
+		If the command exits with non-zero code, throws `CommandFailure` with the same code.
 	*/
-	static public function runCommand(cmd:String, ?args:Array<String>, useRetry:Bool = false, allowFailure:Bool = false):Void {
-		var trials = useRetry ? 3 : 1;
-		var exitCode:Int = 1;
-		var cmdStr = cmd + (args == null ? '' : ' $args');
+	static public function runCommand(cmd:String, ?args:Array<String>):Void {
+		final exitCode = showAndRunCommand(cmd, args);
 
-		while (trials-->0) {
-			infoMsg('Command: $cmdStr');
-
-			var t = Timer.stamp();
-			exitCode = Sys.command(cmd, args);
-			var dt = Math.round(Timer.stamp() - t);
-
-			if (exitCode == 0) {
-				successMsg('Command exited with $exitCode in ${dt}s: $cmdStr');
-				return;
-			}
-			else
-				failMsg('Command exited with $exitCode in ${dt}s: $cmdStr');
-
-			if (trials > 0) {
-				infoMsg('Command will be re-run...');
-			}
-		}
+		if (exitCode != 0)
+			throw new CommandFailure(exitCode);
+	}
 
-		if (!allowFailure)
-			fail();
+	/** Runs command using `Sys.command()` but ignores failures. **/
+	static public function attemptCommand(cmd:String, ?args:Array<String>) {
+		showAndRunCommand(cmd, args);
 	}
 
-	static function showAndRunCommand(cmd:String, args:Null<Array<String>>, displayed:String):Int {
+	static function showAndRunCommand(cmd:String, args:Null<Array<String>>, ?displayed:String):Int {
+		if (displayed == null)
+			displayed = getDisplayCmd(cmd, args);
+
 		infoMsg('Command: $displayed');
 
 		final t = Timer.stamp();
@@ -99,6 +88,28 @@ class System {
 		return exitCode;
 	}
 
+
+	static final TRIALS = 3;
+	/**
+		Run a command using `Sys.command()` with up to three attempts. Useful for running network-dependent commands.
+
+		If all three attempts fail, throws a `CommandFailure` with the exit code of the last run.
+	**/
+	static public function runNetworkCommand(cmd:String, ?args:Array<String>) {
+		final cmdStr = getDisplayCmd(cmd, args);
+
+		for (trial in 1...TRIALS+1){
+			final exitCode = showAndRunCommand(cmd, args, cmdStr);
+			if (exitCode == 0)
+				return;
+
+			if (trial == TRIALS)
+				throw new CommandFailure(exitCode);
+
+			infoMsg('Command will be re-run...');
+		}
+	}
+
 	/**
 	 * Recursively delete a directory.
 	 * @return Int Exit code of a system command executed to perform deletion.
@@ -112,11 +123,6 @@ class System {
 		}
 	}
 
-	static public function fail():Void {
-		success = false;
-		throw Fail;
-	}
-
 	static public function addToPATH(path:String):Void {
 		infoMsg('Prepending $path to PATH.');
 		switch (systemName) {
@@ -127,50 +133,54 @@ class System {
 		}
 	}
 
-	static public function haxelibInstallGit(account:String, repository:String, ?branch:String, ?srcPath:String, useRetry:Bool = false, ?altName:String):Void {
-		var name:String = (altName == null) ? repository : altName;
-		try {
-			getHaxelibPath(name);
-			infoMsg('$name has already been installed.');
-		} catch (e:Dynamic) {
-			var args:Array<String> = ["git", name, 'https://github.com/$account/$repository'];
-			if (branch != null) {
-				args.push(branch);
-			}
-			if (srcPath != null) {
-				args.push(srcPath);
-			}
+	static function isLibraryInstalled(library:String):Bool {
+		return new Process("haxelib", ["path", library]).exitCode() == 0;
+	}
 
-			runCommand("haxelib", args, useRetry);
-		}
+	static public function haxelibInstallGit(account:String, repository:String, ?branch:String, ?srcPath:String, useRetry:Bool = false, ?altName:String):Void {
+		final name = (altName == null) ? repository : altName;
+		// It could only be installed already if we're on a local machine
+		if (!isCi() && isLibraryInstalled(name))
+			return infoMsg('$name has already been installed.');
+
+		final args = ["git", name, 'https://github.com/$account/$repository'];
+		if (branch != null)
+			args.push(branch);
+		if (srcPath != null)
+			args.push(srcPath);
+
+		if (useRetry)
+			runNetworkCommand("haxelib", args);
+		else
+			runCommand("haxelib", args);
 	}
 
 	static public function haxelibInstall(library:String):Void {
-		try {
-			getHaxelibPath(library);
-			infoMsg('$library has already been installed.');
-		} catch (e:Dynamic) {
-			runCommand("haxelib", ["install", library]);
-		}
+		// It could only be installed already if we're on a local machine
+		if (!isCi() && isLibraryInstalled(library))
+			return infoMsg('$library has already been installed.');
+		runCommand("haxelib", ["install", library]);
 	}
 
 	static public function haxelibDev(library:String, path:String):Void {
-		try {
-			getHaxelibPath(library);
-			infoMsg('$library has already been installed.');
-		} catch (e:Dynamic) {
-			runCommand("haxelib", ["dev", library, path]);
-		}
+		// It could only be installed already if we're on a local machine
+		if (!isCi() && isLibraryInstalled(library))
+			return infoMsg('$library has already been installed.');
+		runCommand("haxelib", ["dev", library, path]);
 	}
 
 	static public function haxelibRun(args:Array<String>, useRetry:Bool = false):Void {
-		runCommand("haxelib", ["run"].concat(args), useRetry);
+		final allArgs = ["run"].concat(args);
+		if (useRetry)
+			runNetworkCommand("haxelib", allArgs);
+		else
+			runCommand("haxelib", allArgs);
 	}
 
 	static public function getHaxelibPath(libName:String) {
-		var proc = new Process("haxelib", ["path", libName]);
+		final proc = new Process("haxelib", ["path", libName]);
+		final code = proc.exitCode();
 		var result;
-		var code = proc.exitCode();
 		do {
 			result = proc.stdout.readLine();
 			if (!result.startsWith("-L")) {
@@ -204,16 +214,32 @@ class System {
 
 	/** Prepares environment for system tests and runs `cmd` with `args` **/
 	static public function runSysTest(cmd:String, ?args:Array<String>) {
-		final cmdStr = '$setCommand [$nameAndValue] && ' + cmd + (args == null ? '' : ' $args');
+		final toDisplay = getDisplayCmd(setCommand, [nameAndValue]) + ' && ' + getDisplayCmd(cmd, args);
 
 		if (args != null)
 			cmd = mergeArgs(cmd, args);
 
 		final fullCmd = '$setCommand $nameAndValue && $cmd';
 
-		final exitCode = showAndRunCommand(fullCmd, null, cmdStr);
+		final exitCode = showAndRunCommand(fullCmd, null, toDisplay);
 
 		if (exitCode != 0)
-			fail();
+			throw new CommandFailure(exitCode);
 	}
+
+	static final installPath = if (systemName == "Windows")
+			Sys.getEnv("USERPROFILE") + "/haxe-ci";
+		else
+			Sys.getEnv("HOME") + "/haxe-ci";
+
+	/** Returns path where packages should be installed. **/
+	public static inline function getInstallPath():String {
+		return installPath;
+	}
+
+	/** Returns path where downloads should be placed. **/
+	public static inline function getDownloadPath():String {
+		return installPath + "/downloads";
+	}
+
 }

+ 0 - 1
tests/runci/TestTarget.hx

@@ -6,7 +6,6 @@ enum abstract TestTarget(String) from String {
 	var Js = "js";
 	var Lua = "lua";
 	var Php = "php";
-	var Php7 = "php7";
 	var Cpp = "cpp";
 	var Cppia = "cppia";
 	var Flash9 = "flash9";

+ 11 - 9
tests/runci/targets/Cpp.hx

@@ -6,7 +6,7 @@ import runci.Config.*;
 
 class Cpp {
 	static public var gotCppDependencies = false;
-	static final miscCppDir = miscDir + 'cpp/';
+	static final miscCppDir = getMiscSubDir('cpp');
 
 	static public function getCppDependencies() {
 		if (gotCppDependencies) return;
@@ -25,11 +25,11 @@ class Cpp {
 
 		//install and build hxcpp
 		try {
-			var path = getHaxelibPath("hxcpp");
+			final path = getHaxelibPath("hxcpp");
 			infoMsg('hxcpp has already been installed in $path.');
 		} catch(e:Dynamic) {
 			haxelibInstallGit("HaxeFoundation", "hxcpp", true);
-			var oldDir = Sys.getCwd();
+			final oldDir = Sys.getCwd();
 			changeDirectory(getHaxelibPath("hxcpp") + "tools/hxcpp/");
 			runCommand("haxe", ["-D", "source-header=''", "compile.hxml"]);
 			changeDirectory(oldDir);
@@ -47,7 +47,9 @@ class Cpp {
 	static public function run(args:Array<String>, testCompiled:Bool, testCppia:Bool) {
 		getCppDependencies();
 
-		var archFlag = switch systemName {
+		final isLinuxArm64 = systemName == 'Linux' && Linux.arch == Arm64;
+
+		final archFlag = switch systemName {
 			case 'Windows':
 				'HXCPP_M32';
 			case 'Linux' if(Linux.arch == Arm64):
@@ -55,7 +57,7 @@ class Cpp {
 			case _:
 				'HXCPP_M64';
 		}
-				
+
 		if (testCompiled) {
 			runCommand("rm", ["-rf", "cpp"]);
 			runCommand("haxe", ["compile-cpp.hxml", "-D", archFlag].concat(args));
@@ -66,8 +68,8 @@ class Cpp {
 			runCommand("haxe", ["compile-cppia-host.hxml", "-D", archFlag].concat(args));
 			runCommand("haxe", ["compile-cppia.hxml"].concat(args));
 			runCpp("bin/cppia/Host-debug", ["bin/unit.cppia"]);
-			
-			if(systemName != 'Linux' && Linux.arch != Arm64) // FIXME
+
+			if (!isLinuxArm64) // FIXME
 				runCpp("bin/cppia/Host-debug", ["bin/unit.cppia", "-jit"]);
 		}
 
@@ -75,7 +77,7 @@ class Cpp {
 		runCommand("haxe", ["-D", archFlag, "--each", "compile-cpp.hxml"].concat(args));
 		runSysTest(FileSystem.fullPath("bin/cpp/Main-debug"));
 
-		if(systemName != 'Linux' && Linux.arch != Arm64) { // FIXME 
+		if (!isLinuxArm64) { // FIXME
 			changeDirectory(threadsDir);
 			runCommand("haxe", ["-D", archFlag, "build.hxml", "-cpp", "export/cpp"]);
 			runCpp("export/cpp/Main");
@@ -83,7 +85,7 @@ class Cpp {
 
 		// if (Sys.systemName() == "Mac")
 		// {
-		// 	changeDirectory(miscDir + "cppObjc");
+		// 	changeDirectory(getMiscSubDir("cppObjc"));
 		// 	runCommand("haxe", ["build.hxml"]);
 		// 	runCpp("bin/TestObjc-debug");
 		// }

+ 6 - 7
tests/runci/targets/Cs.hx

@@ -5,8 +5,7 @@ import runci.System.*;
 import runci.Config.*;
 
 class Cs {
-	static var miscCsDir(get,never):String;
-	static inline function get_miscCsDir() return miscDir + 'cs/';
+	static final miscCsDir = getMiscSubDir('cs');
 
 	static public function getCsDependencies() {
 		switch (systemName) {
@@ -20,7 +19,7 @@ class Cs {
 				if (commandSucceed("mono", ["--version"]))
 					infoMsg('mono has already been installed.');
 				else
-					runCommand("brew", ["install", "mono"], true);
+					runNetworkCommand("brew", ["install", "mono"]);
 				runCommand("mono", ["--version"]);
 			case "Windows":
 				//pass
@@ -47,7 +46,7 @@ class Cs {
 		for (noroot in        [[], ["-D", "no_root"]])
 		for (erasegenerics in [[], ["-D", "erase_generics"]])
 		{
-			var extras = fastcast.concat(erasegenerics).concat(noroot);
+			final extras = fastcast.concat(erasegenerics).concat(noroot);
 			runCommand("haxe", ['compile-cs.hxml'].concat(extras).concat(args));
 			runCs("bin/cs/bin/TestMain-Debug.exe");
 
@@ -62,10 +61,10 @@ class Cs {
 		runCommand("haxe", ["compile-cs.hxml",'-D','fast_cast'].concat(args));
 		final exe = FileSystem.fullPath("bin/cs/bin/Main-Debug.exe");
 		switch (systemName) {
-			case "Linux" | "Mac":
-				runSysTest("mono", [exe]);
 			case "Windows":
 				runSysTest(exe);
+			case _:
+				runSysTest("mono", [exe]);
 		}
 
 		changeDirectory(threadsDir);
@@ -75,7 +74,7 @@ class Cs {
 		changeDirectory(miscCsDir);
 		runCommand("haxe", ["run.hxml"]);
 
-		changeDirectory(miscCsDir + "csTwoLibs");
+		changeDirectory(getMiscSubDir("cs", "csTwoLibs"));
 		for (i in 1...5)
 		{
 			runCommand("haxe", ['compile-$i.hxml','-D','fast_cast']);

+ 20 - 20
tests/runci/targets/Flash.hx

@@ -1,22 +1,22 @@
 package runci.targets;
 
-import sys.io.File;
 import sys.FileSystem;
-import haxe.io.Path;
-import haxe.Json;
+import sys.io.File;
 import sys.io.Process;
+import haxe.Json;
 import haxe.Http;
+import haxe.io.Path;
 
 import runci.System.*;
+import runci.System.CommandFailure;
 import runci.Config.*;
 
 class Flash {
-	static var miscFlashDir(get,never):String;
-	static inline function get_miscFlashDir() return miscDir + 'flash/';
+	static final miscFlashDir = getMiscSubDir('flash');
 
-	static public function getLatestFPVersion():Array<Int> {
-		var appcast = Xml.parse(haxe.Http.requestUrl("http://fpdownload2.macromedia.com/get/flashplayer/update/current/xml/version_en_mac_pep.xml"));
-		var versionStr = new haxe.xml.Access(appcast).node.XML.node.update.att.version;
+	static function getLatestFPVersion():Array<Int> {
+		final appcast = Xml.parse(haxe.Http.requestUrl("http://fpdownload2.macromedia.com/get/flashplayer/update/current/xml/version_en_mac_pep.xml"));
+		final versionStr = new haxe.xml.Access(appcast).node.XML.node.update.att.version;
 		return versionStr.split(",").map(Std.parseInt);
 	}
 
@@ -26,14 +26,14 @@ class Flash {
 		} else {
 			var apacheMirror = Json.parse(Http.requestUrl("http://www.apache.org/dyn/closer.lua?as_json=1")).preferred;
 			var flexVersion = "4.16.0";
-			runCommand("wget", ["-nv", '${apacheMirror}/flex/${flexVersion}/binaries/apache-flex-sdk-${flexVersion}-bin.tar.gz'], true);
-			runCommand("tar", ["-xf", 'apache-flex-sdk-${flexVersion}-bin.tar.gz', "-C", Sys.getEnv("HOME")]);
-			var flexsdkPath = Sys.getEnv("HOME") + '/apache-flex-sdk-${flexVersion}-bin';
+			runNetworkCommand("wget", ["-nv", '${apacheMirror}/flex/${flexVersion}/binaries/apache-flex-sdk-${flexVersion}-bin.tar.gz']);
+			runCommand("tar", ["-xf", 'apache-flex-sdk-${flexVersion}-bin.tar.gz', "-C", getDownloadPath()]);
+			var flexsdkPath = getDownloadPath() + '/apache-flex-sdk-${flexVersion}-bin';
 			addToPATH(flexsdkPath + "/bin");
 			var playerglobalswcFolder = flexsdkPath + "/player";
 			FileSystem.createDirectory(playerglobalswcFolder + "/11.1");
 			var flashVersion = runci.targets.Flash.getLatestFPVersion();
-			runCommand("wget", ["-nv", 'http://download.macromedia.com/get/flashplayer/updaters/${flashVersion[0]}/playerglobal${flashVersion[0]}_${flashVersion[1]}.swc', "-O", playerglobalswcFolder + "/11.1/playerglobal.swc"], true);
+			runNetworkCommand("wget", ["-nv", 'http://download.macromedia.com/get/flashplayer/updaters/${flashVersion[0]}/playerglobal${flashVersion[0]}_${flashVersion[1]}.swc', "-O", playerglobalswcFolder + "/11.1/playerglobal.swc"]);
 			File.saveContent(flexsdkPath + "/env.properties", 'env.PLAYERGLOBAL_HOME=$playerglobalswcFolder');
 			runCommand("mxmlc", ["--version"]);
 		}
@@ -59,9 +59,9 @@ class Flash {
 						"libglib2.0-0", "libfreetype6"
 					]);
 					var majorVersion = getLatestFPVersion()[0];
-					runCommand("wget", ["-nv", 'http://fpdownload.macromedia.com/pub/flashplayer/updaters/${majorVersion}/flash_player_sa_linux_debug.x86_64.tar.gz'], true);
-					runCommand("tar", ["-xf", "flash_player_sa_linux_debug.x86_64.tar.gz", "-C", Sys.getEnv("HOME")]);
-					playerCmd = Path.join([Sys.getEnv("HOME"), "flashplayerdebugger"]);
+					runNetworkCommand("wget", ["-nv", 'http://fpdownload.macromedia.com/pub/flashplayer/updaters/${majorVersion}/flash_player_sa_linux_debug.x86_64.tar.gz']);
+					runCommand("tar", ["-xf", "flash_player_sa_linux_debug.x86_64.tar.gz", "-C", getDownloadPath()]);
+					playerCmd = Path.join([getDownloadPath(), "flashplayerdebugger"]);
 				}
 				if (!FileSystem.exists(mmcfgPath)) {
 					File.saveContent(mmcfgPath, "ErrorReportingEnable=1\nTraceOutputFileEnable=1");
@@ -76,10 +76,10 @@ class Flash {
 				if (commandResult("brew", ["cask", "list", "flash-player-debugger"]).exitCode == 0) {
 					return;
 				}
-				runCommand("brew", ["uninstall", "[email protected]"], false, true);
-				runCommand("brew", ["uninstall", "[email protected]"], false, true);
-				runCommand("brew", ["untap", "local/openssl"], false, true);
-				runCommand("brew", ["untap", "local/python2"], false, true);
+				attemptCommand("brew", ["uninstall", "[email protected]"]);
+				attemptCommand("brew", ["uninstall", "[email protected]"]);
+				attemptCommand("brew", ["untap", "local/openssl"]);
+				attemptCommand("brew", ["untap", "local/python2"]);
 				runCommand("brew", ["update"]);
 				runCommand("brew", ["install", "--cask", "flash-player-debugger"]);
 
@@ -173,7 +173,7 @@ class Flash {
 		runCommand("haxe", ["run.hxml"]);
 
 		if (!success)
-			fail();
+			throw new CommandFailure();
 	}
 
 

+ 22 - 33
tests/runci/targets/Hl.hx

@@ -3,32 +3,29 @@ package runci.targets;
 import haxe.io.Path;
 import sys.FileSystem;
 import runci.System.*;
-import runci.System.Failure;
 import runci.Config.*;
 
+using StringTools;
+
 class Hl {
-	static var hlSrc = switch [ci, systemName] {
-	  case [GithubActions, "Windows"]: "C:\\hashlink";
-	  case _: Path.join([Sys.getEnv("HOME"), "hashlink"]);
-	};
-	static var hlBuild = switch [ci, systemName] {
-	  case [GithubActions, "Windows"]: "C:\\hashlink_build";
-	  case _: Path.join([Sys.getEnv("HOME"), "hashlink_build"]);
-	};
-	static var hlBinDir = switch [ci, systemName] {
-	  case [GithubActions, "Windows"]: "C:\\hashlink_build\\bin";
-	  case _: Path.join([Sys.getEnv("HOME"), "hashlink_build", "bin"]);
-	};
-	static var hlBinary = switch [ci, systemName] {
-	  case [GithubActions, "Windows"]: "C:\\hashlink_build\\bin\\hl.exe";
-	  case _: Path.join([Sys.getEnv("HOME"), "hashlink_build", "bin", "hl"]);
-	};
-
-	static final miscHlDir = miscDir + 'hl/';
+	static final hlSrc = Path.join([getDownloadPath(), "hashlink"]);
+
+	static final hlBuild = Path.join([getInstallPath(), "hashlink_build"]);
+
+	static final hlBuildBinDir = Path.join([getInstallPath(), "hashlink_build", "bin"]);
+
+	static final hlBinary =
+		if (isCi() || !commandSucceed("hl", ["--version"])){
+			Path.join([hlBuildBinDir, "hl"]) + ((systemName == "Windows") ? ".exe" : "");
+		} else {
+			commandResult(if(systemName == "Windows") "where" else "which", ["hl"]).stdout.trim();
+		};
+
+	static final miscHlDir = getMiscSubDir('hl');
 
 	static public function getHlDependencies() {
-		if (commandSucceed("hl", ["--version"])) {
-			infoMsg('hl has already been installed.');
+		if (!isCi() && FileSystem.exists(hlBinary)) {
+			infoMsg('hl has already been installed at $hlBinary.');
 			return;
 		}
 		if (!FileSystem.exists(hlSrc))
@@ -40,14 +37,14 @@ class Hl {
 			case "Linux":
 				Linux.requireAptPackages(["libpng-dev", "libjpeg-turbo8-dev", "libturbojpeg", "zlib1g-dev", "libvorbis-dev"]);
 			case "Mac":
-				runCommand("brew", ["update", '--preinstall'], true);
-				runCommand("brew", ["bundle", '--file=${hlSrc}/Brewfile'], true);
+				runNetworkCommand("brew", ["update", '--preinstall']);
+				runNetworkCommand("brew", ["bundle", '--file=${hlSrc}/Brewfile']);
 			case "Windows":
 				//pass
 		}
 
 		FileSystem.createDirectory(hlBuild);
-		var generator = systemName == "Windows" ? ["-DCMAKE_SYSTEM_VERSION=10.0.19041.0"] : ["-GNinja"];
+		final generator = systemName == "Windows" ? ["-DCMAKE_SYSTEM_VERSION=10.0.19041.0"] : ["-GNinja"];
 		runCommand("cmake", generator.concat([
 			"-DBUILD_TESTING=OFF",
 			"-DWITH_BULLET=OFF",
@@ -68,7 +65,7 @@ class Hl {
 		]);
 
 		runCommand(hlBinary, ["--version"]);
-		addToPATH(hlBinDir);
+		addToPATH(hlBuildBinDir);
 
 		haxelibDev("hashlink", '$hlSrc/other/haxelib/');
 	}
@@ -76,14 +73,6 @@ class Hl {
 	static public function run(args:Array<String>) {
 		getHlDependencies();
 
-		var hlBinary = try {
-			if(!isCi())
-				runCommand(hlBinary, ["--version"]);
-			hlBinary;
-		} catch(e:Failure) {
-			'hl';
-		}
-
 		switch (systemName) {
 			case "Windows":
 				runCommand("haxe", ["compile-hl.hxml"].concat(args));

+ 13 - 16
tests/runci/targets/Java.hx

@@ -1,13 +1,13 @@
 package runci.targets;
 
 import sys.FileSystem;
+import haxe.io.Path;
 import runci.System.*;
 import runci.Config.*;
 using StringTools;
 
 class Java {
-	static var miscJavaDir(get,never):String;
-	static inline function get_miscJavaDir() return miscDir + 'java/';
+	static final miscJavaDir = getMiscSubDir('java');
 
 	static public function getJavaDependencies() {
 		haxelibInstallGit("HaxeFoundation", "hxjava", true);
@@ -36,20 +36,17 @@ class Java {
 		runCommand("java", ["-jar", "export/java/Main.jar"]);
 
 		infoMsg("Testing java-lib extras");
-		changeDirectory('$unitDir/bin');
-		if (!FileSystem.exists('java-lib-tests')) {
-			runCommand("git", ["clone", "https://github.com/waneck/java-lib-tests.git", "--depth", "1"], true);
-		}
-		for (dir in FileSystem.readDirectory('java-lib-tests'))
-		{
-			var path = 'java-lib-tests/$dir';
-			if (FileSystem.isDirectory(path)) for (file in FileSystem.readDirectory(path))
-			{
-				if (file.endsWith('.hxml'))
-				{
-					runCommand("haxe", ["--cwd",'java-lib-tests/$dir',file]);
-				}
-			}
+		changeDirectory(Path.join([unitDir, 'bin']));
+		final libTestDir = 'java-lib-tests';
+		if (!FileSystem.exists(libTestDir))
+			runNetworkCommand("git", ["clone", "https://github.com/waneck/java-lib-tests.git", "--depth", "1"]);
+
+		for (dir in FileSystem.readDirectory(libTestDir)) {
+			final path = Path.join([libTestDir, dir]);
+			if (FileSystem.isDirectory(path))
+				for (file in FileSystem.readDirectory(path))
+					if (file.endsWith('.hxml'))
+						runCommand("haxe", ["--cwd", path, file]);
 		}
 	}
 }

+ 30 - 13
tests/runci/targets/Js.hx

@@ -9,7 +9,7 @@ import sys.io.Process;
 using StringTools;
 
 class Js {
-	static final miscJsDir = miscDir + 'js/';
+	static final miscJsDir = getMiscSubDir('js');
 
 	static public function getJSDependencies() {
 		switch [ci, systemName] {
@@ -26,28 +26,45 @@ class Js {
 		runCommand("node", ["-v"]);
 	}
 
+	static function installNpmPackages(packages:Array<String>) {
+		final required = if (isCi()) {
+			packages;
+		} else {
+			final filtered = packages.filter( (lib) -> {
+				final isInstalled = commandSucceed("npm", ["list", lib]);
+				if (isInstalled)
+					infoMsg('npm package `$lib` has already been installed.');
+				return !isInstalled;
+			});
+			if (filtered.length == 0)
+				return;
+			filtered;
+		};
+		runNetworkCommand("npm", ["install"].concat(required));
+	}
+
 	static public function run(args:Array<String>) {
 		getJSDependencies();
 
-		var jsOutputs = [
+		final jsOutputs = [
 			for (es_ver in    [[], ["-D", "js-es=3"], ["-D", "js-es=6"]])
 			for (unflatten in [[], ["-D", "js-unflatten"]])
 			for (classic in   [[], ["-D", "js-classic"]])
 			for (enums_as_objects in [[], ["-D", "js-enums-as-arrays"]])
 			{
-				var extras = args.concat(es_ver).concat(unflatten).concat(classic).concat(enums_as_objects);
+				final extras = args.concat(es_ver).concat(unflatten).concat(classic).concat(enums_as_objects);
 
 				runCommand("haxe", ["compile-js.hxml"].concat(extras));
 
-				var output = if (extras.length > 0) {
+				final output = if (extras.length > 0) {
 					"bin/js/" + extras.join("") + "/unit.js";
 				} else {
 					"bin/js/default/unit.js";
 				}
-				var outputDir = Path.directory(output);
-				if (!FileSystem.exists(outputDir)) {
+				final outputDir = Path.directory(output);
+				if (!FileSystem.exists(outputDir))
 					FileSystem.createDirectory(outputDir);
-				}
+
 				FileSystem.rename("bin/unit.js", output);
 				FileSystem.rename("bin/unit.js.map", output + ".map");
 				runCommand("node", ["-e", "require('./" + output + "').unit.TestMain.main();"]);
@@ -56,17 +73,17 @@ class Js {
 		];
 
 		infoMsg("Test ES6:");
-		changeDirectory(miscDir + "es6");
+		changeDirectory(getMiscSubDir("es6"));
 		runCommand("haxe", ["run.hxml"]);
 
 		haxelibInstallGit("HaxeFoundation", "hxnodejs");
-		var env = Sys.environment();
+		final env = Sys.environment();
 		if (
 			env.exists("SAUCE") &&
 			env.exists("SAUCE_USERNAME") &&
 			env.exists("SAUCE_ACCESS_KEY")
 		) {
-			var sc = switch (ci) {
+			final sc = switch (ci) {
 				// TODO: figure out SauceConnect for GitHub Actions
 				// case AzurePipelines:
 				// 	var scVersion = "sc-4.5.3-linux";
@@ -89,9 +106,9 @@ class Js {
 			}
 
 			changeDirectory(unitDir);
-			runCommand("npm", ["install", "wd", "q"], true);
+			installNpmPackages(["wd", "q"]);
 			runCommand("haxe", ["compile-saucelabs-runner.hxml"]);
-			var server = new Process("nekotools", ["server"]);
+			final server = new Process("nekotools", ["server"]);
 			runCommand("node", ["bin/RunSauceLabs.js"].concat([for (js in jsOutputs) "unit-js.html?js=" + js.urlEncode()]));
 
 			server.close();
@@ -111,7 +128,7 @@ class Js {
 		runCommand("node", ["test.js"]);
 
 		changeDirectory(sysDir);
-		runCommand("npm", ["install", "deasync"], true);
+		installNpmPackages(["deasync"]);
 		runCommand("haxe", ["compile-js.hxml"].concat(args));
 		runSysTest("node", ["bin/js/sys.js"]);
 

+ 1 - 1
tests/runci/targets/Jvm.hx

@@ -9,7 +9,7 @@ class Jvm {
 		Java.getJavaDependencies();
 
 		for (level in 0...3) {
-			var args = args.concat(["-D", "jvm.dynamic-level=" + level]);
+			final args = args.concat(["-D", "jvm.dynamic-level=" + level]);
 			runCommand("haxe", ["compile-jvm.hxml"].concat(args));
 			runCommand("java", ["-jar", "bin/unit.jar"]);
 

+ 9 - 12
tests/runci/targets/Lua.hx

@@ -1,29 +1,27 @@
 package runci.targets;
 
-import sys.FileSystem;
 import runci.System.*;
 import runci.Config.*;
 import haxe.io.*;
 using StringTools;
 
 class Lua {
-	static var miscLuaDir(get,never):String;
-	static inline function get_miscLuaDir() return miscDir + 'lua/';
+	static final miscLuaDir = getMiscSubDir('lua');
 
 	static public function getLuaDependencies(){
 		switch (systemName){
 			case "Linux":
 				Linux.requireAptPackages(["libpcre3-dev", "libssl-dev", "libreadline-dev"]);
 				runCommand("pip", ["install", "--user", "hererocks"]);
-				var pyUserBase = commandResult("python", ["-m", "site", "--user-base"]).stdout.trim();
+				final pyUserBase = commandResult("python", ["-m", "site", "--user-base"]).stdout.trim();
 				addToPATH(Path.join([pyUserBase, "bin"]));
 			case "Mac": {
 				if (commandSucceed("python3", ["-V"]))
 					infoMsg('python3 has already been installed.');
 				else
-					runCommand("brew", ["install", "python3"], true);
+					runNetworkCommand("brew", ["install", "python3"]);
 
-				runCommand("brew", ["install", "pcre"], false, true);
+				attemptCommand("brew", ["install", "pcre"]);
 				runCommand("pip3", ["install", "hererocks"]);
 			}
 		}
@@ -31,9 +29,9 @@ class Lua {
 
 	static function installLib(lib : String, version : String, ?server :String){
 		if (!commandSucceed("luarocks", ["show", lib, version])) {
-            var args = ["install", lib, version];
+            final args = ["install", lib, version];
             if (server != null){
-                var server_arg = '--server=$server';
+                final server_arg = '--server=$server';
                 args.push(server_arg);
             }
 			runCommand("luarocks", args);
@@ -47,8 +45,7 @@ class Lua {
 		getLuaDependencies();
 
 		for (lv in ["-l5.1", "-l5.2", "-l5.3"].concat(systemName == 'Linux' && Linux.arch == Arm64 ? [] : ["-j2.0", "-j2.1"])) {
-
-			var envpath = Sys.getEnv("HOME") + '/lua_env$lv';
+			final envpath = getInstallPath() + '/lua_env/lua$lv';
 			addToPATH(envpath + '/bin');
 
 			if (systemName == "Mac" && lv.startsWith("-j")) continue;
@@ -67,7 +64,7 @@ class Lua {
 			runCommand("luarocks", ["config", "--rock-trees"]);
 
 			// Note: don't use a user config
-			// runCommand("luarocks", ["config", "--user-config"], false, true);
+			// attemptCommand("luarocks", ["config", "--user-config"]);
 
 			installLib("haxe-deps", "0.0.1-6");
 
@@ -79,7 +76,7 @@ class Lua {
 			runCommand("haxe", ["compile-lua.hxml"].concat(args));
 			runSysTest("lua", ["bin/lua/sys.lua"]);
 
-			changeDirectory(miscDir + "luaDeadCode/stringReflection");
+			changeDirectory(getMiscSubDir("luaDeadCode", "stringReflection"));
 			runCommand("haxe", ["compile.hxml"]);
 
 			changeDirectory(miscLuaDir);

+ 3 - 4
tests/runci/targets/Macro.hx

@@ -1,6 +1,5 @@
 package runci.targets;
 
-import sys.FileSystem;
 import runci.System.*;
 import runci.Config.*;
 
@@ -21,10 +20,10 @@ class Macro {
 		infoMsg("Js-es6 null safety:");
 		runCommand("haxe", ["test-js-es6.hxml"]);
 
-		changeDirectory(miscDir);
+		changeDirectory(getMiscSubDir());
 		runCommand("haxe", ["compile.hxml"]);
 
-		changeDirectory(miscDir + "resolution");
+		changeDirectory(getMiscSubDir("resolution"));
 		runCommand("haxe", ["run.hxml"]);
 
 		changeDirectory(sysDir);
@@ -32,7 +31,7 @@ class Macro {
 
 		switch Sys.systemName() {
 			case 'Linux':
-				changeDirectory(miscDir + 'compiler_loops');
+				changeDirectory(getMiscSubDir('compiler_loops'));
 				runCommand("haxe", ["run.hxml"]);
 			case _: // TODO
 		}

+ 0 - 1
tests/runci/targets/Neko.hx

@@ -1,6 +1,5 @@
 package runci.targets;
 
-import sys.FileSystem;
 import runci.System.*;
 import runci.Config.*;
 

+ 8 - 10
tests/runci/targets/Php.hx

@@ -1,17 +1,15 @@
 package runci.targets;
 
-import sys.FileSystem;
 import runci.System.*;
 import runci.Config.*;
 
 class Php {
-	static var miscPhpDir(get,never):String;
-	static inline function get_miscPhpDir() return miscDir + 'php/';
+	static final miscPhpDir = getMiscSubDir('php');
 
 	static public function getPhpDependencies() {
-		var phpCmd = commandResult("php", ["-v"]);
-		var phpVerReg = ~/PHP ([0-9]+\.[0-9]+)/i;
-		var phpVer = if (phpVerReg.match(phpCmd.stdout))
+		final phpCmd = commandResult("php", ["-v"]);
+		final phpVerReg = ~/PHP ([0-9]+\.[0-9]+)/i;
+		final phpVer = if (phpVerReg.match(phpCmd.stdout))
 			Std.parseFloat(phpVerReg.matched(1));
 		else
 			null;
@@ -33,9 +31,9 @@ class Php {
 				// TODO: install php-sqlite3?
 				Linux.requireAptPackages(["php-cli", "php-mbstring"]);
 			case "Mac":
-				runCommand("brew", ["install", "php"], true);
+				runNetworkCommand("brew", ["install", "php"]);
 			case "Windows":
-				runCommand("cinst", ["php", "-version", "7.1.8", "-y"], true);
+				runNetworkCommand("cinst", ["php", "-version", "7.1.8", "-y"]);
 			case _:
 				throw 'unknown system: $systemName';
 		}
@@ -48,9 +46,9 @@ class Php {
 		changeDirectory(miscPhpDir);
 		runCommand("haxe", ["run.hxml"]);
 
-		var binDir = "bin/php";
+		final binDir = "bin/php";
 
-		var prefixes = [[]];
+		final prefixes = [[]];
 		if(isCi()) {
 			prefixes.push(['-D', 'php-prefix=haxe']);
 			prefixes.push(['-D', 'php-prefix=my.pack']);

+ 8 - 11
tests/runci/targets/Python.hx

@@ -5,9 +5,6 @@ import runci.System.*;
 import runci.Config.*;
 
 class Python {
-	static var miscPythonDir(get,never):String;
-	static inline function get_miscPythonDir() return miscDir + 'python/';
-
 	static public function getPythonDependencies():Array<String> {
 		switch (systemName) {
 			case "Linux":
@@ -21,13 +18,13 @@ class Python {
 				if (commandSucceed(pypy, ["-V"])) {
 					infoMsg('pypy3 has already been installed.');
 				} else {
-					var pypyVersion = "pypy3.8-v7.3.7-" + (switch Linux.arch {
+					final pypyVersion = "pypy3.8-v7.3.7-" + (switch Linux.arch {
 						case Arm64: "aarch64";
 						case Amd64: "linux64";
 					});
-					var file = '${pypyVersion}.tar.bz2';
+					final file = '${pypyVersion}.tar.bz2';
 					if(!FileSystem.exists(file)) {
-						runCommand("wget", ["-nv", 'https://downloads.python.org/pypy/$file'], true);
+						runNetworkCommand("wget", ["-nv", 'https://downloads.python.org/pypy/$file']);
 					}
 					runCommand("tar", ["-xf", file]);
 					pypy = FileSystem.fullPath('${pypyVersion}/bin/pypy3');
@@ -39,13 +36,13 @@ class Python {
 				if (commandSucceed("python3", ["-V"]))
 					infoMsg('python3 has already been installed.');
 				else
-					runCommand("brew", ["install", "python3"], true);
+					runNetworkCommand("brew", ["install", "python3"]);
 				runCommand("python3", ["-V"]);
 
 				if (commandSucceed("pypy3", ["-V"]))
 					infoMsg('pypy3 has already been installed.');
 				else
-					runCommand("brew", ["install", "pypy3"], true);
+					runNetworkCommand("brew", ["install", "pypy3"]);
 				runCommand("pypy3", ["-V"]);
 
 				return ["python3", "pypy3"];
@@ -62,7 +59,7 @@ class Python {
 	}
 
 	static public function run(args:Array<String>) {
-		var pys = getPythonDependencies();
+		final pys = getPythonDependencies();
 		runCommand("haxe", ["compile-python.hxml"].concat(args));
 		for (py in pys) {
 			runCommand(py, ["bin/unit.py"]);
@@ -74,10 +71,10 @@ class Python {
 			runSysTest(py, ["bin/python/sys.py"]);
 		}
 
-		changeDirectory(miscPythonDir);
+		changeDirectory(getMiscSubDir("python"));
 		runCommand("haxe", ["run.hxml"]);
 
-		changeDirectory(miscPythonDir + "pythonImport");
+		changeDirectory(getMiscSubDir('python', "pythonImport"));
 		runCommand("haxe", ["compile.hxml"]);
 		for (py in pys) {
 			runCommand(py, ["test.py"]);

+ 1 - 0
tests/sys/compile.hxml

@@ -9,3 +9,4 @@
 --next compile-php.hxml
 --next compile-hl.hxml
 --next compile-js.hxml
+--next compile-lua.hxml

+ 0 - 99
tests/sys/genTestRes.py

@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-
-# Generates files and directories in test-res used for Unicode sys tests.
-# The test vector printf'ed into data.bin, as well as the names in filenames()
-# should correspond exactly to the sequences in UnicodeSequences.valid.
-
-# Run with:
-#   python3 genTestRes.py
-# Or:
-#   python3 genTestRes.py TEST_INVALID_UNICODE_FS
-# The latter will attempt to create filenames which contain invalid Unicode
-# codepoints; this does not work on some filesystems, e.g. APFS.
-
-import os
-import shutil
-import sys
-
-MODE = " ".join(sys.argv[1:])
-
-TESTDIR = "test-res"
-
-# delete previous, if any
-if os.path.isdir(TESTDIR):
-  shutil.rmtree(TESTDIR)
-
-os.mkdir(TESTDIR)
-
-# Unicode test vectors
-allUnicode = [
-    [0x01], # will not work on NTFS
-    [0x7F],
-    [0xC2, 0x80],
-    [0xDF, 0xBF],
-    [0xE0, 0xA0, 0x80],
-    [0xED, 0x9F, 0xBF], # will not work on APFS
-    [0xEE, 0x80, 0x80],
-    [0xEF, 0xBF, 0xBD],
-    [0xF0, 0x90, 0x80, 0x80],
-    [0xF0, 0x9F, 0xBF, 0xBF], # will not work on APFS
-    [0xF3, 0xBF, 0xBF, 0xBF], # will not work on APFS
-    [0xF4, 0x80, 0x80, 0x80],
-    [0xF4, 0x8F, 0xBF, 0xBF], # will not work on APFS
-    [0xF0, 0x9F, 0x98, 0x82, 0xF0, 0x9F, 0x98, 0x84, 0xF0, 0x9F, 0x98, 0x99],
-    [0xC8, 0xA7],
-    [0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, 0xEF, 0xBC, 0x8C, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0x94]
-  ]
-
-allStrings = [ bytes(data).decode("utf-8") for data in allUnicode ]
-
-allFilenames = allStrings[:]
-# Windows does not allow codepoints in the U+0000 - U+001F range
-# see https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
-if os.name == "nt":
-  allFilenames.remove(bytes([0x01]).decode("utf-8"))
-
-# on APFS (macOS 10.13+), filenames must consist of valid Unicode codepoints
-if MODE != "TEST_INVALID_UNICODE_FS":
-  allFilenames.remove(bytes([0xED, 0x9F, 0xBF]).decode("utf-8"))
-  allFilenames.remove(bytes([0xF0, 0x9F, 0xBF, 0xBF]).decode("utf-8"))
-  allFilenames.remove(bytes([0xF3, 0xBF, 0xBF, 0xBF]).decode("utf-8"))
-  allFilenames.remove(bytes([0xF4, 0x8F, 0xBF, 0xBF]).decode("utf-8"))
-
-allBinary = b""
-for data in allUnicode:
-  allBinary += bytes(data) + b"\n"
-
-# generate a file with Unicode data
-with open(os.path.join(TESTDIR, "data.bin"), "wb") as f:
-  f.write(allBinary)
-
-# generate sub-directories with symlinks
-os.mkdir(os.path.join(TESTDIR, "a"))
-for data in allFilenames:
-  os.mkdir(os.path.join(TESTDIR, data))
-  os.mkdir(os.path.join(TESTDIR, "a", data))
-  if os.name != "nt":
-    for target, name in [
-      ("../../bin/cpp/UtilityProcess-debug", "bin-cpp-debug"),
-      ("../../bin/cpp/UtilityProcess", "bin-cpp"),
-      ("../../bin/cs/bin/UtilityProcess-Debug.exe", "bin-cs-debug"),
-      ("../../bin/cs/bin/UtilityProcess.exe", "bin-cs"),
-      ("../../bin/hl/UtilityProcess.hl", "bin-hl"),
-      ("../../bin/lua/UtilityProcess.lua", "bin-lua"),
-      ("../../bin/java/UtilityProcess-Debug.jar", "bin-java-debug"),
-      ("../../bin/java/UtilityProcess.jar", "bin-java"),
-      ("../../bin/jvm/UtilityProcess.jar", "bin-jvm"),
-      ("../../bin/neko/UtilityProcess.n", "bin-neko"),
-      ("../../bin/php/UtilityProcess/index.php", "bin-php"),
-      ("../../bin/python/UtilityProcess.py", "bin-py"),
-      ("../../src/UtilityProcess.hx", "bin-eval"),
-      ("../../bin/js/UtilityProcess.js", "bin-js")
-    ]:
-      os.symlink(target, os.path.join(TESTDIR, data, name), target_is_directory = False)
-
-# files
-os.mkdir(os.path.join(TESTDIR, "b"))
-for data in allFilenames:
-  with open(os.path.join(TESTDIR, "b", data), "wb") as f:
-    f.write(allBinary)

+ 101 - 0
tests/sys/gen_test_res.py

@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+
+# Generates files and directories in test-res used for Unicode sys tests.
+# The test vector printf'ed into data.bin, as well as the names in filenames()
+# should correspond exactly to the sequences in UnicodeSequences.valid.
+
+# Run with:
+#   python3 gen_test_res.py
+# Or:
+#   python3 gen_test_res.py TEST_INVALID_UNICODE_FS
+# The latter will attempt to create filenames which contain invalid Unicode
+# codepoints; this does not work on some filesystems, e.g. APFS.
+
+import os
+import shutil
+import sys
+
+MODE = " ".join(sys.argv[1:])
+
+TESTDIR = "test-res"
+
+# delete previous, if any
+if os.path.isdir(TESTDIR):
+    shutil.rmtree(TESTDIR)
+
+os.mkdir(TESTDIR)
+
+# Unicode test vectors
+all_unicode = [
+    [0x01],  # will not work on NTFS
+    [0x7F],
+    [0xC2, 0x80],
+    [0xDF, 0xBF],
+    [0xE0, 0xA0, 0x80],
+    [0xED, 0x9F, 0xBF],  # will not work on APFS
+    [0xEE, 0x80, 0x80],
+    [0xEF, 0xBF, 0xBD],
+    [0xF0, 0x90, 0x80, 0x80],
+    [0xF0, 0x9F, 0xBF, 0xBF],  # will not work on APFS
+    [0xF3, 0xBF, 0xBF, 0xBF],  # will not work on APFS
+    [0xF4, 0x80, 0x80, 0x80],
+    [0xF4, 0x8F, 0xBF, 0xBF],  # will not work on APFS
+    [0xF0, 0x9F, 0x98, 0x82, 0xF0, 0x9F, 0x98, 0x84, 0xF0, 0x9F, 0x98, 0x99],
+    [0xC8, 0xA7],
+    [0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, 0xEF, 0xBC, 0x8C, 0xE3, 0x81, 0xAB,
+        0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0x94]
+  ]
+
+all_strings = [bytes(data).decode("utf-8") for data in all_unicode]
+
+all_filenames = all_strings[:]
+# Windows does not allow codepoints in the U+0000 - U+001F range
+# see https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
+if os.name == "nt":
+    all_filenames.remove(bytes([0x01]).decode("utf-8"))
+
+# on APFS (macOS 10.13+), filenames must consist of valid Unicode codepoints
+if MODE != "TEST_INVALID_UNICODE_FS":
+    all_filenames.remove(bytes([0xED, 0x9F, 0xBF]).decode("utf-8"))
+    all_filenames.remove(bytes([0xF0, 0x9F, 0xBF, 0xBF]).decode("utf-8"))
+    all_filenames.remove(bytes([0xF3, 0xBF, 0xBF, 0xBF]).decode("utf-8"))
+    all_filenames.remove(bytes([0xF4, 0x8F, 0xBF, 0xBF]).decode("utf-8"))
+
+all_binary = b""
+for data in all_unicode:
+    all_binary += bytes(data) + b"\n"
+
+# generate a file with Unicode data
+with open(os.path.join(TESTDIR, "data.bin"), "wb") as f:
+    f.write(all_binary)
+
+# generate sub-directories with symlinks
+os.mkdir(os.path.join(TESTDIR, "a"))
+for data in all_filenames:
+    os.mkdir(os.path.join(TESTDIR, data))
+    os.mkdir(os.path.join(TESTDIR, "a", data))
+    if os.name == "nt":
+        continue
+    for target, name in [
+        ("../../bin/cpp/UtilityProcess-debug", "bin-cpp-debug"),
+        ("../../bin/cpp/UtilityProcess", "bin-cpp"),
+        ("../../bin/cs/bin/UtilityProcess-Debug.exe", "bin-cs-debug"),
+        ("../../bin/cs/bin/UtilityProcess.exe", "bin-cs"),
+        ("../../bin/hl/UtilityProcess.hl", "bin-hl"),
+        ("../../bin/lua/UtilityProcess.lua", "bin-lua"),
+        ("../../bin/java/UtilityProcess-Debug.jar", "bin-java-debug"),
+        ("../../bin/java/UtilityProcess.jar", "bin-java"),
+        ("../../bin/jvm/UtilityProcess.jar", "bin-jvm"),
+        ("../../bin/neko/UtilityProcess.n", "bin-neko"),
+        ("../../bin/php/UtilityProcess/index.php", "bin-php"),
+        ("../../bin/python/UtilityProcess.py", "bin-py"),
+        ("../../src/UtilityProcess.hx", "bin-eval"),
+        ("../../bin/js/UtilityProcess.js", "bin-js")
+    ]:
+        os.symlink(target, os.path.join(TESTDIR, data, name), target_is_directory=False)
+
+# files
+os.mkdir(os.path.join(TESTDIR, "b"))
+for data in all_filenames:
+    with open(os.path.join(TESTDIR, "b", data), "wb") as f:
+        f.write(all_binary)

+ 2 - 0
tests/sys/run.hxml

@@ -18,6 +18,7 @@ compile.hxml
 --cmd echo Php    && export EXISTS=1 && php bin/php/Main/index.php
 --cmd echo Hl     && export EXISTS=1 && hl bin/hl/sys.hl
 --cmd echo Js     && export EXISTS=1 && node bin/js/sys.js
+--cmd echo Lua    && export EXISTS=1 && lua bin/sys.lua
 --cmd echo Macro  && export EXISTS=1 && haxe compile-macro.hxml
 
 # Windows
@@ -30,4 +31,5 @@ compile.hxml
 # --cmd echo Php    && set EXISTS=1 && php bin\php\Main\index.php
 # --cmd echo Hl     && set EXISTS=1 && hl bin/hl/sys.hl
 # --cmd echo Js     && set EXISTS=1 && node bin/js/sys.js
+# --cmd echo Lua    && set EXISTS=1 && lua bin/lua/sys.lua
 # --cmd echo Macro  && set EXISTS=1 && haxe compile-macro.hxml

+ 2 - 2
tests/sys/src/TestUnicode.hx

@@ -131,9 +131,9 @@ class TestUnicode extends utest.Test {
 	function setupClass() {
 		FileSystem.createDirectory("temp-unicode");
 		#if TEST_INVALID_UNICODE_FS
-		Sys.command("python3", ["genTestRes.py", "TEST_INVALID_UNICODE_FS"]);
+		Sys.command("python3", ["gen_test_res.py", "TEST_INVALID_UNICODE_FS"]);
 		#else
-		Sys.command("python3", ["genTestRes.py"]);
+		Sys.command("python3", ["gen_test_res.py"]);
 		#end
 	}
 

+ 1 - 1
tests/unit/compile-each.hxml

@@ -1,4 +1,4 @@
--D source-header=''
+-D source-header=""
 --debug
 -p src
 # -cp "C:\Program Files\The Haxe Effect\src/dev/null"