mirror of https://github.com/miraclx/freyr-js
chore: revamp ci testing (#264)
This commit is contained in:
parent
42cd913453
commit
9cd5ae9552
|
|
@ -1,51 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
RG_SRC="$(which rg)"
|
||||
rg() {
|
||||
printf 'rg: pattern: %s' "/$*/" >/dev/stderr
|
||||
if $RG_SRC --fixed-strings --passthru "$@"; then
|
||||
echo " (matched)" >/dev/stderr
|
||||
else
|
||||
echo " (failed to match)" >/dev/stderr
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
freyr() {
|
||||
echo "::group::[$attempts/3] Downloading..."
|
||||
script -qfc "freyr --no-bar $*" /dev/null | tee .freyr_log
|
||||
echo "::endgroup::"
|
||||
i=$($RG_SRC -n '.' .freyr_log | $RG_SRC --fixed-strings '[•] Embedding Metadata' | cut -d':' -f1)
|
||||
if [[ $i ]]; then
|
||||
echo "::group::[$attempts/3] View Download Status"
|
||||
tail +"$i" .freyr_log
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_retry() {
|
||||
cmd="$(cat)" && attempts=1
|
||||
until eval "$cmd"; do
|
||||
echo "::endgroup::"
|
||||
if ((attempts < 3)); then
|
||||
echo "::warning::[$attempts/3] Download failed, retrying.."
|
||||
: $((attempts += 1))
|
||||
else
|
||||
echo "::error::[$attempts/3] Download failed."
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
echo "::endgroup::"
|
||||
echo "::group::View Files"
|
||||
STAGE=$(realpath --relative-to=../.. .) && cd ../..
|
||||
tree -sh "$STAGE"
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
validate() {
|
||||
echo "::group::[$attempts/3] Verifying..."
|
||||
res=$(<.freyr_log)
|
||||
for arg in "[•] Collation Complete" "$@"; do
|
||||
res=$(echo "$res" | rg "$arg") || return 1
|
||||
done >/dev/null
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ffmpeg ripgrep atomicparsley
|
||||
sudo apt-get install -y ffmpeg atomicparsley
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
|
|
@ -67,51 +67,23 @@ jobs:
|
|||
|
||||
- name: Spotify - Download Track
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/spotify/track
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://open.spotify.com/track/5FNS5Vj69AhRGJWjhrAd01
|
||||
validate \
|
||||
"[•] Total tracks: [01]" \
|
||||
"✓ Passed: [01]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/spotify/track
|
||||
npm run test -- spotify.track
|
||||
|
||||
- name: Spotify - Download Album
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/spotify/album
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://open.spotify.com/album/2D23kwwoy2JpZVuJwzE42B
|
||||
validate \
|
||||
"[•] Total tracks: [04]" \
|
||||
"✓ Passed: [04]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/spotify/album
|
||||
npm run test -- spotify.album
|
||||
|
||||
- name: Spotify - Download Artist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/spotify/artist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://open.spotify.com/artist/4adSXA1GDOxNG7Zw89YHyz \
|
||||
-l album=\"the rainbow cassette\" \
|
||||
-l album=\"make believe\"
|
||||
validate \
|
||||
"✓ Passed: [10]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/spotify/artist
|
||||
npm run test -- spotify.artist
|
||||
|
||||
- name: Spotify - Download Playlist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/spotify/playlist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://open.spotify.com/playlist/5KcCGEx7fFqXYNiJkSQ5KT
|
||||
validate \
|
||||
"✓ Passed: [05]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/spotify/playlist
|
||||
npm run test -- spotify.playlist
|
||||
|
||||
apple_music:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -123,7 +95,7 @@ jobs:
|
|||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ffmpeg ripgrep atomicparsley
|
||||
sudo apt-get install -y ffmpeg atomicparsley
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
|
|
@ -140,51 +112,23 @@ jobs:
|
|||
|
||||
- name: Apple Music - Download Track
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/apple_music/track
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://music.apple.com/us/album/elio-irl/1547735824?i=1547736100
|
||||
validate \
|
||||
"[•] Total tracks: [01]" \
|
||||
"✓ Passed: [01]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/apple_music/track
|
||||
npm run test -- apple_music.track
|
||||
|
||||
- name: Apple Music - Download Album
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/apple_music/album
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://music.apple.com/us/album/im-sorry-im-not-sorry-ep/1491795443
|
||||
validate \
|
||||
"[•] Total tracks: [04]" \
|
||||
"✓ Passed: [04]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/apple_music/album
|
||||
npm run test -- apple_music.album
|
||||
|
||||
- name: Apple Music - Download Artist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/apple_music/artist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://music.apple.com/us/artist/mazie/1508029053 \
|
||||
-l album=\"the rainbow cassette\" \
|
||||
-l album=\"make believe\"
|
||||
validate \
|
||||
"✓ Passed: [10]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/apple_music/artist
|
||||
npm run test -- apple_music.artist
|
||||
|
||||
- name: Apple Music - Download Playlist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/apple_music/playlist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://music.apple.com/us/playlist/songs-from-up-next-bazzi/pl.56c4bdf909954beca0cf69379a48144f
|
||||
validate \
|
||||
"✓ Passed: [06]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/apple_music/playlist
|
||||
npm run test -- apple_music.playlist
|
||||
|
||||
deezer:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -196,7 +140,7 @@ jobs:
|
|||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ffmpeg ripgrep atomicparsley
|
||||
sudo apt-get install -y ffmpeg atomicparsley
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
|
|
@ -213,51 +157,23 @@ jobs:
|
|||
|
||||
- name: Deezer - Download Track
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/deezer/track
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://www.deezer.com/en/track/1189202982
|
||||
validate \
|
||||
"[•] Total tracks: [01]" \
|
||||
"✓ Passed: [01]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/deezer/track
|
||||
npm run test -- deezer.track
|
||||
|
||||
- name: Deezer - Download Album
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/deezer/album
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://www.deezer.com/en/album/123330212
|
||||
validate \
|
||||
"[•] Total tracks: [04]" \
|
||||
"✓ Passed: [04]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/deezer/album
|
||||
npm run test -- deezer.album
|
||||
|
||||
- name: Deezer - Download Artist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/deezer/artist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://www.deezer.com/en/artist/14808825 \
|
||||
-l album=\"the rainbow cassette\" \
|
||||
-l album=\"make believe\"
|
||||
validate \
|
||||
"✓ Passed: [10]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/deezer/artist
|
||||
npm run test -- deezer.artist
|
||||
|
||||
- name: Deezer - Download Playlist
|
||||
run: |
|
||||
. .github/workflows/ci-prep.sh && cd ./CI/deezer/playlist
|
||||
|
||||
exec_retry << EOF
|
||||
freyr https://www.deezer.com/en/playlist/10004168842
|
||||
validate \
|
||||
"✓ Passed: [05]" \
|
||||
"✕ Failed: [00]"
|
||||
EOF
|
||||
cd ./CI/deezer/playlist
|
||||
npm run test -- deezer.playlist
|
||||
|
||||
|
||||
docker-build:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Fix long standing issue with freyr seeming frozen on exit. <https://github.com/miraclx/freyr-js/pull/216>
|
||||
- Upgraded to ES6 Modules. <https://github.com/miraclx/freyr-js/pull/202>
|
||||
- Introduced the pushing of docker images for each PR. <https://github.com/miraclx/freyr-js/pull/218>, <https://github.com/miraclx/freyr-js/pull/228>
|
||||
- Introduced a test runner, with local reproducible builds. <https://github.com/miraclx/freyr-js/pull/264>
|
||||
- Introduced CI checks for formatting.
|
||||
- Updated dependencies.
|
||||
- Removed some unused dependencies. <https://github.com/miraclx/freyr-js/pull/217>, <https://github.com/miraclx/freyr-js/pull/245>
|
||||
|
|
|
|||
|
|
@ -1081,6 +1081,10 @@ cd freyr
|
|||
yarn link
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Freyr comes bundled with a lightweight test suite. See [TEST.md](https://github.com/miraclx/freyr-js/blob/master/TEST.md) for instructions on how to run it.
|
||||
|
||||
### Docker Development
|
||||
|
||||
With docker, you can drop into a sandbox that has all the dependencies you need. Without needing to mess around with your host system or install any weird dependencies.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
# freyr testing
|
||||
|
||||
freyr is bundled with its own flexibly customizable test runner.
|
||||
|
||||
- To run all tests
|
||||
|
||||
```console
|
||||
npm run test -- --all
|
||||
```
|
||||
|
||||
- To run just Spotify tests
|
||||
|
||||
```console
|
||||
npm run test -- spotify
|
||||
```
|
||||
|
||||
- To run just Apple Music artist tests
|
||||
|
||||
```console
|
||||
npm run test -- apple_music.artist
|
||||
```
|
||||
|
||||
- You can use a custom test suite (see the [default suite](https://github.com/miraclx/freyr-js/blob/master/test/default.json) for an example)
|
||||
|
||||
```console
|
||||
npm run test -- --all --suite ./special_cases.json
|
||||
```
|
||||
|
||||
- And optionally, you can run the tests inside a freyr docker container
|
||||
|
||||
```console
|
||||
npm run test -- deezer --docker freyr-dev:latest
|
||||
```
|
||||
|
||||
## `npm run test -- --help`
|
||||
|
||||
```console
|
||||
freyr-test
|
||||
----------
|
||||
Usage: freyr-test [options] [<SERVICE>[.<TYPE>]...]
|
||||
|
||||
Utility for testing the Freyr CLI
|
||||
|
||||
Options:
|
||||
|
||||
SERVICE spotify / apple_music / deezer
|
||||
TYPE track / album / artist / playlist
|
||||
|
||||
--all run all tests
|
||||
--suite <SUITE> use a specific test suite (json)
|
||||
--docker <IMAGE> run tests in a docker container
|
||||
--help show this help message
|
||||
|
||||
Enviroment Variables:
|
||||
|
||||
DOCKER_ARGS arguments to pass to `docker run`
|
||||
|
||||
Example:
|
||||
|
||||
$ freyr-test --all
|
||||
runs all tests
|
||||
|
||||
$ freyr-test spotify
|
||||
runs all Spotify tests
|
||||
|
||||
$ freyr-test apple_music.album
|
||||
tests downloading an Apple Music album
|
||||
|
||||
$ freyr-test spotify.track deezer.artist
|
||||
tests downloading a Spotify track and Deezer artist
|
||||
```
|
||||
|
|
@ -10,6 +10,9 @@
|
|||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node test/index.js"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"conf.json",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default async function genFile(opts) {
|
|||
const dir = join(opts.tmpdir, opts.dirname || '.');
|
||||
await mkdirp(dir);
|
||||
const name = join(dir, opts.filename);
|
||||
const fd = await open(name, fs.constants.O_CREAT);
|
||||
const fd = await open(name, fs.constants.O_CREAT | opts.mode);
|
||||
hookupListeners();
|
||||
let closed = false;
|
||||
const garbageHandler = () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
{
|
||||
"spotify": {
|
||||
"track": {
|
||||
"uri": "https://open.spotify.com/track/5FNS5Vj69AhRGJWjhrAd01",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [01]",
|
||||
"✓ Passed: [01]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"album": {
|
||||
"uri": "https://open.spotify.com/album/2D23kwwoy2JpZVuJwzE42B",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [04]",
|
||||
"✓ Passed: [04]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"artist": {
|
||||
"uri": "https://open.spotify.com/artist/4adSXA1GDOxNG7Zw89YHyz",
|
||||
"filter": [
|
||||
"album=\"the rainbow cassette\"",
|
||||
"album=\"make believe\""
|
||||
],
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [10]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"playlist": {
|
||||
"uri": "https://open.spotify.com/playlist/5KcCGEx7fFqXYNiJkSQ5KT",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [05]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
}
|
||||
},
|
||||
"apple_music": {
|
||||
"track": {
|
||||
"uri": "https://music.apple.com/us/album/elio-irl/1547735824?i=1547736100",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [01]",
|
||||
"✓ Passed: [01]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"album": {
|
||||
"uri": "https://music.apple.com/us/album/im-sorry-im-not-sorry-ep/1491795443",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [04]",
|
||||
"✓ Passed: [04]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"artist": {
|
||||
"uri": "https://music.apple.com/us/artist/mazie/1508029053",
|
||||
"filter": [
|
||||
"album=\"the rainbow cassette\"",
|
||||
"album=\"make believe\""
|
||||
],
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [10]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"playlist": {
|
||||
"uri": "https://music.apple.com/us/playlist/songs-from-up-next-bazzi/pl.56c4bdf909954beca0cf69379a48144f",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [06]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
}
|
||||
},
|
||||
"deezer": {
|
||||
"track": {
|
||||
"uri": "https://www.deezer.com/en/track/1189202982",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [01]",
|
||||
"✓ Passed: [01]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"album": {
|
||||
"uri": "https://www.deezer.com/en/album/123330212",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"[•] Total tracks: [04]",
|
||||
"✓ Passed: [04]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"artist": {
|
||||
"uri": "https://www.deezer.com/en/artist/14808825",
|
||||
"filter": [
|
||||
"album=\"the rainbow cassette\"",
|
||||
"album=\"make believe\""
|
||||
],
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [10]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
},
|
||||
"playlist": {
|
||||
"uri": "https://www.deezer.com/en/playlist/10004168842",
|
||||
"expect": [
|
||||
"[•] Collation Complete",
|
||||
"✓ Passed: [05]",
|
||||
"✕ Failed: [00]"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
import fs from 'fs';
|
||||
import url from 'url';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import {randomUUID} from 'crypto';
|
||||
import {PassThrough} from 'stream';
|
||||
import {spawn} from 'child_process';
|
||||
|
||||
import fileMgr from '../src/file_mgr.js';
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
function sed(fn) {
|
||||
return new PassThrough({
|
||||
write(chunk, _, cb) {
|
||||
this.buf = Buffer.concat([(this.buf ||= Buffer.alloc(0)), chunk]);
|
||||
let eol;
|
||||
while (~(eol = this.buf.indexOf(0x0a))) {
|
||||
this.push(fn(this.buf.slice(0, eol + 1)));
|
||||
this.buf = this.buf.slice(eol + 1);
|
||||
}
|
||||
cb(null);
|
||||
},
|
||||
final(cb) {
|
||||
this.push(this.buf);
|
||||
cb();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function tee(stream1, stream2) {
|
||||
let stream = new PassThrough();
|
||||
stream.pipe(stream1);
|
||||
stream.pipe(stream2);
|
||||
return stream;
|
||||
}
|
||||
|
||||
async function pRetry(tries, fn) {
|
||||
let result,
|
||||
rawErr,
|
||||
abortSymbol = Symbol('RetryAbort');
|
||||
for (let [i] of Array.apply(null, {length: tries}).entries()) {
|
||||
try {
|
||||
result = await fn(i + 1, rawErr, () => {
|
||||
throw abortSymbol;
|
||||
});
|
||||
} catch (err) {
|
||||
if (err === abortSymbol) break;
|
||||
(result = Promise.reject((rawErr = err))).catch(() => {});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function run_tests(stage, args) {
|
||||
let docker_image, i;
|
||||
if ((docker_image = !!~(i = args.indexOf('--docker'))) && !(docker_image = args.splice(i, 2)[1]))
|
||||
throw new Error('`--docker` requires an image name');
|
||||
|
||||
let is_gha = 'GITHUB_ACTIONS' in process.env && process.env['GITHUB_ACTIONS'] === 'true';
|
||||
if (~(i = args.indexOf('--all'))) args = Object.keys(stage);
|
||||
let invalidArg;
|
||||
if ((invalidArg = args.find(arg => arg.startsWith('-')))) throw new Error(`Invalid argument: ${invalidArg}`);
|
||||
|
||||
for (let [i, arg] of args.entries()) {
|
||||
let [service, type] = arg.split('.');
|
||||
if (!(service in stage)) throw new Error(`Invalid service: ${service}`);
|
||||
if (!type) {
|
||||
args.splice(i + 1, 0, ...Object.keys(stage[service]).map(type => `${arg}.${type}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
let {uri, filter = [], expect} = stage[service][type];
|
||||
|
||||
let child_args = ['--no-logo', '--no-header', '--no-bar', uri, ...filter.map(f => `--filter=${f}`)];
|
||||
|
||||
let unmetExpectations = new Error('One or more expectations failed');
|
||||
|
||||
let child_id = randomUUID();
|
||||
|
||||
await pRetry(3, async (attempt, lastErr, abort) => {
|
||||
if (attempt > 1 && lastErr !== unmetExpectations) abort();
|
||||
|
||||
let logFile = await fileMgr({
|
||||
filename: `${service}-${type}-${attempt}.log`,
|
||||
dirname: path.join('freyr-test', child_id),
|
||||
keep: true,
|
||||
mode: fs.constants.W_OK,
|
||||
});
|
||||
|
||||
logFile.stream = fs.createWriteStream(null, {fd: logFile.fd});
|
||||
|
||||
let logline = line => `│ ${line}`;
|
||||
|
||||
let raw_stdout = tee(logFile.stream, process.stdout);
|
||||
let stdout = sed(logline);
|
||||
stdout.pipe(raw_stdout);
|
||||
|
||||
let raw_stderr = tee(logFile.stream, process.stderr);
|
||||
let stderr = sed(logline);
|
||||
stderr.pipe(raw_stderr);
|
||||
|
||||
stdout.log = (...args) => void stdout.write(util.formatWithOptions({colors: true}, ...args, '\n'));
|
||||
stderr.log = (...args) => void stderr.write(util.formatWithOptions({colors: true}, ...args, '\n'));
|
||||
|
||||
if (attempt > 1)
|
||||
if (is_gha) console.log(`::warning::[${attempt}/3] Download failed, retrying..`);
|
||||
else console.log(`\x1b[33m[${attempt}/3] Download failed, retrying..\x1b[0m`);
|
||||
console.log(`Log File: ${logFile.name}`);
|
||||
|
||||
let top_bar = `┌──> ${`[${attempt}/3] ${service} ${type} `.padEnd(56, '─')}┐`;
|
||||
if (is_gha) console.log(`::group::${top_bar}`);
|
||||
else raw_stdout.write(`${top_bar}\n`);
|
||||
|
||||
let child, handler;
|
||||
|
||||
if (!docker_image) {
|
||||
child = spawn('node', [path.join(__dirname, '..', 'cli.js'), ...child_args]);
|
||||
} else {
|
||||
let extra_docker_args = process.env['DOCKER_ARGS'] ? process.env['DOCKER_ARGS'].split(' ') : [];
|
||||
child = spawn('docker', [
|
||||
'run',
|
||||
...extra_docker_args,
|
||||
'--rm',
|
||||
'-i',
|
||||
'--log-driver=none',
|
||||
'--name',
|
||||
child_id,
|
||||
docker_image,
|
||||
...child_args,
|
||||
]);
|
||||
process.on('SIGINT', (handler = () => (spawn('docker', ['kill', child_id]), process.off('SIGINT', handler))));
|
||||
}
|
||||
|
||||
let childErrors = [];
|
||||
child.on('error', err => childErrors.push(err));
|
||||
|
||||
let logs = [];
|
||||
|
||||
for (let [i, o] of [
|
||||
[child.stdout, stdout],
|
||||
[child.stderr, stderr],
|
||||
])
|
||||
i.on('data', data => {
|
||||
let line = data.toString();
|
||||
let pos;
|
||||
if (~(pos = line.indexOf('\x1b[G'))) line = line.slice(0, pos + 3) + logline(line.slice(pos + 3));
|
||||
logs.push(line);
|
||||
o.write(line);
|
||||
});
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
child.on('close', (code, err) => {
|
||||
if (docker_image) {
|
||||
process.off('SIGINT', handler);
|
||||
if (code === 137) process.exit(130);
|
||||
}
|
||||
if (code !== 0) err = new Error(`child process exited with code ${code}`);
|
||||
else if (childErrors.length) err = childErrors.shift();
|
||||
if (!err) res();
|
||||
else {
|
||||
err.code = code;
|
||||
if (childErrors.length) err[errorCauses] = childErrors;
|
||||
rej(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (is_gha && (i = logs.findIndex(line => line.includes('[•] Embedding Metadata')))) {
|
||||
console.log(`::group::├──> ${`[${attempt}/3] View Download Status `.padEnd(56, '─')}┤`);
|
||||
for (let line of logs
|
||||
.slice(i)
|
||||
.join('')
|
||||
.split('\n')
|
||||
.filter(line => line.trim().length))
|
||||
console.log(`│ ${line}`);
|
||||
console.log('::endgroup::');
|
||||
}
|
||||
|
||||
let ml = expect.reduce((a, v) => Math.max(a, v.length), 0);
|
||||
if (is_gha) console.log(`::group::├──> ${`[${attempt}/3] Verifying... `.padEnd(56, '─')}┤`);
|
||||
else raw_stdout.write(`├──> ${`[${attempt}/3] Verifying... `.padEnd(56, '─')}┤\n`);
|
||||
let as_expected = true;
|
||||
for (let expected of expect) {
|
||||
stdout.write(`• \x1b[33m${expected.padEnd(ml + 2, ' ')}\x1b[0m `);
|
||||
let this_passed;
|
||||
if ((this_passed = logs.some(line => line.match(new RegExp(expected.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')))))
|
||||
stdout.log('\x1b[32m(matched)\x1b[0m');
|
||||
else stdout.log('\x1b[31m(failed to match)\x1b[0m');
|
||||
as_expected &&= this_passed;
|
||||
}
|
||||
if (is_gha) console.log('::endgroup::');
|
||||
if (is_gha) console.log(`::group::└${'─'.repeat(top_bar.length - 2)}┘\n::endgroup::`);
|
||||
else raw_stdout.write(`└${'─'.repeat(top_bar.length - 2)}┘\n`);
|
||||
|
||||
if (!as_expected) throw unmetExpectations;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let errorCauses = Symbol('ErrorCauses');
|
||||
|
||||
function main() {
|
||||
let args = process.argv.slice(2);
|
||||
|
||||
let stage, test_suite, i;
|
||||
|
||||
if ((test_suite = !!~(i = args.indexOf('--suite'))) && !(test_suite = args.splice(i, 2)[1]))
|
||||
throw new Error('`--suite` requires a file path');
|
||||
|
||||
try {
|
||||
stage = JSON.parse(fs.readFileSync(test_suite || path.join(__dirname, 'default.json')));
|
||||
} catch (e) {
|
||||
(i = ''), (stage = JSON.parse(fs.readFileSync(path.join(__dirname, 'default.json'))));
|
||||
console.error("\x1b[33mCouldn't read test suite file\x1b[0m\n", e.message, '\n');
|
||||
}
|
||||
|
||||
if (!args.length || i === '' || args.includes('--help') || args.includes('-h')) {
|
||||
console.log('freyr-test');
|
||||
console.log('----------');
|
||||
console.log('Usage: freyr-test [options] [<SERVICE>[.<TYPE>]...]');
|
||||
console.log();
|
||||
console.log('Utility for testing the Freyr CLI');
|
||||
console.log();
|
||||
console.log('Options:');
|
||||
console.log();
|
||||
console.log(` SERVICE ${Object.keys(stage).join(' / ')}`);
|
||||
console.log(` TYPE ${[...new Set(Object.values(stage).flatMap(s => Object.keys(s)))].join(' / ')}`);
|
||||
console.log();
|
||||
console.log(' --all run all tests');
|
||||
console.log(' --suite <SUITE> use a specific test suite (json)');
|
||||
console.log(' --docker <IMAGE> run tests in a docker container');
|
||||
console.log(' --help show this help message');
|
||||
console.log();
|
||||
console.log('Enviroment Variables:');
|
||||
console.log();
|
||||
console.log(' DOCKER_ARGS arguments to pass to `docker run`');
|
||||
console.log();
|
||||
console.log('Example:');
|
||||
console.log();
|
||||
console.log(' $ freyr-test --all');
|
||||
console.log(' runs all tests');
|
||||
console.log();
|
||||
console.log(' $ freyr-test spotify');
|
||||
console.log(' runs all Spotify tests');
|
||||
console.log();
|
||||
console.log(' $ freyr-test apple_music.album');
|
||||
console.log(' tests downloading an Apple Music album');
|
||||
console.log();
|
||||
console.log(' $ freyr-test spotify.track deezer.artist');
|
||||
console.log(' tests downloading a Spotify track and Deezer artist');
|
||||
return;
|
||||
}
|
||||
|
||||
run_tests(stage, args).catch(err => {
|
||||
console.error('An error occurred!');
|
||||
if (errorCauses in err) {
|
||||
let causes = err[errorCauses];
|
||||
delete err[errorCauses];
|
||||
console.error('', err);
|
||||
for (let cause of causes) console.error('', cause);
|
||||
} else console.error('', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in New Issue