Add generic plugin test runner script#10963
Conversation
|
Every now and then, I need to run an ad-hoc plugin rake task (usually |
Plugins like Katello and foreman_puppet have their own wrapper scripts or Rakefile customizations to make it easier to run individual test files without manually switching to the Foreman directory and setting up cross-project load paths. This script generalizes that pattern for all plugins. Supports running from the Foreman checkout, a plugin checkout, or anywhere on PATH with automatic Foreman/plugin discovery. Single-file runs use direct ruby invocation for faster feedback; full-suite runs delegate to the plugin's existing rake tasks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
I think that's fair. Do you think we can count on the pattern always including |
I'd prefer this to also cover cases like string extraction where the plugin-name-prefix is not there (it is |
7717fdf to
00ebd76
Compare
| gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true | ||
| if [[ -n "$gemspec" ]]; then | ||
| basename "$gemspec" .gemspec | ||
| fi |
There was a problem hiding this comment.
Here we'll run into foreman-tasks being silly with spaces and underscores. The gemspec is foreman-tasks.gemspec, but the rake task prefix is foreman_tasks.
| Usage: plugin-test [PLUGIN_NAME] [TEST_FILES|RAKE_TASKS...] [OPTIONS...] | ||
|
|
||
| Run Foreman plugin tests or rake tasks from any directory. | ||
|
|
||
| Arguments: | ||
| PLUGIN_NAME Plugin gem name (e.g. katello, foreman_puppet). | ||
| Optional when running from a plugin checkout or | ||
| when test file paths include the plugin directory. | ||
| TEST_FILES One or more test files to run. Paths are resolved | ||
| relative to the current directory or the plugin root. | ||
| RAKE_TASKS One or more rake tasks to run. Any argument containing | ||
| ":" is treated as a rake task (e.g. katello:reindex). | ||
| OPTIONS Extra arguments passed through to the test runner | ||
| (e.g. --name test_create). | ||
|
|
||
| Examples: | ||
| plugin-test katello # full suite via rake | ||
| plugin-test katello test/models/repo_test.rb # single file | ||
| plugin-test test/models/repo_test.rb # from plugin checkout | ||
| plugin-test ../katello/test/models/repo_test.rb # direct path from foreman | ||
| plugin-test test/models/repo_test.rb --name test_create | ||
| plugin-test foreman_theme_satellite:validate_docs # run a rake task |
There was a problem hiding this comment.
Could we s/plugin-test/\$0 everywhere so the script can be freely renamed? Alternatively, use a more descriptive name like foreman-plugin-test?
There was a problem hiding this comment.
foreman-plugin-test sounds fine. Be great if we had some prefix that wasn't "foreman" since a hundred things share that bash-completion and name 😁
| local dir="$1" | ||
| for candidate in "$dir" "$dir/foreman" "$dir/../foreman" "$dir/../../foreman"; do | ||
| if [[ -f "$candidate/config/application.rb" ]]; then | ||
| (cd "$candidate" && pwd) |
There was a problem hiding this comment.
Personal preference, feel free to ignore
| (cd "$candidate" && pwd) | |
| readlink -f "$candidate" |
| detect_plugin_name() { | ||
| local dir="$1" | ||
| local gemspec | ||
| gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true |
There was a problem hiding this comment.
Personal preference, feel free to ignore
I had to look up what compgen does, wouldn't this work as well?
| gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true | |
| gemspec=$(ls -1 "$dir"/*.gemspec | head -1) || true |
| local depth=0 | ||
| while [[ "$dir" != "/" ]] && [[ $depth -lt 10 ]]; do | ||
| if compgen -G "$dir"/*.gemspec &>/dev/null; then | ||
| echo "$dir" | ||
| return 0 | ||
| fi | ||
| dir=$(dirname "$dir") | ||
| depth=$((depth + 1)) | ||
| done |
There was a problem hiding this comment.
| local depth=0 | |
| while [[ "$dir" != "/" ]] && [[ $depth -lt 10 ]]; do | |
| if compgen -G "$dir"/*.gemspec &>/dev/null; then | |
| echo "$dir" | |
| return 0 | |
| fi | |
| dir=$(dirname "$dir") | |
| depth=$((depth + 1)) | |
| done | |
| git -C "$dir" rev-parse --show-toplevel 2>/dev/null |
| INCLUDE_PATHS="lib:test" | ||
| if [[ -n "$PLUGIN_PATH" ]]; then | ||
| [[ -d "$PLUGIN_PATH/test" ]] && INCLUDE_PATHS="$INCLUDE_PATHS:$PLUGIN_PATH/test" | ||
| [[ -d "$PLUGIN_PATH/spec" ]] && INCLUDE_PATHS="$INCLUDE_PATHS:$PLUGIN_PATH/spec" | ||
| fi | ||
|
|
||
| RAKE_PATH=$(bundle info rake --path) || { | ||
| echo "Error: Could not find rake in the bundle." >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| exec bundle exec ruby \ | ||
| -I"$INCLUDE_PATHS" \ | ||
| -I"${RAKE_PATH}/lib" \ | ||
| "${TEST_FILES[@]}" \ | ||
| "${OTHER_OPTS[@]}" |
There was a problem hiding this comment.
I can't say I'm exactly happy about inventing yet another way of running the tests by modifying the include path. Considering we're limited to a single plugin, wouldn't this sort of work
exec bundle exec rake test:"$PLUGIN_NAME" "${OTHER_OPTS[@]}" TEST="$TEST_FILES[@]"
I'm a bit rusty with bashisms, so this might need some tweaking, but the point should be clear.
There was a problem hiding this comment.
I don't follow what the issue is, or what you are requesting a change for. Is it the use of ruby vs rake ?
There was a problem hiding this comment.
Test rake tasks can declare additional paths to be put on the load path. By invoking plugin tests without rake, we might be missing those additional paths. It is an edge case, true, but still.
There was a problem hiding this comment.
Those can fall back to the supply a rake task method. The original ktest tried to solve two problems:
- make it easy to run Katello test's from it's home directory
- speed up execution
The use of rake loads more of the environment than is needed. One option I have not explored is to try to have a special Rakefile for testing that's as fast as running ruby directly. Similar to how Rakefile.dist is much quicker for the tasks it handles because it avoids loading all of Rails.
There was a problem hiding this comment.
Those can fall back to the supply a rake task method.
Fair
One option I have not explored is to try to have a special Rakefile for testing
That's tempting
There was a problem hiding this comment.
The syntax (if it works) would be:
rake -f Rakefile.test test:single TEST=path/to/test.rb PLUGIN_PATH=/path/to/plugin
vs. this script:
# 1. From the Foreman checkout: plugin-test katello test/models/foo_test.rb
# 2. From a plugin checkout: plugin-test test/models/foo_test.rb
# 3. From anywhere (on PATH): plugin-test katello test/models/foo_test.rb
# 4. With direct paths: plugin-test ../katello/test/models/foo_test.rb
There was a problem hiding this comment.
When you put it like that, the script feels more ergonomic
Plugins like Katello and foreman_puppet have their own wrapper scripts or Rakefile customizations to make it easier to run individual test files without manually switching to the Foreman directory and setting up cross-project load paths. This script generalizes that pattern for all plugins.
Supports running from the Foreman checkout, a plugin checkout, or anywhere on PATH with automatic Foreman/plugin discovery. Single-file runs use direct ruby invocation for faster feedback; full-suite runs delegate to the plugin's existing rake tasks.
I am submitting this as a replacement for ktest to be available for any plugin, and support different paths of usage. And instead of this living inside our development deployment tooling, to live as a script in the main repository and thus allow it's usage from more places.
In the use case of
foremanctl, the development deployer would copy the script to the users local bin directory to make it broadly available within the environment.