diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a0c02e2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,74 @@ +name: Test + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +jobs: + test: + runs-on: ${{ matrix.os }} + container: ${{ matrix.container }} + name: ${{ matrix.label }} + defaults: + run: + shell: bash + # Mirror alexzhangs/xsh's shell matrix: bash 3.2 / 4.4 / 5.x + zsh 5.x. + # Utilities run under zsh's ksh emulation (provided by xsh >= 0.7.0). + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-latest, container: '', shell_path: /bin/bash, label: bash-5.x-linux } + - { os: macos-latest, container: '', shell_path: /bin/bash, label: bash-3.2-macos } + - { os: macos-latest, container: '', shell_path: brew, label: bash-5.x-macos } + - { os: ubuntu-latest, container: rockylinux:8, shell_path: /bin/bash, label: bash-4.4-linux } + - { os: macos-latest, container: '', shell_path: /bin/zsh, label: zsh-5.x-macos } + - { os: ubuntu-latest, container: '', shell_path: /usr/bin/zsh, label: zsh-5.x-linux } + + steps: + - name: Pre-install git (rockylinux container) + if: matrix.container != '' + run: dnf install -y git + + - name: Install dependencies (rockylinux container) + if: matrix.container != '' + run: | + dnf install -y --allowerasing coreutils + dnf install -y gawk sed file findutils diffutils procps-ng which tar gzip python3 + command -v python >/dev/null 2>&1 || ln -sf "$(command -v python3)" /usr/local/bin/python + git config --global --add safe.directory '*' + + - name: Install zsh (Linux) + if: matrix.container == '' && runner.os == 'Linux' && contains(matrix.shell_path, 'zsh') + run: sudo apt-get update && sudo apt-get install -y zsh + + - name: Install Homebrew bash (macOS bash 5.x) + if: matrix.shell_path == 'brew' + run: | + brew install bash + echo "SHELL_PATH=$(brew --prefix)/bin/bash" >> "$GITHUB_ENV" + + - name: Set shell path + if: matrix.shell_path != 'brew' + run: echo "SHELL_PATH=${{ matrix.shell_path }}" >> "$GITHUB_ENV" + + - name: Install xsh + run: | + git clone --depth=50 --branch=master https://github.com/alexzhangs/xsh.git /tmp/xsh + bash /tmp/xsh/install.sh + + - name: Load dependency library xsh-lib/core + run: | + source ~/.xshrc + xsh load xsh-lib/core + + - name: Load library from current branch + run: | + source ~/.xshrc + xsh load -b "${{ github.head_ref || github.ref_name }}" xsh-lib/shadowsocks + + - name: Run tests (${{ matrix.label }}) + run: | + "$SHELL_PATH" ~/.xsh/repo/xsh-lib/shadowsocks/test.sh diff --git a/README.md b/README.md index 9c08987..57efcc3 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,16 @@ About xsh and its libraries, check out [xsh document](https://github.com/alexzha ## Requirements -1. bash +1. bash or zsh Tested with bash: * 4.3.48 on Linux * 3.2.57 on macOS + The utilities also run under **zsh** (the default shell on modern macOS); + xsh executes them under zsh's ksh emulation. Tested with zsh 5.x. + Requires xsh >= 0.7.0 for zsh. + ## Dependency 1. xsh-lib/core diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..9c08afa --- /dev/null +++ b/test.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# +# Smoke + functional tests for xsh-lib/shadowsocks. Plain assertions, no +# external test framework (mirrors xsh-lib/core/test.sh). +# +# Usage: +# xsh load xsh-lib/core xsh-lib/shadowsocks # one-time +# bash test.sh # or: zsh test.sh +# + +# Make the `xsh` function available when run as a child process. Under bash it +# is inherited as an exported function (no-op here); zsh cannot export functions, +# so a child `zsh test.sh` sources ~/.xshrc to define xsh as a real zsh function. +if ! type xsh 2>/dev/null | grep -q 'function'; then + # shellcheck source=/dev/null + . ~/.xshrc +fi + +set -e -o pipefail + +xsh log info "xsh list ss/" +xsh list 'ss/*' >/dev/null + +# ---------------------------------------------------------------------------- +# import-smoke: every function utility sources cleanly (under zsh each is +# sourced with the injected `emulate -L ksh`). +# ---------------------------------------------------------------------------- +xsh log info "import-smoke: all ss function utilities" +while read -r __lpue; do + [ -z "$__lpue" ] && continue + xsh import "$__lpue" +done < <(xsh list 'ss/*' | awk '$1 == "[functions]" {print $2}') + +# ---------------------------------------------------------------------------- +# ss/libev/plugin/v2ray/config — rewrites an ss-libev JSON config to add the +# v2ray-plugin `plugin`/`plugin_opts` keys (pure transform via /json/parser). +# ---------------------------------------------------------------------------- +xsh log info "ss/libev/plugin/v2ray/config (plugin keys)" +__cfg=$(mktemp "${TMPDIR:-/tmp}/xsh-ss-test.XXXXXXXX") +printf '%s' '{"server":"0.0.0.0","server_port":8388,"password":"pw","method":"aes-256-gcm"}' > "$__cfg" + +__out=$(xsh ss/libev/plugin/v2ray/config -f "$__cfg" -b /usr/local/bin/v2ray-plugin) +case $__out in + *'/usr/local/bin/v2ray-plugin'*) : ;; + *) echo "FAIL: plugin not added: $__out" >&2; exit 1 ;; +esac +case $__out in + *'server;loglevel=none'*) : ;; + *) echo "FAIL: plugin_opts missing: $__out" >&2; exit 1 ;; +esac + +xsh log info "ss/libev/plugin/v2ray/config (-n/-c/-k adds tls opts)" +__out_tls=$(xsh ss/libev/plugin/v2ray/config -f "$__cfg" -b /usr/local/bin/v2ray-plugin \ + -n example.com -c /tmp/cert.pem -k /tmp/key.pem) +case $__out_tls in + *'tls'*'host=example.com'*'cert=/tmp/cert.pem'*'key=/tmp/key.pem'*) : ;; + *) echo "FAIL: tls plugin_opts missing: $__out_tls" >&2; exit 1 ;; +esac +rm -f "$__cfg" + +xsh log info "ss/libev/plugin/v2ray/config (missing config file = error)" +! xsh ss/libev/plugin/v2ray/config -f /no/such/file 2>/dev/null + +echo +echo "All tests passed." + +exit