Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/go-basic-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ on:
required: false
type: string
default: "./..."
TEST_TIMEOUT_MINUTES:
description: Per-package Go test timeout in minutes; must be a positive integer
required: false
type: number
default: 10
TEST_PARALLELISM:
description: Maximum parallel Go tests per package; 0 keeps the Go default
required: false
Expand Down Expand Up @@ -103,14 +108,19 @@ jobs:
shell: bash
env:
UNIT_TESTS_PATH: ${{ inputs.UNIT_TESTS_PATH }}
TEST_TIMEOUT_MINUTES: ${{ inputs.TEST_TIMEOUT_MINUTES }}
TEST_PARALLELISM: ${{ inputs.TEST_PARALLELISM || 0 }}
TEST_PACKAGE_PARALLELISM: ${{ inputs.TEST_PACKAGE_PARALLELISM || 0 }}
run: |
set -euo pipefail
results_dir=$(mktemp -d "$RUNNER_TEMP/go-tests-${GITHUB_JOB}.XXXXXX")
printf 'results_dir=%s\nartifact_name=%s\n' "$results_dir" "${results_dir##*/}" >> "$GITHUB_OUTPUT"

args=(-json -count=1)
if [[ ! $TEST_TIMEOUT_MINUTES =~ ^[1-9][0-9]*$ ]]; then
printf 'TEST_TIMEOUT_MINUTES must be a positive integer\n' | tee "$results_dir/setup.stderr" >&2
exit 1
fi
args=(-json -count=1 -timeout "${TEST_TIMEOUT_MINUTES}m")
for setting in TEST_PARALLELISM TEST_PACKAGE_PARALLELISM; do
value=${!setting}
if [[ ! $value =~ ^(0|[1-9][0-9]*)$ ]]; then
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/incluster-comp-pr-created.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ on:
required: false
type: string
default: "./..."
TEST_TIMEOUT_MINUTES:
description: Per-package Go test timeout in minutes; must be a positive integer
required: false
type: number
default: 10
TEST_PARALLELISM:
description: Maximum parallel Go tests per package; 0 keeps the Go default
required: false
Expand Down Expand Up @@ -61,6 +66,7 @@ jobs:
CGO_ENABLED: ${{ inputs.CGO_ENABLED }}
UNIT_TESTS_PATH: ${{ inputs.UNIT_TESTS_PATH }}
BUILD_PATH: ${{ inputs.BUILD_PATH }}
TEST_TIMEOUT_MINUTES: ${{ inputs.TEST_TIMEOUT_MINUTES }}
TEST_PARALLELISM: ${{ inputs.TEST_PARALLELISM || 0 }}
TEST_PACKAGE_PARALLELISM: ${{ inputs.TEST_PACKAGE_PARALLELISM || 0 }}
# TEST_MULTI_ENVIRONMENTS: ${{ inputs.TEST_MULTI_ENVIRONMENTS }}
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,9 @@ strict latency checks need a separate performance test with controlled load.

The reporting regression harness uses Python's standard library and local Go
fixtures: `python3 -m unittest discover -s tests -v`.

`TEST_TIMEOUT_MINUTES` sets Go's per-package test timeout for both race and
coverage runs. It defaults to `10`, preserving Go's default budget; callers
with large migration or integration suites can set `30`. It must be a positive
integer. This limits test execution, not compilation or the whole workflow,
and does not change race detection or assertions inside tests.
30 changes: 28 additions & 2 deletions tests/test_go_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def setUp(self):
GITHUB_STEP_SUMMARY=str(self.root / "summary"),
UNIT_TESTS_PATH="./...",
TEST_PARALLELISM="0", TEST_PACKAGE_PARALLELISM="0",
TEST_COVERAGE="false", GOPROXY="off", GOSUMDB="off",
TEST_TIMEOUT_MINUTES="10", TEST_COVERAGE="false", GOPROXY="off", GOSUMDB="off",
GOTOOLCHAIN="local", GOFLAGS="", GOWORK="off")

def shell(self, anchor):
Expand Down Expand Up @@ -141,7 +141,7 @@ def test_flags_and_package_filtering(self):
calls = [json.loads(line) for line in (self.root / "calls").read_text().splitlines()]
self.assertEqual(calls[-2], ["list", "./pkg/...", "./internal/...",
"./literal-$(touch-INJECTED)/*"])
expected = ["test", "-json", "-count=1"]
expected = ["test", "-json", "-count=1", "-timeout", "10m"]
if parallel != "0":
expected += ["-parallel", parallel, "-p", package_parallel]
if coverage == "true":
Expand All @@ -150,6 +150,32 @@ def test_flags_and_package_filtering(self):
expected += ["-race"]
self.assertEqual(calls[-1], expected + ["example.com/ci"])

def test_timeout_wrapper_preserves_input_for_validation(self):
wrapper = WORKFLOW.with_name("incluster-comp-pr-created.yaml").read_text()
self.assertIn("TEST_TIMEOUT_MINUTES: ${{ inputs.TEST_TIMEOUT_MINUTES }}", wrapper)
self.assertNotIn("inputs.TEST_TIMEOUT_MINUTES ||", wrapper)

def test_custom_timeout_in_both_modes(self):
self.fake_go()
for coverage in ("false", "true"):
with self.subTest(coverage=coverage):
self.env.update(TEST_TIMEOUT_MINUTES="30", TEST_COVERAGE=coverage)
result, _ = self.execute()
self.assertEqual(result.returncode, 0, result.stderr)
calls = [json.loads(line) for line in (self.root / "calls").read_text().splitlines()]
self.assertEqual(calls[-1][3:5], ["-timeout", "30m"])

def test_invalid_timeout_fails_before_go(self):
self.fake_go()
for value in ("0", "-1", "1.5", "abc", "", "$(touch INJECTED)"):
with self.subTest(value=value):
self.env["TEST_TIMEOUT_MINUTES"] = value
result, summary = self.execute()
self.assertNotEqual(result.returncode, 0)
self.assertIn("TEST_TIMEOUT_MINUTES must be a positive integer", summary)
self.assertFalse((self.root / "calls").exists())
self.assertFalse((self.module / "INJECTED").exists())

def test_invalid_parallelism_fails_before_go(self):
self.fake_go()
for setting in ("TEST_PARALLELISM", "TEST_PACKAGE_PARALLELISM"):
Expand Down
Loading