Skip to content

Commit 3f07437

Browse files
author
ivanmkc
committed
Added runner tests
1 parent dd8f5fe commit 3f07437

File tree

5 files changed

+283
-55
lines changed

5 files changed

+283
-55
lines changed

.github/workflows/go-snippets-pr-check.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ jobs:
4040
with:
4141
go-version: '1.22'
4242

43+
- name: Cache Go modules
44+
uses: actions/cache@v3
45+
with:
46+
path: ~/go/pkg/mod
47+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
48+
restore-keys: |
49+
${{ runner.os }}-go-
50+
4351
# --- PR-Specific Checks ---
4452
# These steps only run for pull request events.
4553

tools/go-snippets/check_go_snippets.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,15 @@ else
4646
echo "All Go files are correctly referenced in the snippets file."
4747
fi
4848

49+
# Check for files in the list that don't exist on disk
50+
dangling_references=$(comm -23 <(echo "${referenced_files}") <(echo "${all_go_files}"))
51+
52+
if [[ -n "${dangling_references}" ]]; then
53+
echo -e "${RED}Error: The following files are referenced in ${SNIPPETS_FILE} but do not exist:${NC}"
54+
echo "${dangling_references}" | sed 's/^/ /'
55+
echo
56+
echo "Please remove them from ${SNIPPETS_FILE}."
57+
EXIT_CODE=1
58+
fi
59+
4960
exit ${EXIT_CODE}

tools/go-snippets/files_to_test.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ snippets/agents/custom-agent/storyflow_agent.go
3838
snippets/agents/workflow-agents/sequential/main.go
3939
snippets/agents/workflow-agents/parallel/main.go
4040
snippets/agents/workflow-agents/loop/main.go
41-
snippets/quickstart/main.go "What is the weather in London?"
4241
snippets/agents/multi-agent/main.go
4342
snippets/tools/function-tools/long-running-tool/long_running_tool.go
4443
snippets/agents/llm-agents/snippets/main.go

tools/go-snippets/runner.sh

Lines changed: 123 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,92 +22,161 @@
2222
#
2323
# 2. Full Regression Mode: If no arguments are provided, it runs a predefined
2424
# list of all Go snippets in the repository. This is used for scheduled weekly tests.
25-
# Example: ./tools/go-snippets/runner.sh run
25+
# Example: ./tools/go-snippets/runner.sh build
2626

2727
# --- Configuration ---
28+
# Define color codes for colored output.
2829
RED='\033[0;31m'
2930
GREEN='\033[0;32m'
3031
NC='\033[0m' # No Color
32+
33+
# Global exit code for the script. It is set to 1 if any test fails.
3134
EXIT_CODE=0
35+
36+
# The configuration file that lists all Go snippets to be tested.
3237
SNIPPETS_FILE="tools/go-snippets/files_to_test.txt"
3338

