Skip to content

Commit 693b2e2

Browse files
committed
Major refactor of 'query generate new-query'
Refactors the 'query generate new-query' subcommand of the CodeQLToolkit.Core (qlt) CLI in order to: - Update the version of CodeQL used to 'v2.22.4' (current latest); - Support generating new queries for all currently supported languages; - Add support for generating `@kind path-problem` (dataflow) queries; - Create scripts for end-to-end testing of the `new-query` subcommand; - Create a new actions workflow for automated verification and testing of queries generated via `qlt query generate new-query`; - Ensure that (liquid) template rendering fails if a required template var is unset. Adds unit tests to ensure template vars are not empty.
1 parent dc1d992 commit 693b2e2

File tree

92 files changed

+1866
-337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+1866
-337
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: ⚙️ Internal - Validate CLI Outputs
2+
3+
on:
4+
push:
5+
branches:
6+
- 'main'
7+
pull_request:
8+
branches:
9+
- 'main'
10+
workflow_dispatch:
11+
12+
jobs:
13+
validate-cli-outputs:
14+
name: Validate CLI End-to-End
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Setup .NET
22+
uses: actions/setup-dotnet@v4
23+
with:
24+
dotnet-version: '8.0.x'
25+
26+
- name: Restore dependencies
27+
run: dotnet restore
28+
29+
- name: Build project
30+
run: dotnet build --no-restore --configuration Release
31+
32+
- name: Run end-to-end CLI validation
33+
run: ./scripts/validate-cli-e2e.sh
34+
env:
35+
# Use a temporary directory in the runner
36+
TEST_DIR: ${{ runner.temp }}/qlt-cli-e2e-test
37+
38+
- name: Upload test artifacts on failure
39+
if: failure()
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: cli-validation-artifacts
43+
path: |
44+
${{ runner.temp }}/qlt-cli-e2e-test/**
45+
if-no-files-found: warn
46+
retention-days: 3

example/qlt.conf.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"CodeQLCLI": "2.15.5",
3-
"CodeQLStandardLibrary": "codeql-cli/v2.15.5",
4-
"CodeQLCLIBundle": "codeql-bundle-v2.15.5",
2+
"CodeQLCLI": "2.22.4",
3+
"CodeQLStandardLibrary": "codeql-cli/v2.22.4",
4+
"CodeQLCLIBundle": "codeql-bundle-v2.22.4",
55
"EnableCustomCodeQLBundles": true,
6-
"CodeQLStandardLibraryIdent": "codeql-cli_v2.15.5",
6+
"CodeQLStandardLibraryIdent": "codeql-cli_v2.22.4",
77
"CodeQLPackConfiguration" : [
88
{
99
"Name": "qlt/cpp-customizations",

scripts/run-unit-tests.sh

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
#!/bin/bash
2+
3+
# Script: run-unit-tests.sh
4+
# Purpose: Run CodeQL unit tests for generated queries
5+
# Usage: ./run-unit-tests.sh [OPTIONS]
6+
7+
set -euo pipefail
8+
9+
# Color codes for output
10+
readonly RED='\033[0;31m'
11+
readonly GREEN='\033[0;32m'
12+
readonly YELLOW='\033[1;33m'
13+
readonly BLUE='\033[0;34m'
14+
readonly NC='\033[0m' # No Color
15+
16+
# Logging functions
17+
log_info() {
18+
echo -e "${BLUE}[INFO]${NC} $1" >&2
19+
}
20+
21+
log_success() {
22+
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
23+
}
24+
25+
log_error() {
26+
echo -e "${RED}[ERROR]${NC} $1" >&2
27+
}
28+
29+
log_warning() {
30+
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
31+
}
32+
33+
# Configuration - Centralized path variables
34+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
36+
QLT_PROJECT_DIR="$REPO_ROOT/src/CodeQLToolkit.Core"
37+
QLT_PROJECT_FILE="$QLT_PROJECT_DIR/CodeQLToolkit.Core.csproj"
38+
39+
# Default configuration
40+
TEST_DIR=""
41+
LANGUAGES=""
42+
VERBOSE=false
43+
HELP=false
44+
45+
# Usage information
46+
show_help() {
47+
cat << EOF
48+
Usage: $0 [OPTIONS]
49+
50+
Run CodeQL unit tests for generated queries in a test directory.
51+
52+
OPTIONS:
53+
-d, --test-dir DIR Test directory containing language subdirectories
54+
-l, --languages LANGS Comma-separated list of languages to test (e.g., "python,java")
55+
-v, --verbose Enable verbose output
56+
-h, --help Show this help message
57+
58+
EXAMPLES:
59+
$0 --test-dir /tmp/qlt-test --languages python,java
60+
$0 -d /tmp/qlt-test -l python -v
61+
62+
DESCRIPTION:
63+
This script runs CodeQL unit tests for queries generated by the CodeQL Toolkit.
64+
It expects a directory structure like:
65+
66+
TEST_DIR/
67+
├── python/
68+
│ └── testpack-python/
69+
│ └── test/
70+
└── java/
71+
└── testpack-java/
72+
└── test/
73+
74+
The script will:
75+
1. Execute unit tests for each language
76+
2. Validate test results
77+
3. Generate a summary report
78+
79+
EOF
80+
}
81+
82+
# Parse command line arguments
83+
parse_args() {
84+
while [[ $# -gt 0 ]]; do
85+
case $1 in
86+
-d|--test-dir)
87+
TEST_DIR="$2"
88+
shift 2
89+
;;
90+
-l|--languages)
91+
LANGUAGES="$2"
92+
shift 2
93+
;;
94+
-v|--verbose)
95+
VERBOSE=true
96+
shift
97+
;;
98+
-h|--help)
99+
HELP=true
100+
shift
101+
;;
102+
*)
103+
log_error "Unknown option: $1"
104+
show_help
105+
exit 1
106+
;;
107+
esac
108+
done
109+
}
110+
111+
# Validate required parameters
112+
validate_params() {
113+
if [[ "$HELP" == "true" ]]; then
114+
show_help
115+
exit 0
116+
fi
117+
118+
if [[ -z "$TEST_DIR" ]]; then
119+
log_error "Test directory is required. Use --test-dir option."
120+
show_help
121+
exit 1
122+
fi
123+
124+
if [[ ! -d "$TEST_DIR" ]]; then
125+
log_error "Test directory does not exist: $TEST_DIR"
126+
exit 1
127+
fi
128+
129+
if [[ -z "$LANGUAGES" ]]; then
130+
log_error "Languages are required. Use --languages option."
131+
show_help
132+
exit 1
133+
fi
134+
}
135+
136+
# Execute unit tests for a language
137+
run_unit_tests() {
138+
local language="$1"
139+
local work_dir="$2"
140+
141+
log_info "Running unit tests for $language..."
142+
143+
# Get environment info
144+
local os_name=$(uname -s)
145+
if [[ "$os_name" == "Darwin" ]]; then
146+
os_name="macOS"
147+
fi
148+
149+
log_info " → Environment: $os_name, CodeQL CLI: Startup...
150+
unknown, Stdlib: codeql-cli_vStartup...
151+
unknown"
152+
153+
# Find the QLT project using centralized variables
154+
if [[ ! -f "$QLT_PROJECT_FILE" ]]; then
155+
log_error "Could not find QLT project at: $QLT_PROJECT_FILE"
156+
return 1
157+
fi
158+
159+
# Change to the work directory since that's where QLT expects to be run from
160+
local old_pwd=$(pwd)
161+
cd "$work_dir" || return 1
162+
163+
# Create test results directory for the language
164+
local test_results_dir="${work_dir}/${language}/test-results"
165+
mkdir -p "$test_results_dir"
166+
167+
# Execute tests using dotnet run
168+
local test_output
169+
if ! test_output=$(dotnet run --project "$QLT_PROJECT_FILE" -- test run execute-unit-tests \
170+
--language "$language" \
171+
--num-threads 2 \
172+
--work-dir "$test_results_dir" \
173+
--runner-os "$os_name" \
174+
--automation-type actions 2>&1); then
175+
log_error "Unit test execution failed for $language"
176+
if [[ "$VERBOSE" == "true" ]]; then
177+
log_error "$test_output"
178+
fi
179+
cd "$old_pwd"
180+
return 1
181+
fi
182+
183+
cd "$old_pwd"
184+
185+
if [[ "$VERBOSE" == "true" ]]; then
186+
log_info "Test execution output for $language:"
187+
echo "$test_output"
188+
fi
189+
190+
log_success "Unit tests completed for $language"
191+
return 0
192+
}
193+
194+
# Validate unit test results
195+
validate_test_results() {
196+
local language="$1"
197+
local work_dir="$2"
198+
199+
log_info "Validating unit test results for $language..."
200+
201+
local results_dir="${work_dir}/${language}/test-results"
202+
if [[ ! -d "$results_dir" ]]; then
203+
log_error "Test results directory not found: $results_dir"
204+
return 1
205+
fi
206+
207+
# Find the QLT project using centralized variables
208+
if [[ ! -f "$QLT_PROJECT_FILE" ]]; then
209+
log_error "Could not find QLT project at: $QLT_PROJECT_FILE"
210+
return 1
211+
fi
212+
213+
# Change to the work directory since that's where QLT expects to be run from
214+
local old_pwd=$(pwd)
215+
cd "$work_dir" || return 1
216+
217+
local validation_output
218+
if ! validation_output=$(dotnet run --project "$QLT_PROJECT_FILE" -- test run validate-unit-tests --pretty-print --results-directory "$results_dir" 2>&1); then
219+
log_error "Test result validation failed"
220+
if [[ "$VERBOSE" == "true" ]]; then
221+
log_error "$validation_output"
222+
fi
223+
cd "$old_pwd"
224+
return 1
225+
fi
226+
227+
cd "$old_pwd"
228+
229+
if [[ "$VERBOSE" == "true" ]]; then
230+
log_info "Validation output:"
231+
echo "$validation_output"
232+
fi
233+
234+
log_success "Test result validation passed"
235+
return 0
236+
}
237+
238+
# Main execution function
239+
main() {
240+
parse_args "$@"
241+
validate_params
242+
243+
log_info "Starting unit test execution..."
244+
log_info "Test directory: $TEST_DIR"
245+
log_info "Languages: $LANGUAGES"
246+
247+
# Convert comma-separated languages to array
248+
IFS=',' read -ra LANG_ARRAY <<< "$LANGUAGES"
249+
250+
local passed=0
251+
local failed=0
252+
local failed_languages=()
253+
254+
# Process each language
255+
for language in "${LANG_ARRAY[@]}"; do
256+
language=$(echo "$language" | xargs) # trim whitespace
257+
258+
log_info "Processing language: $language"
259+
260+
# Check if language directory exists
261+
local lang_dir="$TEST_DIR/$language"
262+
if [[ ! -d "$lang_dir" ]]; then
263+
log_warning "Language directory not found: $lang_dir, skipping..."
264+
continue
265+
fi
266+
267+
# Run tests for this language
268+
local language_failed=false
269+
if ! run_unit_tests "$language" "$TEST_DIR"; then
270+
log_error "Unit test execution failed for $language"
271+
language_failed=true
272+
fi
273+
274+
# Update counters
275+
if [[ "$language_failed" == "true" ]]; then
276+
((failed++))
277+
failed_languages+=("$language")
278+
log_error "Language $language validation failed"
279+
else
280+
((passed++))
281+
log_success "Language $language validation complete"
282+
fi
283+
284+
# Validate all test results if any tests were run
285+
if [[ $((passed + failed)) -gt 0 ]]; then
286+
if ! validate_test_results "$language" "$TEST_DIR"; then
287+
log_error "Unit test validation failed"
288+
((failed++))
289+
else
290+
log_success "Unit test validation passed"
291+
fi
292+
fi
293+
done
294+
295+
# Print summary
296+
echo
297+
log_info "=== Unit Test Summary ==="
298+
log_success "Unit Tests Passed: $passed"
299+
if [[ $failed -gt 0 ]]; then
300+
log_error "Unit Tests Failed: $failed"
301+
if [[ ${#failed_languages[@]} -gt 0 ]]; then
302+
log_error "Unit Tests Failed for Languages: ${failed_languages[*]}"
303+
fi
304+
else
305+
log_success "No Unit Tests Failed"
306+
fi
307+
308+
if [[ $failed -eq 0 ]]; then
309+
log_success "All unit tests passed! 🎉"
310+
exit 0
311+
else
312+
log_error "Some unit tests failed. 😞"
313+
exit 1
314+
fi
315+
}
316+
317+
# Run main function with all arguments
318+
main "$@"

0 commit comments

Comments
 (0)