mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-15 11:17:58 +01:00
Merge branch 'develop' into dependabot/github_actions/nixbuild/nix-quick-install-action-33
This commit is contained in:
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
rust: [default, msrv]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v33
|
||||
@@ -32,9 +32,12 @@ jobs:
|
||||
- name: Build
|
||||
run: nix develop .#${{ matrix.rust }} --command cargo build --verbose
|
||||
|
||||
- name: Run tests
|
||||
- name: Run cargo tests
|
||||
run: nix develop .#${{ matrix.rust }} --command cargo test --verbose
|
||||
|
||||
- name: Run integration tests
|
||||
run: nix develop .#${{ matrix.rust }} --command tests/integration.sh
|
||||
|
||||
- name: Check formatting
|
||||
run: nix develop .#${{ matrix.rust }} --command cargo fmt --check
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Create the release
|
||||
env:
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
CARGO: cargo
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
47
Cargo.lock
generated
47
Cargo.lock
generated
@@ -84,7 +84,7 @@ checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "asciinema"
|
||||
version = "3.0.0-rc.5"
|
||||
version = "3.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -813,11 +813,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1060,27 +1060,6 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
@@ -1089,15 +1068,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@@ -1478,9 +1451,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -1779,13 +1752,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
[package]
|
||||
name = "asciinema"
|
||||
version = "3.0.0-rc.5"
|
||||
version = "3.0.0"
|
||||
edition = "2021"
|
||||
authors = ["Marcin Kulik <m@ku1ik.com>"]
|
||||
homepage = "https://asciinema.org"
|
||||
repository = "https://github.com/asciinema/asciinema"
|
||||
description = "Terminal session recorder"
|
||||
description = "Terminal session recorder, streamer, and player"
|
||||
license = "GPL-3.0"
|
||||
|
||||
# MSRV
|
||||
rust-version = "1.75.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
nix = { version = "0.30", features = ["fs", "term", "process", "signal", "poll"] }
|
||||
@@ -34,7 +32,7 @@ tokio-stream = { version = "0.1", default-features = false, features = ["sync",
|
||||
rust-embed = "8.0"
|
||||
tower-http = { version = "0.6", features = ["trace"] }
|
||||
tracing = { version = "0.1", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.19", default-features = false, features = ["fmt", "env-filter"] }
|
||||
tracing-subscriber = { version = "0.3.20", default-features = false, features = ["fmt", "env-filter"] }
|
||||
rgb = { version = "0.8", default-features = false }
|
||||
url = "2.5"
|
||||
tokio-tungstenite = { version = "0.26", default-features = false, features = ["connect", "rustls-tls-native-roots"] }
|
||||
|
||||
@@ -22,7 +22,7 @@ let
|
||||
})
|
||||
];
|
||||
|
||||
buildInputs = [ pkgs.bashInteractive ];
|
||||
packages = [ pkgs.shellcheck ];
|
||||
|
||||
env.RUST_BACKTRACE = 1;
|
||||
};
|
||||
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/minimal.json").unwrap();
|
||||
} = super::open_from_path("tests/casts/minimal-v1.json").unwrap();
|
||||
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
@@ -262,7 +262,7 @@ mod tests {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/full.json").unwrap();
|
||||
} = super::open_from_path("tests/casts/full-v1.json").unwrap();
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!(version, 1);
|
||||
|
||||
@@ -81,9 +81,9 @@ pub async fn run<S: AsRef<str>, T: Tty + ?Sized, N: Notifier>(
|
||||
let (events_tx, events_rx) = mpsc::channel::<Event>(1024);
|
||||
let winsize = tty.get_size();
|
||||
let pty = pty::spawn(command, winsize, extra_env)?;
|
||||
tokio::spawn(forward_events(events_rx, outputs));
|
||||
let forwarder = tokio::spawn(forward_events(events_rx, outputs));
|
||||
|
||||
let mut session = Session {
|
||||
let session = Session {
|
||||
epoch,
|
||||
events_tx,
|
||||
input_decoder: Utf8Decoder::new(),
|
||||
@@ -97,7 +97,10 @@ pub async fn run<S: AsRef<str>, T: Tty + ?Sized, N: Notifier>(
|
||||
tty_size: winsize.into(),
|
||||
};
|
||||
|
||||
session.run(pty, tty).await
|
||||
let result = session.run(pty, tty).await;
|
||||
let _ = forwarder.await;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn forward_events(mut events_rx: mpsc::Receiver<Event>, outputs: Vec<Box<dyn Output>>) {
|
||||
@@ -131,7 +134,7 @@ async fn forward_event(mut output: Box<dyn Output>, event: Event) -> Option<Box<
|
||||
}
|
||||
|
||||
impl<N: Notifier> Session<N> {
|
||||
async fn run<T: Tty + ?Sized>(&mut self, pty: Pty, tty: &mut T) -> anyhow::Result<i32> {
|
||||
async fn run<T: Tty + ?Sized>(mut self, pty: Pty, tty: &mut T) -> anyhow::Result<i32> {
|
||||
let mut signals =
|
||||
Signals::new([SIGWINCH, SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGALRM, SIGCHLD])?;
|
||||
let mut output_buf = [0u8; BUF_SIZE];
|
||||
@@ -202,8 +205,16 @@ impl<N: Notifier> Session<N> {
|
||||
}
|
||||
}
|
||||
|
||||
while let Ok(n) = pty.read(&mut output_buf).await {
|
||||
if n > 0 {
|
||||
self.handle_output(&output_buf[..n]).await;
|
||||
output.extend_from_slice(&output_buf[0..n]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !output.is_empty() {
|
||||
self.handle_output(&output).await;
|
||||
let _ = tty.write_all(&output).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly DISTROS=(
|
||||
'arch'
|
||||
'alpine'
|
||||
'centos'
|
||||
'debian'
|
||||
'fedora'
|
||||
'ubuntu'
|
||||
)
|
||||
|
||||
readonly DOCKER='docker'
|
||||
|
||||
# do not redefine builtin `test`
|
||||
test_() {
|
||||
local -r tag="${1}"
|
||||
|
||||
local -ra docker_opts=(
|
||||
"--tag=asciinema/asciinema:${tag}"
|
||||
"--file=tests/distros/Dockerfile.${tag}"
|
||||
)
|
||||
|
||||
printf "\e[1;32mTesting on %s...\e[0m\n\n" "${tag}"
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
"${DOCKER}" build ${docker_opts[@]} .
|
||||
|
||||
"${DOCKER}" run --rm -it "asciinema/asciinema:${tag}" tests/integration.sh
|
||||
}
|
||||
|
||||
|
||||
for distro in "${DISTROS[@]}"; do
|
||||
test_ "${distro}"
|
||||
done
|
||||
|
||||
printf "\n\e[1;32mAll tests passed.\e[0m\n"
|
||||
@@ -1,19 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
FROM docker.io/library/alpine:3.15
|
||||
|
||||
# https://github.com/actions/runner/issues/241
|
||||
RUN apk --no-cache add bash ca-certificates make python3 util-linux
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,22 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
FROM docker.io/library/archlinux:latest
|
||||
|
||||
RUN pacman-key --init \
|
||||
&& pacman --sync --refresh --sysupgrade --noconfirm make python3 \
|
||||
&& printf "LANG=en_US.UTF-8\n" > /etc/locale.conf \
|
||||
&& locale-gen \
|
||||
&& pacman --sync --clean --clean --noconfirm
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,18 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
FROM docker.io/library/centos:7
|
||||
|
||||
RUN yum install -y epel-release && yum install -y make python36 && yum clean all
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,33 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
FROM docker.io/library/debian:bullseye
|
||||
|
||||
ENV DEBIAN_FRONTENT="noninteractive"
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
ca-certificates \
|
||||
locales \
|
||||
make \
|
||||
procps \
|
||||
python3 \
|
||||
&& localedef \
|
||||
-i en_US \
|
||||
-c \
|
||||
-f UTF-8 \
|
||||
-A /usr/share/locale/locale.alias \
|
||||
en_US.UTF-8 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENV SHELL="/bin/bash"
|
||||
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,20 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
# https://medium.com/nttlabs/ubuntu-21-10-and-fedora-35-do-not-work-on-docker-20-10-9-1cd439d9921
|
||||
# https://www.mail-archive.com/ubuntu-bugs@lists.ubuntu.com/msg5971024.html
|
||||
FROM registry.fedoraproject.org/fedora:34
|
||||
|
||||
RUN dnf install -y make python3 procps && dnf clean all
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
ENV SHELL="/bin/bash"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,32 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
FROM docker.io/library/ubuntu:20.04
|
||||
|
||||
ENV DEBIAN_FRONTENT="noninteractive"
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
ca-certificates \
|
||||
locales \
|
||||
make \
|
||||
python3 \
|
||||
&& localedef \
|
||||
-i en_US \
|
||||
-c \
|
||||
-f UTF-8 \
|
||||
-A /usr/share/locale/locale.alias \
|
||||
en_US.UTF-8 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY asciinema/ asciinema/
|
||||
COPY tests/ tests/
|
||||
|
||||
ENV LANG="en_US.utf8"
|
||||
|
||||
USER nobody
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
# vim:ft=dockerfile
|
||||
@@ -1,104 +1,451 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eExuo pipefail
|
||||
set -eEuo pipefail -o errtrace
|
||||
|
||||
if ! command -v "pkill" >/dev/null 2>&1; then
|
||||
printf "error: pkill not installed\n"
|
||||
exit 1
|
||||
# Colors for output (disabled if no TTY or NO_COLOR set)
|
||||
if [[ ! -t 1 ]] || [[ -n "${NO_COLOR:-}" ]]; then
|
||||
RED=""
|
||||
GREEN=""
|
||||
YELLOW=""
|
||||
BLUE=""
|
||||
NC=""
|
||||
else
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
fi
|
||||
|
||||
python3 -V
|
||||
# Test tracking
|
||||
TESTS_RUN=0
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
ASCIINEMA_CONFIG_HOME="$(
|
||||
mktemp -d 2>/dev/null || mktemp -d -t asciinema-config-home
|
||||
)"
|
||||
|
||||
export ASCIINEMA_CONFIG_HOME
|
||||
|
||||
TMP_DATA_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t asciinema-data-dir)"
|
||||
|
||||
trap 'rm -rf ${ASCIINEMA_CONFIG_HOME} ${TMP_DATA_DIR}' EXIT
|
||||
|
||||
asciinema() {
|
||||
python3 -m asciinema "${@}"
|
||||
# Helper functions
|
||||
log_info() {
|
||||
printf "%b\n" "${BLUE}INFO:${NC} $*"
|
||||
}
|
||||
|
||||
## disable notifications
|
||||
log_success() {
|
||||
printf "%b\n" "${GREEN}PASS:${NC} $*"
|
||||
((TESTS_PASSED++))
|
||||
}
|
||||
|
||||
printf "[notifications]\nenabled = no\n" >> "${ASCIINEMA_CONFIG_HOME}/config"
|
||||
log_error() {
|
||||
printf "%b\n" "${RED}FAIL:${NC} $*"
|
||||
((TESTS_FAILED++))
|
||||
}
|
||||
|
||||
## test help message
|
||||
log_warning() {
|
||||
printf "%b\n" "${YELLOW}WARN:${NC} $*"
|
||||
}
|
||||
|
||||
asciinema -h
|
||||
assert_exit_code() {
|
||||
local expected=$1
|
||||
local actual=$2
|
||||
local test_name=$3
|
||||
|
||||
((TESTS_RUN++))
|
||||
if [[ $actual -eq $expected ]]; then
|
||||
log_success "$test_name - exit code $actual"
|
||||
else
|
||||
log_error "$test_name - expected exit code $expected, got $actual"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
## test version command
|
||||
assert_file_exists() {
|
||||
local file=$1
|
||||
local test_name=$2
|
||||
|
||||
((TESTS_RUN++))
|
||||
if [[ -f "$file" ]]; then
|
||||
log_success "$test_name - file exists: $file"
|
||||
else
|
||||
log_error "$test_name - file does not exist: $file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
asciinema --version
|
||||
assert_file_not_empty() {
|
||||
local file=$1
|
||||
local test_name=$2
|
||||
|
||||
((TESTS_RUN++))
|
||||
if [[ -s "$file" ]]; then
|
||||
log_success "$test_name - file not empty: $file"
|
||||
else
|
||||
log_error "$test_name - file is empty: $file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
## test auth command
|
||||
assert_output_contains() {
|
||||
local expected=$1
|
||||
local output=$2
|
||||
local test_name=$3
|
||||
|
||||
((TESTS_RUN++))
|
||||
if echo "$output" | grep -q "$expected"; then
|
||||
log_success "$test_name - output contains: $expected"
|
||||
else
|
||||
log_error "$test_name - output missing: $expected"
|
||||
log_error "Actual output: $output"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
asciinema auth
|
||||
assert_file_contains() {
|
||||
local expected=$1
|
||||
local file=$2
|
||||
local test_name=$3
|
||||
|
||||
((TESTS_RUN++))
|
||||
if grep -q "$expected" "$file"; then
|
||||
log_success "$test_name - file contains: $expected"
|
||||
else
|
||||
log_error "$test_name - file missing: $expected"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
## test play command
|
||||
# SETUP
|
||||
setup() {
|
||||
log_info "Setting up test environment..."
|
||||
|
||||
ASCIINEMA_CONFIG_HOME="$(
|
||||
mktemp -d 2>/dev/null || mktemp -d -t asciinema-config-home
|
||||
)"
|
||||
|
||||
# asciicast v1
|
||||
asciinema play -s 5 tests/demo.json
|
||||
asciinema play -s 5 -i 0.2 tests/demo.json
|
||||
# shellcheck disable=SC2002
|
||||
cat tests/demo.json | asciinema play -s 5 -
|
||||
ASCIINEMA_STATE_HOME="$(
|
||||
mktemp -d 2>/dev/null || mktemp -d -t asciinema-state-home
|
||||
)"
|
||||
|
||||
# asciicast v2
|
||||
asciinema play -s 5 tests/demo.cast
|
||||
asciinema play -s 5 -i 0.2 tests/demo.cast
|
||||
# shellcheck disable=SC2002
|
||||
cat tests/demo.cast | asciinema play -s 5 -
|
||||
ASCIINEMA_GEN_DIR="$(
|
||||
mktemp -d 2>/dev/null || mktemp -d -t asciinema-gen-dir
|
||||
)"
|
||||
|
||||
## test cat command
|
||||
export ASCIINEMA_CONFIG_HOME ASCIINEMA_STATE_HOME ASCIINEMA_GEN_DIR
|
||||
export ASCIINEMA_SERVER_URL=https://asciinema.example.com
|
||||
|
||||
# asciicast v1
|
||||
asciinema cat tests/demo.json
|
||||
# shellcheck disable=SC2002
|
||||
cat tests/demo.json | asciinema cat -
|
||||
TMP_DATA_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t asciinema-data-dir)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FIXTURES="$SCRIPT_DIR/casts"
|
||||
ASCIINEMA_BIN="$SCRIPT_DIR/../target/release/asciinema"
|
||||
|
||||
# asciicast v2
|
||||
asciinema cat tests/demo.cast
|
||||
# shellcheck disable=SC2002
|
||||
cat tests/demo.cast | asciinema cat -
|
||||
trap 'cleanup' EXIT
|
||||
|
||||
## test rec command
|
||||
log_info "Building release binary..."
|
||||
cargo build --release --locked
|
||||
|
||||
# normal program
|
||||
asciinema rec -c 'bash -c "echo t3st; sleep 2; echo ok"' "${TMP_DATA_DIR}/1a.cast"
|
||||
grep '"o",' "${TMP_DATA_DIR}/1a.cast"
|
||||
# disable notifications
|
||||
printf "[notifications]\nenabled = false\n" >> "${ASCIINEMA_CONFIG_HOME}/config.toml"
|
||||
|
||||
log_info "Setup complete"
|
||||
}
|
||||
|
||||
# very quickly exiting program
|
||||
asciinema rec -c whoami "${TMP_DATA_DIR}/1b.cast"
|
||||
grep '"o",' "${TMP_DATA_DIR}/1b.cast"
|
||||
cleanup() {
|
||||
log_info "Cleaning up..."
|
||||
rm -rf "${ASCIINEMA_CONFIG_HOME:-}" "${ASCIINEMA_STATE_HOME:-}" "${ASCIINEMA_GEN_DIR:-}" "${TMP_DATA_DIR:-}"
|
||||
}
|
||||
|
||||
# signal handling
|
||||
bash -c "sleep 1; pkill -28 -n -f 'm asciinema'" &
|
||||
asciinema rec -c 'bash -c "echo t3st; sleep 2; echo ok"' "${TMP_DATA_DIR}/2.cast"
|
||||
# Test runner function
|
||||
run_test() {
|
||||
local test_name="$1"
|
||||
shift
|
||||
|
||||
if [[ -z "${TEST:-}" || "${TEST:-}" == "$test_name" ]]; then
|
||||
echo
|
||||
echo "#################### TEST $test_name ####################"
|
||||
"$@" || true # Don't exit on test failure
|
||||
fi
|
||||
}
|
||||
|
||||
bash -c "sleep 1; pkill -n -f 'bash -c echo t3st'" &
|
||||
asciinema rec -c 'bash -c "echo t3st; sleep 2; echo ok"' "${TMP_DATA_DIR}/3.cast"
|
||||
# TEST FUNCTIONS
|
||||
|
||||
bash -c "sleep 1; pkill -9 -n -f 'bash -c echo t3st'" &
|
||||
asciinema rec -c 'bash -c "echo t3st; sleep 2; echo ok"' "${TMP_DATA_DIR}/4.cast"
|
||||
test_help() {
|
||||
log_info "Testing help command..."
|
||||
|
||||
# Test short help
|
||||
local output rc
|
||||
if output=$("$ASCIINEMA_BIN" -h 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "help short flag"
|
||||
assert_output_contains "Terminal session recorder" "$output" "help content"
|
||||
assert_output_contains "Commands:" "$output" "help shows commands"
|
||||
|
||||
# Test long help
|
||||
if output=$("$ASCIINEMA_BIN" --help 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "help long flag"
|
||||
assert_output_contains "Terminal session recorder" "$output" "help content"
|
||||
|
||||
# Test help subcommand
|
||||
if output=$("$ASCIINEMA_BIN" help 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "help subcommand"
|
||||
assert_output_contains "Terminal session recorder" "$output" "help subcommand content"
|
||||
}
|
||||
|
||||
# with stdin recording
|
||||
echo "ls" | asciinema rec --stdin -c 'bash -c "sleep 1"' "${TMP_DATA_DIR}/5.cast"
|
||||
cat "${TMP_DATA_DIR}/5.cast"
|
||||
grep '"i", "ls\\n"' "${TMP_DATA_DIR}/5.cast"
|
||||
grep '"o",' "${TMP_DATA_DIR}/5.cast"
|
||||
test_version() {
|
||||
log_info "Testing version command..."
|
||||
|
||||
# Test short version
|
||||
local output rc
|
||||
if output=$("$ASCIINEMA_BIN" -V 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "version short flag"
|
||||
assert_output_contains "asciinema" "$output" "version output format"
|
||||
|
||||
# Test long version
|
||||
if output=$("$ASCIINEMA_BIN" --version 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "version long flag"
|
||||
assert_output_contains "asciinema" "$output" "version output format"
|
||||
}
|
||||
|
||||
# raw output recording
|
||||
asciinema rec --raw -c 'bash -c "echo t3st; sleep 1; echo ok"' "${TMP_DATA_DIR}/6.raw"
|
||||
test_auth() {
|
||||
log_info "Testing auth command..."
|
||||
|
||||
# Test auth command (should handle offline gracefully)
|
||||
local output rc
|
||||
if output=$("$ASCIINEMA_BIN" auth 2>&1); then rc=0; else rc=$?; fi
|
||||
|
||||
# Auth should complete without hanging and show expected message
|
||||
assert_exit_code 0 "$rc" "auth"
|
||||
assert_output_contains "Open the following URL in a web browser" "$output" "auth command output"
|
||||
}
|
||||
|
||||
# appending to existing recording
|
||||
asciinema rec -c 'echo allright!; sleep 0.1' "${TMP_DATA_DIR}/7.cast"
|
||||
asciinema rec --append -c uptime "${TMP_DATA_DIR}/7.cast"
|
||||
test_record() {
|
||||
log_info "Testing record command..."
|
||||
|
||||
# Test basic recording
|
||||
local file1="$TMP_DATA_DIR/record_basic.cast"
|
||||
local rc
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "hello world"' --return "$file1"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record basic"
|
||||
assert_file_contains '"o",' "$file1" "record output event"
|
||||
assert_file_contains 'hello world' "$file1" "record output content"
|
||||
|
||||
# Test different formats
|
||||
local file2="$TMP_DATA_DIR/record_v2.cast"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "test v2"' --output-format asciicast-v2 --return "$file2"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record v2 format"
|
||||
assert_file_not_empty "$file2" "record v2 format"
|
||||
|
||||
local file3="$TMP_DATA_DIR/record_v3.cast"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "test v3"' --output-format asciicast-v3 --return "$file3"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record v3 format"
|
||||
assert_file_not_empty "$file3" "record v3 format"
|
||||
|
||||
# Test raw format
|
||||
local file4="$TMP_DATA_DIR/record_raw.raw"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "test raw"' --output-format raw --return "$file4"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record raw format"
|
||||
assert_file_not_empty "$file4" "record raw format"
|
||||
|
||||
# Test txt format
|
||||
local file5="$TMP_DATA_DIR/record_txt.txt"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "test txt"' --output-format txt --return "$file5"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record txt format"
|
||||
assert_file_not_empty "$file5" "record txt format"
|
||||
|
||||
# Test return flag with failure
|
||||
local file6="$TMP_DATA_DIR/record_fail.cast"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'exit 42' --return "$file6"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 42 "$rc" "record return flag with failure"
|
||||
assert_file_not_empty "$file6" "record failure"
|
||||
|
||||
# Test append mode
|
||||
local file7="$TMP_DATA_DIR/record_append.cast"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "first"' --return "$file7"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record append setup"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "second"' --append --return "$file7"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record append"
|
||||
assert_file_contains 'first' "$file7" "record append first content"
|
||||
assert_file_contains 'second' "$file7" "record append second content"
|
||||
|
||||
# Test idle time limits
|
||||
local file8="$TMP_DATA_DIR/record_idle.cast"
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'bash -c "echo start; sleep 2; echo end"' --idle-time-limit 1 --return "$file8"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "record idle time limit"
|
||||
assert_file_not_empty "$file8" "record idle time limit"
|
||||
}
|
||||
|
||||
# adding a marker
|
||||
printf "[record]\nadd_marker_key = C-b\n" >> "${ASCIINEMA_CONFIG_HOME}/config"
|
||||
(bash -c "sleep 1; printf '.'; sleep 0.5; printf '\x08'; sleep 0.5; printf '\x02'; sleep 0.5; printf '\x04'") | asciinema rec -c /bin/bash "${TMP_DATA_DIR}/8.cast"
|
||||
grep '"m",' "${TMP_DATA_DIR}/8.cast"
|
||||
test_stream() {
|
||||
log_info "Testing stream command..."
|
||||
|
||||
# Test local streaming
|
||||
timeout 10s "$ASCIINEMA_BIN" stream --headless --local 127.0.0.1:8081 --command 'bash -c "echo streaming test; sleep 3; echo done"' --return &
|
||||
local stream_pid=$!
|
||||
|
||||
# Wait a moment for server to start
|
||||
sleep 1
|
||||
|
||||
# Test if HTTP server is responding and serving the player
|
||||
local curl_output rc
|
||||
if curl_output=$(curl -fsS "http://127.0.0.1:8081" 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "stream server responding"
|
||||
assert_output_contains "AsciinemaPlayer" "$curl_output" "stream server AsciinemaPlayer content"
|
||||
|
||||
# Clean up
|
||||
kill $stream_pid 2>/dev/null || true
|
||||
wait $stream_pid 2>/dev/null || true
|
||||
}
|
||||
|
||||
test_session() {
|
||||
log_info "Testing session command..."
|
||||
|
||||
# Test session with file output
|
||||
local file1="$TMP_DATA_DIR/session_basic.cast"
|
||||
local rc
|
||||
if "$ASCIINEMA_BIN" session --headless --output-file "$file1" --command 'echo "session test"' --return; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "session basic"
|
||||
assert_file_contains 'session test' "$file1" "session output content"
|
||||
|
||||
# Test session with return flag failure
|
||||
local file2="$TMP_DATA_DIR/session_fail.cast"
|
||||
if "$ASCIINEMA_BIN" session --headless --output-file "$file2" --command 'exit 13' --return; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 13 "$rc" "session return flag with failure"
|
||||
assert_file_contains '"x", "13"' "$file2" "session exit event"
|
||||
|
||||
# Test session with local streaming + file output
|
||||
local file3="$TMP_DATA_DIR/session_stream.cast"
|
||||
timeout 8s "$ASCIINEMA_BIN" session --headless --output-file "$file3" --stream-local 127.0.0.1:8081 --command 'bash -c "echo stream session; sleep 3; echo done"' --return &
|
||||
local session_pid=$!
|
||||
|
||||
# Wait a moment for server to start
|
||||
sleep 1
|
||||
|
||||
# Test if both file and HTTP server work
|
||||
local curl_output
|
||||
if curl_output=$(curl -fsS "http://127.0.0.1:8081" 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "stream server responding"
|
||||
assert_output_contains "AsciinemaPlayer" "$curl_output" "session streaming server AsciinemaPlayer content"
|
||||
|
||||
# Clean up and check file
|
||||
kill $session_pid 2>/dev/null || true
|
||||
wait $session_pid 2>/dev/null || true
|
||||
|
||||
if [[ -f "$file3" ]]; then
|
||||
assert_file_contains 'stream session' "$file3" "session output content"
|
||||
fi
|
||||
|
||||
# Test different output formats
|
||||
local file4="$TMP_DATA_DIR/session_v2.cast"
|
||||
if "$ASCIINEMA_BIN" session --headless --output-file "$file4" --output-format asciicast-v2 --command 'echo "session v2"' --return; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "session v2 format"
|
||||
assert_file_contains 'session v2' "$file4" "session output content"
|
||||
|
||||
# Test append mode
|
||||
local file5="$TMP_DATA_DIR/session_append.cast"
|
||||
if "$ASCIINEMA_BIN" session --headless --output-file "$file5" --command 'echo "first session"' --return; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "session append setup"
|
||||
if "$ASCIINEMA_BIN" session --headless --output-file "$file5" --append --command 'echo "second session"' --return; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "session append"
|
||||
assert_file_contains 'first session' "$file5" "session append first content"
|
||||
assert_file_contains 'second session' "$file5" "session append second content"
|
||||
}
|
||||
|
||||
test_cat() {
|
||||
log_info "Testing cat command..."
|
||||
|
||||
# Create test recordings first
|
||||
local file1="$TMP_DATA_DIR/cat_input1.cast"
|
||||
local file2="$TMP_DATA_DIR/cat_input2.cast"
|
||||
local rc
|
||||
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "first recording"' --return "$file1"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "cat setup first recording"
|
||||
|
||||
if "$ASCIINEMA_BIN" record --headless --command 'echo "second recording"' --return "$file2"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "cat setup second recording"
|
||||
|
||||
# Test concatenation to stdout
|
||||
local output
|
||||
if output=$("$ASCIINEMA_BIN" cat "$file1" "$file2" 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "cat concatenate"
|
||||
assert_output_contains 'first recording' "$output" "cat first content"
|
||||
assert_output_contains 'second recording' "$output" "cat second content"
|
||||
|
||||
# Test with different format inputs (using fixtures, v2+v3 only since v1 can't be concatenated)
|
||||
if output=$("$ASCIINEMA_BIN" cat "$FIXTURES/minimal-v2.cast" "$FIXTURES/minimal-v3.cast" 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "cat mixed formats"
|
||||
assert_output_contains '"version":' "$output" "cat mixed formats output"
|
||||
}
|
||||
|
||||
test_convert() {
|
||||
log_info "Testing convert command..."
|
||||
|
||||
# Test v1 to v3 conversion
|
||||
local file1="$TMP_DATA_DIR/convert_v1_to_v3.cast"
|
||||
local rc
|
||||
if "$ASCIINEMA_BIN" convert "$FIXTURES/minimal-v1.json" "$file1"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert v1 to v3"
|
||||
assert_file_contains '"version":3' "$file1" "convert v1 to v3 version"
|
||||
|
||||
# Test v2 to v3 conversion
|
||||
local file2="$TMP_DATA_DIR/convert_v2_to_v3.cast"
|
||||
if "$ASCIINEMA_BIN" convert "$FIXTURES/minimal-v2.cast" "$file2"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert v2 to v3"
|
||||
assert_file_contains '"version":3' "$file2" "convert v2 to v3 version"
|
||||
|
||||
# Test to raw format
|
||||
local file3="$TMP_DATA_DIR/convert_to_raw.raw"
|
||||
if "$ASCIINEMA_BIN" convert --output-format raw "$FIXTURES/minimal-v2.cast" "$file3"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert to raw"
|
||||
assert_file_exists "$file3" "convert to raw output"
|
||||
|
||||
# Test to txt format
|
||||
local file4="$TMP_DATA_DIR/convert_to_txt.txt"
|
||||
if "$ASCIINEMA_BIN" convert --output-format txt "$FIXTURES/minimal-v2.cast" "$file4"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert to txt"
|
||||
assert_file_exists "$file4" "convert to txt output"
|
||||
|
||||
# Test output to stdout
|
||||
local output
|
||||
if output=$("$ASCIINEMA_BIN" convert "$FIXTURES/minimal-v2.cast" - 2>&1); then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert to stdout"
|
||||
assert_output_contains '"version":3' "$output" "convert stdout version"
|
||||
|
||||
# Test overwrite behavior
|
||||
local file5="$TMP_DATA_DIR/convert_overwrite.cast"
|
||||
echo "existing content" > "$file5"
|
||||
if "$ASCIINEMA_BIN" convert --overwrite "$FIXTURES/minimal-v2.cast" "$file5"; then rc=0; else rc=$?; fi
|
||||
assert_exit_code 0 "$rc" "convert overwrite"
|
||||
assert_file_contains '"version":3' "$file5" "convert overwrite content"
|
||||
}
|
||||
|
||||
# MAIN EXECUTION
|
||||
|
||||
# Setup always runs
|
||||
setup
|
||||
|
||||
echo
|
||||
echo "######################################################"
|
||||
echo "# ASCIINEMA CLI INTEGRATION TESTS"
|
||||
echo "######################################################"
|
||||
echo "# Test filter: ${TEST:-ALL}"
|
||||
echo "######################################################"
|
||||
|
||||
# Individual test blocks
|
||||
run_test "help" test_help
|
||||
run_test "version" test_version
|
||||
run_test "auth" test_auth
|
||||
run_test "record" test_record
|
||||
run_test "stream" test_stream
|
||||
run_test "session" test_session
|
||||
run_test "cat" test_cat
|
||||
run_test "convert" test_convert
|
||||
|
||||
# Final summary
|
||||
echo
|
||||
echo "######################################################"
|
||||
echo "# TEST SUMMARY"
|
||||
echo "######################################################"
|
||||
echo "Tests run: $TESTS_RUN"
|
||||
printf "%bTests passed: %b%s%b\n" "" "${GREEN}" "$TESTS_PASSED" "${NC}"
|
||||
if [[ $TESTS_FAILED -gt 0 ]]; then
|
||||
printf "%bTests failed: %b%s%b\n" "" "${RED}" "$TESTS_FAILED" "${NC}"
|
||||
echo "OVERALL RESULT: FAILED"
|
||||
exit 1
|
||||
else
|
||||
printf "%bTests failed: %b%s%b\n" "" "${GREEN}" "0" "${NC}"
|
||||
echo "OVERALL RESULT: SUCCESS"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user