34-
# --- Helper Function ---
35-
# execute_and_check executes a command, captures the output,
36-
# and prints a formatted, colored status message.
39+
# --- Helper Functions ---
40+
41+
# should_process_line determines if a line from the snippets file should be processed.
42+
# It returns 0 (success) for valid lines and 1 (failure) for comments or empty lines.
43+
#
44+
# @param {string} line - The line to check.
45+
# @returns {int} 0 if the line should be processed, 1 otherwise.
46+
should_process_line() {
47+
local line=$1
48+
# Remove all whitespace from the line to correctly handle lines with only spaces or tabs.
49+
local trimmed_line=$(echo "${line}" | tr -d '[:space:]')
50+
# Return failure (1) if the trimmed line is empty or starts with a hash.
51+
if [[ -z "${trimmed_line}" || "${trimmed_line}" =~ ^# ]]; then
52+
return 1
53+
else
54+
return 0
55+
fi
56+
}
57+
58+
# find_snippet_line searches the SNIPPETS_FILE for a given Go file path.
59+
# It ignores comments and returns the full line from the file.
60+
#
61+
# @param {string} file_path_from_root - The full path to the Go file relative to the project root (e.g., "examples/go/snippets/quickstart/main.go").
62+
# @returns {string} The matching line from SNIPPETS_FILE, or an empty string if not found.
63+
find_snippet_line() {
64+
local file_path_from_root=$1
65+
# The SNIPPETS_FILE contains paths relative to 'examples/go/', so we strip that prefix from the input path.
66+
local relative_path=${file_path_from_root#examples/go/}
67+
# First, filter out all commented lines, then search for the relative path.
68+
grep -v '^\s*#' "${SNIPPETS_FILE}" | grep "${relative_path}"
69+
}
70+
71+
# get_command_for_action constructs the appropriate Go command based on the action.
72+
# It specifically handles stripping arguments for the 'build' action.
73+
#
74+
# @param {string} action - The action to perform ("build" or "run").
75+
# @param {string} line - The line from the snippets file, which may include arguments.
76+
# @returns {string} The fully formed Go command.
77+
get_command_for_action() {
78+
local action=$1
79+
local line=$2
80+
local command=""
81+
82+
if [ "${action}" == "build" ]; then
83+
# For 'build', extract only the file paths, ignoring any arguments.
84+
# 'go build' does not accept application arguments, so they must be stripped.
85+
local files_to_build=$(echo "${line}" | awk '{for(i=1;i<=NF;i++) if($i ~ /\.go$/) printf "%s ", $i}')
86+
command="go build ${files_to_build}"
87+
elif [ "${action}" == "run" ]; then
88+
# For 'run', use the line as is, as 'go run' will pass arguments to the application.
89+
command="go run ${line}"
90+
fi
91+
echo "${command}"
92+
}
93+
94+
# execute_and_check executes a command and prints a formatted status message.
3795
#
3896
# @param {string} command - The full command to execute.
3997
# @param {string} display_name - A user-friendly name for the command/file.
4098
execute_and_check() {
4199
local command=$1
42100
local display_name=$2
43101

44-
# Capture the run output and exit code.
102+
# 'eval' is used to correctly execute the command string, which may contain quotes and other special characters.
45103
local output
46104
output=$(eval ${command} 2>&1)
47105
local exit_code=$?
48106

49107
if [ ${exit_code} -eq 0 ]; then
50-
# Print PASS status in green if run is successful.
51108
echo -e "[${GREEN}PASS${NC}] ${display_name}"
52109
else
53-
# Print FAIL status in red and the indented error message.
54110
echo -e "[${RED}FAIL${NC}] ${display_name}"
111+
# Indent the error output for better readability.
55112
echo "${output}" | sed 's/^/ /'
56-
# Set the script's exit code to 1 to fail the CI check.
113+
# Set the global exit code to indicate failure.
57114
EXIT_CODE=1
58115
fi
59116
}
60117

61118
# --- Main Logic ---
62119

63-
if [[ "$1" != "build" && "$1" != "run" ]]; then
64-
echo "Usage: $0 <build|run> [file1 file2 ...]"
65-
exit 1
66-
fi
120+
# This check prevents the main logic from running if the script is being sourced (e.g., by the test script).
121+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
122+
# Validate the first argument is either 'build' or 'run'.
123+
if [[ "$1" != "build" && "$1" != "run" ]]; then
124+
echo "Usage: $0 <build|run> [file1 file2 ...]"
125+
exit 1
126+
fi
67127

68-
ACTION=$1
69-
shift
128+
ACTION=$1
129+
shift # Remove the first argument, so '$@' contains only the file paths.
70130

71-
# Run go mod tidy once for the entire examples/go module
72-
(cd examples/go && go mod tidy)
73-
if [ $? -ne 0 ]; then
74-
echo -e "[${RED}FAIL${NC}] go mod tidy failed in examples/go"
75-
EXIT_CODE=1
76-
fi
131+
# Ensure all Go module dependencies are tidy before running any builds or tests.
132+
# This is run from the 'examples/go' directory where the go.mod file is located.
133+
(cd examples/go && go mod tidy)
134+
if [ $? -ne 0 ]; then
135+
echo -e "[${RED}FAIL${NC}] go mod tidy failed in examples/go"
136+
exit 1 # Exit immediately if dependencies are not clean.
137+
fi
138+
139+
# Check if file paths were provided as arguments (Targeted Mode).
140+
if [ "$#" -gt 0 ]; then
141+
echo "Running targeted Go snippet ${ACTION} for changed files..."
142+
echo
143+
for file in "$@"; do
144+
# Find the corresponding line in the snippets file for the changed file.
145+
line=$(find_snippet_line "${file}")
146+
if [[ -z "${line}" ]]; then
147+
echo -e "[${RED}FAIL${NC}] ${file}"
148+
echo " Error: No corresponding entry found in ${SNIPPETS_FILE}."
149+
EXIT_CODE=1
150+
continue # Skip to the next file.
151+
fi
152+
153+
# Construct the appropriate build or run command.
154+
command_to_execute=$(get_command_for_action "${ACTION}" "${line}")
155+
if [[ -n "${command_to_execute}" ]]; then
156+
# Execute the command from the 'examples/go' directory.
157+
execute_and_check "(cd examples/go && ${command_to_execute})" "${file}"
158+
fi
159+
done
160+
else
161+
# If no file paths were provided, run in Full Regression Mode.
162+
echo "Running full Go snippet ${ACTION}..."
163+
echo
164+
# Read the snippets file line by line.
165+
while IFS= read -r line; do
166+
# Skip empty lines and comments.
167+
if ! should_process_line "${line}"; then
168+
continue
169+
fi
170+
171+
command_to_execute=$(get_command_for_action "${ACTION}" "${line}")
172+
if [[ -n "${command_to_execute}" ]]; then
173+
execute_and_check "(cd examples/go && ${command_to_execute})" "${line}"
174+
fi
175+
done < "${SNIPPETS_FILE}"
176+
fi
77177

78-
if [ "$#" -gt 0 ]; then
79-
echo "Running targeted Go snippet ${ACTION} for changed files..."
80-
echo
81-
for file in "$@"; do
82-
# Find the line in snippets.txt that contains the changed file
83-
line=$(grep "${file#examples/go/}" ${SNIPPETS_FILE})
84-
if [ "${ACTION}" == "build" ]; then
85-
command_to_execute="go build ${line}"
86-
elif [ "${ACTION}" == "run" ]; then
87-
command_to_execute="go run ${line}"
88-
fi
89-
90-
if [[ -n "${command_to_execute}" ]]; then
91-
execute_and_check "(cd examples/go && ${command_to_execute})" "${file}"
92-
fi
93-
done
94-
else
95-
echo "Running full Go snippet ${ACTION}..."
96178
echo
97-
while IFS= read -r line; do
98-
command_to_execute=""
99-
if [ "${ACTION}" == "build" ]; then
100-
command_to_execute="go build ${line}"
101-
elif [ "${ACTION}" == "run" ]; then
102-
command_to_execute="go run ${line}"
103-
fi
104-
105-
if [[ -n "${command_to_execute}" ]]; then
106-
execute_and_check "(cd examples/go && ${command_to_execute})" "${line}"
107-
fi
108-
done < "${SNIPPETS_FILE}"
179+
echo "Script finished."
180+
# Exit with the final status code (0 for success, 1 for failure).
181+
exit ${EXIT_CODE}
109182
fi
110-
111-
echo
112-
echo "Script finished."
113-
exit ${EXIT_CODE}

tools/go-snippets/runner_test.sh

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/bin/bash
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Unit tests for runner.sh.
17+
# This script can be run directly to validate the logic of the functions in runner.sh.
18+
19+
# --- Test Setup ---
20+
# Source the script to make its functions available for testing.
21+
source "$(dirname "$0")/runner.sh"
22+
23+
# --- Test Harness ---
24+
# Simple assertion function to check for equality.
25+
assert_equals() {
26+
local expected=$1
27+
local actual=$2
28+
local test_name=$3
29+
30+
if [ "${expected}" == "${actual}" ]; then
31+
echo -e "[${GREEN}PASS${NC}] ${test_name}"
32+
else
33+
echo -e "[${RED}FAIL${NC}] ${test_name}"
34+
echo " Expected: '${expected}'"
35+
echo " Actual: '${actual}'"
36+
exit 1
37+
fi
38+
}
39+
40+
# --- Test Cases ---
41+
42+
# Tests that the 'run' action correctly forms a 'go run' command,
43+
# preserving any arguments that might be on the line.
44+
test_get_command_for_run_action() {
45+
local line="snippets/quickstart/main.go"
46+
local expected="go run snippets/quickstart/main.go"
47+
local actual=$(get_command_for_action "run" "${line}")
48+
assert_equals "${expected}" "${actual}" "Should create correct 'run' command without arguments"
49+
}
50+
51+
# Tests that the 'build' action correctly forms a 'go build' command
52+
# and, most importantly, strips any non-.go file arguments from the line.
53+
# This is critical because 'go build' does not accept application arguments.
54+
test_get_command_for_build_action_strips_args() {
55+
local line="snippets/quickstart/main.go"
56+
local expected="go build snippets/quickstart/main.go "
57+
local actual=$(get_command_for_action "build" "${line}")
58+
assert_equals "${expected}" "${actual}" "Should create correct 'build' command and strip arguments"
59+
}
60+
61+
# Tests that a line with multiple .go files is correctly handled for a build.
62+
# This is important for packages that are split across multiple files.
63+
test_get_command_for_multi_file_build() {
64+
local line="file1.go file2.go"
65+
local expected="go build file1.go file2.go "
66+
local actual=$(get_command_for_action "build" "${line}")
67+
assert_equals "${expected}" "${actual}" "Should handle multiple files correctly for build"
68+
}
69+
70+
# Tests that a line with multiple .go files is correctly handled for a run.
71+
test_get_command_for_multi_file_run() {
72+
local line="file1.go file2.go"
73+
local expected="go run file1.go file2.go"
74+
local actual=$(get_command_for_action "run" "${line}")
75+
assert_equals "${expected}" "${actual}" "Should handle multiple files correctly for run"
76+
}
77+
78+
# Tests the core logic for finding a snippet in the configuration file.
79+
# It ensures that:
80+
# 1. The 'examples/go/' prefix is correctly stripped from the input path.
81+
# 2. The correct line is found in the test file.
82+
# 3. Commented-out lines are ignored.
83+
test_find_snippet_line() {
84+
# Create a temporary SNIPPETS_FILE for this test to isolate it.
85+
local original_snippets_file="${SNIPPETS_FILE}"
86+
SNIPPETS_FILE=$(mktemp)
87+
# Populate the temporary file with test data.
88+
# This first line acts as a negative test case. The test specifically searches for 'snippets/quickstart/main.go',
89+
# so this line should be correctly ignored by the grep logic, ensuring the function doesn't just return the first line it finds.
90+
echo "file1.go" > "${SNIPPETS_FILE}"
91+
# This line is a commented-out version of our target and should also be ignored.
92+
echo "# snippets/quickstart/main.go" >> "${SNIPPETS_FILE}" # This line should be ignored by grep.
93+
# This is the actual line we expect the function to find and return.
94+
echo "snippets/quickstart/main.go" >> "${SNIPPETS_FILE}" # This is the line we expect to find.
95+
96+
# This simulates the file path that would be passed to the script (e.g., from 'git diff').
97+
local input_file_path="examples/go/snippets/quickstart/main.go"
98+
local expected_line="snippets/quickstart/main.go"
99+
100+
# Call the function under test.
101+
local actual_line=$(find_snippet_line "${input_file_path}")
102+
103+
assert_equals "${expected_line}" "${actual_line}" "Should find the correct snippet line, ignoring comments"
104+
105+
# Cleanup
106+
rm "${SNIPPETS_FILE}"
107+
SNIPPETS_FILE="${original_snippets_file}" # Restore original path
108+
}
109+
110+
# Tests the logic for determining if a line should be processed.
111+
test_should_process_line() {
112+
# A valid line should return 0 (success).
113+
should_process_line "file1.go"
114+
assert_equals "0" "$?" "Should return success for a valid line"
115+
116+
# An empty line should return 1 (failure).
117+
should_process_line ""
118+
assert_equals "1" "$?" "Should return failure for an empty line"
119+
120+
# A line with only whitespace should return 1 (failure).
121+
should_process_line " "
122+
assert_equals "1" "$?" "Should return failure for a whitespace-only line"
123+
124+
# A commented line should return 1 (failure).
125+
should_process_line "# file1.go"
126+
assert_equals "1" "$?" "Should return failure for a commented line"
127+
128+
# A commented line with leading spaces should return 1 (failure).
129+
should_process_line " # file1.go"
130+
assert_equals "1" "$?" "Should return failure for a commented line with leading spaces"
131+
}
132+
133+
# --- Run Tests ---
134+
echo "Running tests for runner.sh..."
135+
test_get_command_for_run_action
136+
test_get_command_for_build_action_strips_args
137+
test_get_command_for_multi_file_build
138+
test_get_command_for_multi_file_run
139+
test_find_snippet_line
140+
test_should_process_line
141+
echo "All tests passed."

0 commit comments

Comments
 (0)