Skip to content

Add generic plugin test runner script#10963

Open
ehelms wants to merge 1 commit into
theforeman:developfrom
ehelms:add-plugin-test-script
Open

Add generic plugin test runner script#10963
ehelms wants to merge 1 commit into
theforeman:developfrom
ehelms:add-plugin-test-script

Conversation

@ehelms
Copy link
Copy Markdown
Member

@ehelms ehelms commented Apr 27, 2026

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.

@adamruzicka
Copy link
Copy Markdown
Contributor

Every now and then, I need to run an ad-hoc plugin rake task (usually foreman_theme_satellite:validate_docs), could this be extended to be able to run arbitrary rake tasks or would that be out of scope?

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>
@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Apr 27, 2026

Every now and then, I need to run an ad-hoc plugin rake task (usually foreman_theme_satellite:validate_docs), could this be extended to be able to run arbitrary rake tasks or would that be out of scope?

I think that's fair. Do you think we can count on the pattern always including : to differentiate rake tasks from test files? Or do you think it will need an option like --rake ?

@adamruzicka
Copy link
Copy Markdown
Contributor

Or do you think it will need an option like --rake ?

I'd prefer this to also cover cases like string extraction where the plugin-name-prefix is not there (it is plugin:gettext[$plugin])

@ehelms ehelms force-pushed the add-plugin-test-script branch from 7717fdf to 00ebd76 Compare April 27, 2026 14:23
Comment thread script/plugin-test
Comment on lines +60 to +63
gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true
if [[ -n "$gemspec" ]]; then
basename "$gemspec" .gemspec
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread script/plugin-test
Comment on lines +20 to +41
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we s/plugin-test/\$0 everywhere so the script can be freely renamed? Alternatively, use a more descriptive name like foreman-plugin-test?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 😁

Comment thread script/plugin-test
local dir="$1"
for candidate in "$dir" "$dir/foreman" "$dir/../foreman" "$dir/../../foreman"; do
if [[ -f "$candidate/config/application.rb" ]]; then
(cd "$candidate" && pwd)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal preference, feel free to ignore

Suggested change
(cd "$candidate" && pwd)
readlink -f "$candidate"

Comment thread script/plugin-test
detect_plugin_name() {
local dir="$1"
local gemspec
gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal preference, feel free to ignore
I had to look up what compgen does, wouldn't this work as well?

Suggested change
gemspec=$(compgen -G "$dir"/*.gemspec | head -1) || true
gemspec=$(ls -1 "$dir"/*.gemspec | head -1) || true

Comment thread script/plugin-test
Comment on lines +68 to +76
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment thread script/plugin-test
Comment on lines +186 to +201
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[@]}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow what the issue is, or what you are requesting a change for. Is it the use of ruby vs rake ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those can fall back to the supply a rake task method. The original ktest tried to solve two problems:

  1. make it easy to run Katello test's from it's home directory
  2. 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you put it like that, the script feels more ergonomic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants