From b29534abce39107ab96985acbacf292619bbaf92 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Sun, 9 Jul 2023 19:52:19 +0200 Subject: test: test empty CI run output, refactoring Meh, moving the shell code generation to Python I like better in the end. --- test/py/conftest.py | 8 +-- test/py/lib/test_repo.py | 116 ++++++++++++++++++++++++++++++++++++++------ test/py/lib/test_repo/ci.sh | 24 --------- test/py/test_repo.py | 4 +- 4 files changed, 108 insertions(+), 44 deletions(-) delete mode 100755 test/py/lib/test_repo/ci.sh (limited to 'test') diff --git a/test/py/conftest.py b/test/py/conftest.py index a92699d..e087dd1 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -11,7 +11,7 @@ from pytest import fixture from lib.db import Database from lib.net import random_unused_port from lib.process import CmdLine -from lib.test_repo import TestRepo +from lib.test_repo import TestRepoOutputSimple, TestRepoOutputEmpty class Param: @@ -188,9 +188,9 @@ def client(client_cmd): return client_cmd -@fixture -def test_repo(tmp_path): - return TestRepo(tmp_path) +@fixture(params=[TestRepoOutputSimple, TestRepoOutputEmpty]) +def test_repo(tmp_path, request): + return request.param(os.path.join(tmp_path, 'repo')) class Env: diff --git a/test/py/lib/test_repo.py b/test/py/lib/test_repo.py index 3a4d847..94b6f93 100644 --- a/test/py/lib/test_repo.py +++ b/test/py/lib/test_repo.py @@ -3,8 +3,10 @@ # For details, see https://github.com/egor-tensin/cimple. # Distributed under the MIT License. +import abc import logging import os +import shlex import shutil from .process import Process @@ -24,28 +26,114 @@ class Repo: Process.run(*args, cwd=self.path, **kwargs) +CI_SCRIPT = R'''#!/usr/bin/env bash + +set -o errexit -o nounset -o pipefail +shopt -s inherit_errexit lastpipe + +readonly runs_dir={runs_dir} + +readonly run_output_template=run_XXXXXX + +run_output_path="$( mktemp --tmpdir="$runs_dir" "$run_output_template" )" +readonly run_output_path + +touch -- "$run_output_path" +''' + + class TestRepo(Repo): # Prevent Pytest from discovering test cases in this class: __test__ = False - DATA_DIR = 'test_repo' - CI_SCRIPT = 'ci.sh' - OUTPUT_DIR = 'output' + def _format_ci_script(self): + runs_dir = shlex.quote(self.runs_dir) + return CI_SCRIPT.format(runs_dir=runs_dir) + + def __init__(self, path, ci_script='ci.sh'): + super().__init__(path) + + self.runs_dir = os.path.join(self.path, 'runs') + os.makedirs(self.runs_dir, exist_ok=True) - @staticmethod - def get_ci_script(): - return os.path.join(os.path.dirname(__file__), TestRepo.DATA_DIR, TestRepo.CI_SCRIPT) + self.ci_script_path = os.path.join(self.path, ci_script) + with open(self.ci_script_path, mode='x') as f: + f.write(self._format_ci_script()) + os.chmod(self.ci_script_path, 0o755) + + self.run('git', 'add', '--', ci_script) + self.run('git', 'commit', '-q', '-m', 'add CI script') + + def count_run_files(self): + return len([name for name in os.listdir(self.runs_dir) if os.path.isfile(os.path.join(self.runs_dir, name))]) + + +class TestRepoOutput(TestRepo, abc.ABC): + __test__ = False + + OUTPUT_SCRIPT_NAME = 'generate-output' def __init__(self, path): super().__init__(path) - shutil.copy(self.get_ci_script(), self.path) - self.run('git', 'add', '.') - self.run('git', 'commit', '-q', '-m', 'add CI script') - self.output_dir = os.path.join(self.path, TestRepo.OUTPUT_DIR) - os.makedirs(self.output_dir, exist_ok=True) - def count_ci_output_files(self): - return len([name for name in os.listdir(self.output_dir) if os.path.isfile(os.path.join(self.output_dir, name))]) + self.output_script_path = os.path.join(self.path, TestRepoOutput.OUTPUT_SCRIPT_NAME) + with open(self.output_script_path, mode='x') as f: + f.write(self._format_output_script()) + os.chmod(self.output_script_path, 0o755) + + with open(self.ci_script_path, mode='a') as f: + f.write(self._format_ci_script_addition()) + + self.run('git', 'add', '--', TestRepoOutput.OUTPUT_SCRIPT_NAME) + self.run('git', 'add', '-u') + self.run('git', 'commit', '-q', '-m', 'add output script') - def output_matches(self, output): + @abc.abstractmethod + def _format_output_script(self): + pass + + def _format_ci_script_addition(self): + return R'{output_script} | tee -a "$run_output_path"'.format( + output_script=shlex.quote(self.output_script_path)) + + @abc.abstractmethod + def run_output_matches(self, output): + pass + + +OUTPUT_SCRIPT_SIMPLE = R'''#!/usr/bin/env bash + +set -o errexit -o nounset -o pipefail +shopt -s inherit_errexit lastpipe + +timestamp="$( date --iso-8601=ns )" +readonly timestamp + +echo "A CI run happened at $timestamp" +''' + + +class TestRepoOutputSimple(TestRepoOutput): + __test__ = False + + def _format_output_script(self): + return OUTPUT_SCRIPT_SIMPLE + + def run_output_matches(self, output): return output.decode().startswith('A CI run happened at ') + + +OUTPUT_SCRIPT_EMPTY = R'''#!/bin/sh + +true +''' + + +class TestRepoOutputEmpty(TestRepoOutput): + __test__ = False + + def _format_output_script(self): + return OUTPUT_SCRIPT_EMPTY + + def run_output_matches(self, output): + return len(output) == 0 diff --git a/test/py/lib/test_repo/ci.sh b/test/py/lib/test_repo/ci.sh deleted file mode 100755 index 804cdb3..0000000 --- a/test/py/lib/test_repo/ci.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit -o nounset -o pipefail -shopt -s inherit_errexit lastpipe - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir - -ci_output_dir="$( git remote get-url origin )" -ci_output_dir="$ci_output_dir/output" -readonly ci_output_dir - -mkdir -p -- "$ci_output_dir" - -readonly ci_output_template=ci_XXXXXX - -ci_output_path="$( mktemp --tmpdir="$ci_output_dir" "$ci_output_template" )" -readonly ci_output_path - -timestamp="$( date --iso-8601=ns )" -readonly timestamp - -echo "A CI run happened at $timestamp" | tee -- "$ci_output_path" diff --git a/test/py/test_repo.py b/test/py/test_repo.py index 3e507c3..76aeef3 100644 --- a/test/py/test_repo.py +++ b/test/py/test_repo.py @@ -47,14 +47,14 @@ def _test_repo_internal(env, repo, numof_processes, runs_per_process): for proc in processes: proc.join() - assert numof_runs == repo.count_ci_output_files() + assert numof_runs == repo.count_run_files() runs = env.db.get_all_runs() assert numof_runs == len(runs) for id, status, ec, output, url, rev in runs: assert status == 'finished', f'Invalid status for run {id}: {status}' - assert repo.output_matches(output), f"Output doesn't match: {output}" + assert repo.run_output_matches(output), f"Output doesn't match: {output}" @pytest.mark.parametrize('numof_clients,runs_per_client', -- cgit v1.2.3