public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH] demo, ci: Switch to asciinema(1) for terminal recordings
@ 2022-02-22 17:33 Stefano Brivio
  0 siblings, 0 replies; only message in thread
From: Stefano Brivio @ 2022-02-22 17:33 UTC (permalink / raw)
  To: passt-dev

[-- Attachment #1: Type: text/plain, Size: 15303 bytes --]

For demos, cool-retro-term(1) looked fancier, but several threads of
that and ffmpeg(1) are just messing up with performance testing.

The CI videos started getting really big as well, and they were
difficult to read.

Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
 README.md            |  52 +++++++++++++++------
 test/README.md       |  26 ++++-------
 test/lib/perf_report |  17 ++++---
 test/lib/term        |  27 ++++-------
 test/lib/video       | 107 ++++++++++++++++++++++++++-----------------
 test/run             |  16 +++----
 6 files changed, 139 insertions(+), 106 deletions(-)

diff --git a/README.md b/README.md
index 16e91b9..9c71c13 100644
--- a/README.md
+++ b/README.md
@@ -398,28 +398,52 @@ is fully configurable with command line options.
 
 ### pasta
 
+<link rel="stylesheet" type="text/css" href="/static/asciinema-player.css" />
+<script src="/static/asciinema-player.min.js"></script>
+
+<div style="display: grid; grid-template-columns: 1fr 1fr;">
+  <div>
+    <div id="demo_pasta" style="width: 100%; height: auto;"></div>
+    <div>use pasta to create and connect a namespace</div>
+  </div>
+</div>
 <div style="display: grid; grid-template-columns: 1fr 1fr;">
-  <div><video id="demo_pasta_video" style="width: 100%; height: auto;" controls>
-    <source src="/builds/latest/web/demo_pasta.webm" type="video/webm">
-  </video>use pasta to create and connect a namespace</div>
-  <div><video id="demo_podman_video" style="width: 100%; height: auto;" controls>
-    <source src="/builds/latest/web/demo_podman.webm" type="video/webm">
-  </video>use Podman with pasta</div>
+  <div>
+    <div id="demo_podman" style="width: 100%; height: auto;"></div>
+    <div>use Podman with pasta</div>
 </div>
+<script>
+demo_pasta_player = AsciinemaPlayer.create('/builds/latest/web/demo_pasta.cast',
+					   document.getElementById('demo_pasta'),
+					   { cols: 130, rows: 40, preload: true }
+					   );
+
+demo_podman_player = AsciinemaPlayer.create('/builds/latest/web/demo_podman.cast',
+					    document.getElementById('demo_podman'),
+					   { cols: 130, rows: 40, preload: true }
+					   );
+</script>
 
 ### passt
 
-<p><video id="demo_passt_video" style="width: 70%; height: auto; max-height: 90%" controls>
- <source src="/builds/latest/web/demo_passt.webm" type="video/webm">
-</video></p>
+<div id="demo_passt" style="width: 70%; height: auto; max-height: 90%"></div>
+<script>
+demo_passt_player = AsciinemaPlayer.create('/builds/latest/web/demo_passt.cast',
+					   document.getElementById('demo_passt'),
+					   { cols: 130, rows: 40, preload: true }
+					  );
+</script>
 
 ## Continuous Integration
 
-<p><video id="ci_video" style="width: 90%; height: auto; max-height: 90%" controls>
- <source src="/builds/latest/web/ci.webm" type="video/webm">
-</video></p>
-
-<script src="/builds/latest/web/ci.js"></script>
+<div id="ci" style="width: 90%; height: auto; max-height: 90%"></div>
+<script>
+ci_player = AsciinemaPlayer.create('/builds/latest/web/ci.cast',
+				   document.getElementById('ci'),
+				   { cols: 240, rows: 50, preload: true }
+				  );
+</script>
+<div><script src="/builds/latest/web/ci.js"></script></div>
 
 Test logs [here](/builds/latest/test/).
 
diff --git a/test/README.md b/test/README.md
index 647100f..88270e8 100644
--- a/test/README.md
+++ b/test/README.md
@@ -49,21 +49,13 @@ as needed, so there's no need to actually install it.
 
 ### Special requirements for continuous integration and demo modes
 
-Running the test suite as continuous integration or demo modes will record a
-video of the steps being executed, and create binary packages. The demo mode
-uses _cool-retro-term_ as terminal, whereas the continuous integration mode uses
-_MATE Terminal_ by default.
+Running the test suite as continuous integration or demo modes will record the
+terminal with the steps being executed, using asciinema(1), and create binary
+packages.
 
-The following additional packages are commonly needed as well:
+The following additional packages are commonly needed:
 
-    dbus-x11 xdotool x11-utils xvfb ffmpeg mate-terminal cool-retro-term xauth
-    dconf-cli alien linux-perf tshark sqlite3`
-
-For convenience, suitable profiles for _MATE Terminal_ and _cool-retro-term_ are
-provided under the `env` directory. To source them:
-
-    dconf load /org/mate/terminal/profiles/ < env/mate-terminal.profile
-    cp env/cool_retro_term.sqlite ~/.local/share/cool-retro-term/QML/OfflineStorage/Databases/*.sqlite
+    alien linux-perf tshark
 
 ## Regular test
 
@@ -79,9 +71,9 @@ Issuing:
 
     ./ci
 
-will run the whole test suite while recording a video of the execution, and it
-will also build JavaScript fragments used on http://passt.top/ for performance
-data tables and links to specific video offsets.
+will run the whole test suite while recording the execution, and it will also
+build JavaScript fragments used on http://passt.top/ for performance data tables
+and links to specific offsets in the captures.
 
 ## Demo mode
 
@@ -89,7 +81,7 @@ Issuing:
 
     ./demo
 
-will run the demo cases under `demo`, recording videos as well.
+will run the demo cases under `demo`, with terminal captures as well.
 
 # Framework
 
diff --git a/test/lib/perf_report b/test/lib/perf_report
index baa084b..9117046 100755
--- a/test/lib/perf_report
+++ b/test/lib/perf_report
@@ -103,19 +103,24 @@ for (var i = 0; i < perf_links.length; i++) {
 	var obj = document.getElementById(perf_links[i][0]);
 
 	obj.addEventListener("click", function(event) {
-		var ci_video = document.getElementById("ci_video");
+		var ci_video = document.getElementById("ci");
 		var top = ci_video.offsetTop - 5;
+		var seek;
 
-		event.preventDefault();
-		ci_video.play();
-		ci_video.pause();
 		for (var i = 0; i < perf_links.length; i++) {
 			if (this.id == perf_links[i][0]) {
-				ci_video.currentTime = perf_links[i][1] - 10;
+				seek = perf_links[i][1];
 			}
 		}
+
+		event.preventDefault();
+		ci_video_player.dispose();
+		ci_video_player = AsciinemaPlayer.create(
+			"/builds/latest/web/ci.cast",
+			ci_video,
+			{ startAt: seek, autoplay: true });
+
 		window.scrollTo({ top: top, behavior: "smooth" })
-		ci_video.play();
 	}, false);
 }
 '
diff --git a/test/lib/term b/test/lib/term
index e8a1d38..205a6a9 100755
--- a/test/lib/term
+++ b/test/lib/term
@@ -539,33 +539,22 @@ pause_continue() {
 	info_nolog ""
 }
 
-# run_term() - Start tmux session, X terminal if requested, running entry point
+# run_term() - Start tmux session, running entry point, with recording if needed
 run_term() {
 	export SHELL="/bin/sh"
 	tmux set-option -g default-shell "/bin/sh"
 	tmux set-option -g update-environment "PCAP DEBUG"
 
 	if [ ${CI} -eq 1 ]; then
-		__xterm_done="$(mktemp)"
-		if [ ${XVFB} -eq 1 ]; then
-			xvfb-run -s "-screen 0 4000x4000x24 -ac" ${CI_XTERM} "$(pwd)" -e "sh -c \"printf '\e[8;50;240t'; tmux new-session -s passt_test ./ci from_term; echo >${__xterm_done}\""
-		else
-			${CI_XTERM} "$(pwd)" -e "sh -c \"printf '\e[8;50;240t'; tmux new-session -s passt_test ./ci from_term; echo >${__xterm_done}\""
-		fi
-		while ! [ -s "${__xterm_done}" ]; do sleep 1; done
-		rm "${__xterm_done}"
+		printf '\e[8;50;240t'
+		asciinema rec --overwrite ci.uncut -c 'tmux new-session -s passt_test ./ci from_term'
+		video_postprocess ci.uncut
 	elif [ ${DEMO} -eq 1 ]; then
-		while true; do
-			if [ ${XVFB} -eq 1 ]; then
-				xvfb-run -s "-screen 0 4000x4000x24 -ac" ${DEMO_XTERM} "$(pwd)" -e sh -c 'tmux new-session -s passt_test ./run_demo from_term'
-			else
-				${DEMO_XTERM} "$(pwd)" -e sh -c 'tmux new-session -s passt_test ./run_demo from_term'
-			fi
-			[ $? -ne 0 ] && { tmux kill-session -t passt_test; continue; }
-			break
-		done
+		printf '\e[8;40;130t'
+		asciinema rec --overwrite demo.uncut -c 'tmux new-session -s passt_test ./run_demo from_term'
+		video_postprocess demo.uncut
 	else
-		tmux new-session -s passt_test ./run from_term
+		tmux new-session -s passt_test ./run_demo from_term
 	fi
 }
 
diff --git a/test/lib/video b/test/lib/video
index 6db9c1d..ec79c85 100755
--- a/test/lib/video
+++ b/test/lib/video
@@ -8,9 +8,9 @@
 # PASTA - Pack A Subtle Tap Abstraction
 #  for network namespace/tap device mode
 #
-# test/lib/video - Video grabbing, JavaScript fragments with links
+# test/lib/video - Session recording, JavaScript fragments with links
 #
-# Copyright (c) 2021 Red Hat GmbH
+# Copyright (c) 2021-2022 Red Hat GmbH
 # Author: Stefano Brivio <sbrivio(a)redhat.com>
 
 FFMPEG_PID_FILE="$(mktemp)"
@@ -33,19 +33,24 @@ for (var i = 0; i < video___VIDEO_NAME__links.length; i++) {
 	var obj = document.getElementById(video___VIDEO_NAME__links[i][0]);
 
 	obj.addEventListener("click", function(event) {
-		var __VIDEO_NAME___video = document.getElementById("__VIDEO_NAME___video");
-		var top = __VIDEO_NAME___video.offsetTop - 5;
+		var __VIDEO_NAME___div = document.getElementById("__VIDEO_NAME__");
+		var top = __VIDEO_NAME___div.offsetTop - 5;
+		var seek;
 
-		event.preventDefault();
-		__VIDEO_NAME___video.play();
-		__VIDEO_NAME___video.pause();
 		for (var i = 0; i < video___VIDEO_NAME__links.length; i++) {
 			if (this.id == video___VIDEO_NAME__links[i][0]) {
-				__VIDEO_NAME___video.currentTime = video___VIDEO_NAME__links[i][1];
+				seek = video___VIDEO_NAME__links[i][1];
 			}
 		}
+
+		event.preventDefault();
+		__VIDEO_NAME___player.dispose();
+		__VIDEO_NAME___player = AsciinemaPlayer.create(
+			"/builds/latest/web/__VIDEO_NAME__.cast",
+			__VIDEO_NAME___div,
+			{ startAt: seek, autoplay: true });
+
 		window.scrollTo({ top: top, behavior: "smooth" })
-		__VIDEO_NAME___video.play();
 	}, false);
 }
 '
@@ -65,53 +70,71 @@ video_append_links_js()
 	VIDEO_LINKS_BUF="${VIDEO_LINKS_BUF}${@}"
 }
 
-# video_grab() - Fetch window geometry, start grabbing video
-video_grab() {
+# video_start() - Mark start of a test in capture, record start timestamp
+video_start() {
 	VIDEO_NAME="${1}"
 
-	rm -f "${BASEPATH}/${VIDEO_NAME}.mp4" "${BASEPATH}/${VIDEO_NAME}.webm" "${BASEPATH}/${VIDEO_NAME}.js"
-
 	echo "${VIDEO_LINKS_TEMPLATE}" > "${BASEPATH}/${VIDEO_NAME}.js"
-
-	if [ ${XVFB} -eq 1 ]; then
-		# Grab the geometry of the first window that's at least 100px wide
-		eval $(xwininfo -d :99.0 -root -tree | sed -n 's/^[ ]*0x[0-f]*[^0-9]*\([0-9]\{3,\}\)x\([0-9]*\)+\([0-9]*\)+\([0-9]*\).*/__width=\1; __height=\2; __x=\3; __y=\4;/p')
-	else
-		__x=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Absolute upper-left X:[ ]*\([0-9]*\)$/\1/p')
-		__y=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Absolute upper-left Y:[ ]*\([0-9]*\)$/\1/p')
-		__width=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Width:[ ]*\([0-9]*\)$/\1/p')
-		__height=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Height:[ ]*\([0-9]*\)$/\1/p')
-	fi
-
-	[ $((__width % 2)) ]  && __width=$((__width - 1))
-	[ $((__height % 2)) ] && __height=$((__height - 1))
-
-	sleep 3
 	VIDEO_START_SECONDS=$(sed -n 's/\([0-9]*\).[0-9]* [0-9]*.[0-9]*/\1/p' /proc/uptime)
-	[ ${XVFB} -eq 1 ] && __disp=":99.0" || __disp=
-	ffmpeg -f x11grab -framerate 15 -video_size "${__width}x${__height}" -i "${__disp}+${__x},${__y}" -vcodec libx264 -qp 0 -draw_mouse 0 "${BASEPATH}/${VIDEO_NAME}.mp4" & echo $! > "${FFMPEG_PID_FILE}"
-}
 
-# video_time_now() - Print current video timestamp, in seconds
-video_time_now() {
-	__now=$(sed -n 's/\([0-9]*\).[0-9]* [0-9]*.[0-9]*/\1/p' /proc/uptime)
-	echo $((__now - VIDEO_START_SECONDS))
+	sync
+	[ ${DEMO} -eq 1 ] && tail -1 "${BASEPATH}/demo.uncut" > "${BASEPATH}/${VIDEO_NAME}.start"
+	[ ${CI} -eq 1 ] && tail -1 "${BASEPATH}/ci.uncut" > "${BASEPATH}/${VIDEO_NAME}.start"
+	sync
+
+	tmux refresh-client
 }
 
-# video_stop() - Stop grabbing, finalise JavaScript templates, convert to webm
+# video_stop() - Mark stop of a test in capture, finalise JavaScript fragments
 video_stop() {
+	tmux refresh-client
+
+	sync
+	[ ${DEMO} -eq 1 ] && tail -1 "${BASEPATH}/demo.uncut" > "${BASEPATH}/${VIDEO_NAME}.stop"
+	[ ${CI} -eq 1 ] && tail -1 "${BASEPATH}/ci.uncut" > "${BASEPATH}/${VIDEO_NAME}.stop"
+	sync
+
 	sed -i 's/^.*$/&\\/g' "${BASEPATH}/${VIDEO_NAME}.js"
 	echo "${VIDEO_LINKS_TEMPLATE_JS}" | sed "s/__VIDEO_NAME__/${VIDEO_NAME}/g" >> "${BASEPATH}/${VIDEO_NAME}.js"
 	echo "${VIDEO_LINKS_BUF}" >> "${BASEPATH}/${VIDEO_NAME}.js"
 	echo "${VIDEO_LINKS_TEMPLATE_POST}"  | sed "s/__VIDEO_NAME__/${VIDEO_NAME}/g" >> "${BASEPATH}/${VIDEO_NAME}.js"
+}
 
-	kill -INT $(cat "${FFMPEG_PID_FILE}")
-	while ps -p $(cat "${FFMPEG_PID_FILE}") >/dev/null; do sleep 1; done
-	rm "${FFMPEG_PID_FILE}"
-
-	[ ${1} -ne 0 ] && return
+# video_postprocess() - Cut terminal recordings based on .start and .stop files
+video_postprocess() {
+	IFS='
+'
+	__cast_name=
+	for __l in $(cat ${1}); do
+		[ -z "${__header}" ] && __header="${__l}" && continue
+
+		if [ -z "${__cast_name}" ]; then
+			for __cast_cut in *.start; do
+				[ "${__l}" != "$(cat "${__cast_cut}")" ] && continue
+				__cast_name="${__cast_cut%.start}"
+				__cast_offset=
+				__stop_line="$(cat ${__cast_name}.stop)"
+				echo "${__header}" > "${__cast_name}.cast"
+				break
+			done
+			continue
+		fi
+
+		[ "${__l}" = "${__stop_line}" ] && __cast_name= && continue
+
+		__l_offset="$(echo ${__l%%.*}|tr -c -d '[:digit:]')"
+		__l_rest="${__l#*.}"
+		[ -z "${__cast_offset}" ] && __cast_offset=${__l_offset}
+		__l_offset=$((__l_offset - __cast_offset))
+		printf '[%s.%s\n' "${__l_offset}" "${__l_rest}" >> "${__cast_name}".cast
+	done
+	unset IFS
+}
 
-	ffmpeg -an -fflags +genpts -i "${BASEPATH}/${VIDEO_NAME}.mp4" -c:v libvpx-vp9 -row-mt 1 -minrate 10k -maxrate 200k -b:v 200k "${BASEPATH}/${VIDEO_NAME}.webm"
+# video_time_now() - Print current video timestamp, in seconds
+video_time_now() {
+	__now=$(sed -n 's/\([0-9]*\).[0-9]* [0-9]*.[0-9]*/\1/p' /proc/uptime)
+	echo $((__now - VIDEO_START_SECONDS))
 }
 
 # video_link() - Append single link to given video chapter
diff --git a/test/run b/test/run
index c91122d..385267e 100755
--- a/test/run
+++ b/test/run
@@ -60,7 +60,7 @@ run() {
 
 	term
 	perf_init
-	[ ${CI} -eq 1 ]   && video_grab ci
+	[ ${CI} -eq 1 ]   && video_start ci
 
 	setup build
 	test build
@@ -94,7 +94,7 @@ run() {
 	teardown two_guests
 
 	perf_finish
-	[ ${CI} -eq 1 ] && video_stop ${STATUS_FAIL}
+	[ ${CI} -eq 1 ] && video_stop
 
 	log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}"
 
@@ -117,24 +117,24 @@ demo() {
 	term_demo
 
 	layout_demo_passt
-	video_grab demo_passt
+	video_start demo_passt
 	MODE=passt
 	test demo
-	video_stop 0
+	video_stop
 	teardown demo_passt
 
 	layout_demo_pasta
-	video_grab demo_pasta
+	video_start demo_pasta
 	MODE=pasta
 	test demo
-	video_stop 0
+	video_stop
 	teardown demo_pasta
 
 	layout_demo_podman
-	video_grab demo_podman
+	video_start demo_podman
 	MODE=podman
 	test demo
-	video_stop 0
+	video_stop
 	teardown_demo_podman
 
 	return 0
-- 
@@ -60,7 +60,7 @@ run() {
 
 	term
 	perf_init
-	[ ${CI} -eq 1 ]   && video_grab ci
+	[ ${CI} -eq 1 ]   && video_start ci
 
 	setup build
 	test build
@@ -94,7 +94,7 @@ run() {
 	teardown two_guests
 
 	perf_finish
-	[ ${CI} -eq 1 ] && video_stop ${STATUS_FAIL}
+	[ ${CI} -eq 1 ] && video_stop
 
 	log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}"
 
@@ -117,24 +117,24 @@ demo() {
 	term_demo
 
 	layout_demo_passt
-	video_grab demo_passt
+	video_start demo_passt
 	MODE=passt
 	test demo
-	video_stop 0
+	video_stop
 	teardown demo_passt
 
 	layout_demo_pasta
-	video_grab demo_pasta
+	video_start demo_pasta
 	MODE=pasta
 	test demo
-	video_stop 0
+	video_stop
 	teardown demo_pasta
 
 	layout_demo_podman
-	video_grab demo_podman
+	video_start demo_podman
 	MODE=podman
 	test demo
-	video_stop 0
+	video_stop
 	teardown_demo_podman
 
 	return 0
-- 
2.34.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2022-02-22 17:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-22 17:33 [PATCH] demo, ci: Switch to asciinema(1) for terminal recordings Stefano Brivio

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).