Skip to content
Open
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
2 changes: 2 additions & 0 deletions docs/Test-config-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ Guest run command can use the following replacements:
- `@client_name@` - Client name
- `@run_number@` - The current run number of a test that is executed multiple times
- `@<driver_short_name>.driver_dir@` - Path to uploaded driver binary
- `@driver_short_name@` - Short name of driver (defined only in case of one driver is in test)
- `@driver_dir@` - Path to uploaded driver binary (defined only in case of one driver is in test)
7 changes: 6 additions & 1 deletion lib/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class TestOptions
:gthb_context_prefix, :gthb_context_suffix, :playlist, :select_test_names,
:reject_test_names, :reject_report_sections, :boot_device,
:allow_test_duplication, :manual, :package_with_playlist, :enable_vbs, :tag_suffix,
:fs_test_image_format
:fs_test_image_format, :extensions

def create_parser
OptionParser.new do |parser|
Expand All @@ -113,6 +113,7 @@ def create_parser
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def define_options(parser)
@reject_report_sections = []
@extensions = []
@svvp = false
@enable_vbs = false

Expand Down Expand Up @@ -205,6 +206,10 @@ def define_options(parser)
'Filesystem test image format (qcow2/raw). Default is qcow2.',
'Has effect only when testing storage drivers.',
&method(:fs_test_image_format=))

parser.on('--extensions <extensions_list>', Array,
'List of extensions for run test',
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

Grammar issue: 'for run test' should be 'to run tests' or 'for running tests'.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cursor VS Copilot haha

&method(:extensions=))
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
end
Expand Down
24 changes: 14 additions & 10 deletions lib/engines/hckinstall/hckinstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,7 @@ def run_second(client:)
end
end

def prepare_setup_scripts_config
kit_type, kit_version = parse_kit_info

config = {
kit_type:,
hlk_kit_ver: kit_version,
debug: @project.options.install.debug,
no_reboot_after_bugcheck: @project.options.install.no_reboot_after_bugcheck
}

def prepare_kit_installer(kit_type, kit_version)
@kit_path = find_kit(kit_type, kit_version)

if @kit_path.nil?
Expand All @@ -267,7 +258,20 @@ def prepare_setup_scripts_config
@kit_is_iso = @kit_path.end_with?('.iso')

@logger.info("HLK installer #{kit_type}#{kit_version} was found at #{@kit_path}")
end

def prepare_setup_scripts_config
kit_type, kit_version = parse_kit_info

options_install = @project.options.install
config = {
kit_type:,
hlk_kit_ver: kit_version,
debug: options_install.debug,
no_reboot_after_bugcheck: options_install.no_reboot_after_bugcheck
}

prepare_kit_installer(kit_type, kit_version)
create_setup_scripts_config(@hck_setup_scripts_path, config)
end

Expand Down
39 changes: 39 additions & 0 deletions lib/engines/hcktest/extensions/hcktrace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"extra_software": [
"hcktrace"
],
"post_start_commands": [
{
"desc": "Configure HCKTrace service for driver tracing",
"guest_run": "Start-Process -Wait -FilePath hcktrace.exe -ArgumentList driver,@driver_short_name@,@driver_dir@,4"
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The argument '4' at the end of the ArgumentList is unclear. Consider adding a comment in the JSON file or updating the desc field to explain what this numeric parameter represents (e.g., log level, trace verbosity).

Copilot uses AI. Check for mistakes.
}
],
"tests_config": [
{
"tests": [
".*"
],
"pre_test_commands": [
{
"desc": "Start HCKTrace recording",
"guest_run": "Start-Process -Wait -FilePath hcktrace.exe -ArgumentList test,@safe_test_name@"
}
],
"post_test_commands": [
{
"desc": "Stop HCKTrace recording",
"guest_run": "Start-Process -Wait -FilePath hcktrace.exe -ArgumentList test,end",
"files_action": [
{
"remote_path": "C:/hcktrace/zip",
"local_path": "@workspace@/trace-@safe_test_name@-@client_name@-@run_number@",
"direction": "remote-to-local",
"move": true,
"allow_missing": true
}
]
}
]
}
]
}
32 changes: 20 additions & 12 deletions lib/engines/hcktest/hcktest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ class HCKTest
extend T::Sig
include Helper

attr_reader :config, :drivers, :platform
attr_reader :config, :drivers, :platform, :extensions

PLATFORMS_JSON_DIR = 'lib/engines/hcktest/platforms'
CONFIG_JSON = 'lib/engines/hcktest/hcktest.json'
DRIVERS_JSON_DIR = 'lib/engines/hcktest/drivers'
EXTENSIONS_JSON_DIR = 'lib/engines/hcktest/extensions'
SVVP_JSON = 'svvp.json'
ENGINE_MODE = 'test'
AUTOHCK_RETRIES = 5
Expand All @@ -24,6 +25,7 @@ def initialize(project)
@config = Models::HCKTestConfig.from_json_file(CONFIG_JSON, @logger)
@driver_path = @project.options.test.driver_path
@drivers = find_drivers
@extensions = find_extensions
prepare_extra_sw
validate_paths unless @driver_path.nil?
end
Expand All @@ -33,18 +35,13 @@ def test_steps
end

def prepare_extra_sw
@drivers.each do |driver|
next if driver.extra_software.nil?

@project.extra_sw_manager.prepare_software_packages(
driver.extra_software, @project.engine_platform['kit'], ENGINE_MODE
)
end

return if @project.engine_platform['extra_software'].nil?
extra_softwares = []
extra_softwares += @drivers.flat_map(&:extra_software)
extra_softwares += @extensions.flat_map(&:extra_software)
extra_softwares += @project.engine_platform['extra_software'] || []

@project.extra_sw_manager.prepare_software_packages(
@platform['extra_software'], @platform['kit'], ENGINE_MODE
extra_softwares, @project.engine_platform['kit'], ENGINE_MODE
)
end

Expand Down Expand Up @@ -105,6 +102,14 @@ def find_drivers
end
end

sig { returns(T::Array[Models::Extension]) }
def find_extensions
@project.options.test.extensions.map do |ext_name|
@logger.info("Loading extension: #{ext_name}")
Models::Extension.from_json_file("#{EXTENSIONS_JSON_DIR}/#{ext_name}.json", @logger)
end
end

def target
if @project.options.test.svvp
{
Expand Down Expand Up @@ -159,6 +164,7 @@ def run_clients(scope, run_opts = {})

def post_start_commands
(@drivers.flat_map(&:post_start_commands) +
@extensions.flat_map(&:post_start_commands) +
@project.setup_manager.client_post_start_commands).select(&:host_run)
end

Expand Down Expand Up @@ -305,7 +311,9 @@ def run_tests_with_config
def group_tests_by_config
grouped_tests = { secure: [] }

tests_config = @config.tests_config + @drivers.flat_map(&:tests_config)
tests_config = @config.tests_config +
@drivers.flat_map(&:tests_config) +
@extensions.flat_map(&:tests_config)

tests_config.each do |test_group|
selected_tests = @test_list.select { |test| test_group.tests.include?(test.name) }
Expand Down
10 changes: 7 additions & 3 deletions lib/engines/hcktest/tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,14 @@ def wait_queued_test(id)
end
end

def select_test_config(test_name, config)
tests_config = @project.engine.config.tests_config + @project.engine.drivers.flat_map(&:tests_config)
def all_tests_configs
@project.engine.config.tests_config +
@project.engine.drivers.flat_map(&:tests_config) +
@project.engine.extensions.flat_map(&:tests_config)
end

tests_config
def select_test_config(test_name, config)
all_tests_configs
.select { |test_config| Regexp.union(test_config.tests.map { Regexp.new(_1) }).match?(test_name) }
.flat_map(&config)
end
Expand Down
1 change: 1 addition & 0 deletions lib/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Models
autoload_relative :CommandInfo, 'models/command_info'
autoload_relative :DriverInstallMethods, 'models/driver_install_methods'
autoload_relative :Driver, 'models/driver'
autoload_relative :Extension, 'models/extension'
autoload_relative :Kit, 'models/kit'
autoload_relative :TestConfig, 'models/test_config'
autoload_relative :HCKTestConfig, 'models/hcktest_config'
Expand Down
2 changes: 1 addition & 1 deletion lib/models/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Driver < T::Struct
const :enlightenments_state, T.nilable(T::Boolean)

const :post_start_commands, T::Array[CommandInfo], default: []
const :extra_software, T.nilable(T::Array[String])
const :extra_software, T::Array[String], default: []
const :reject_test_names, T.nilable(T::Array[String])
const :select_test_names, T.nilable(T::Array[String])
const :tests_config, T::Array[TestConfig], default: []
Expand Down
17 changes: 17 additions & 0 deletions lib/models/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# typed: strict
# frozen_string_literal: true

module AutoHCK
module Models
class Extension < T::Struct
extend T::Sig
extend JsonHelper

prop :short, T.nilable(String)

const :extra_software, T::Array[String], default: []
const :post_start_commands, T::Array[CommandInfo], default: []
const :tests_config, T::Array[TestConfig], default: []
end
end
end
15 changes: 13 additions & 2 deletions lib/setupmanagers/hckclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def set_machine_ready

def post_start_commands
(@project.engine.drivers.flat_map(&:post_start_commands) +
@project.engine.extensions.flat_map(&:post_start_commands) +
@setup_manager.client_post_start_commands).select(&:guest_run)
end

Expand Down Expand Up @@ -107,16 +108,26 @@ def install_driver(driver)
force_install_cert: driver.install_cert)
end

def insert_driver_replacement(driver, one_driver, replacement)
client_replacement = replacement.transform_keys { |k| k.dup.insert(1, "#{driver.short}.") }
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

This code assumes driver.short is non-nil, but the Driver model allows short to be T.nilable(String). If driver.short is nil, this will raise a NoMethodError when attempting string interpolation. Add a nil check or ensure driver.short is always present before calling this method.

Copilot uses AI. Check for mistakes.
@replacement_map.merge!(client_replacement)
@replacement_map.merge!({ '@driver_short_name@' => driver.short })

# If there is only one driver, we can use generic keys without driver short name.
@replacement_map.merge!(replacement) if one_driver
end
Comment on lines +111 to +118

Choose a reason for hiding this comment

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

high

The @driver_short_name@ replacement is being set unconditionally for every driver. In a multi-driver scenario, this will result in @driver_short_name@ holding the value of the last driver in the list, which can lead to incorrect behavior. According to the documentation, this replacement should only be available when a single driver is being tested.

To fix this, the logic for setting @driver_short_name@ should be moved inside the if one_driver block, along with the other generic driver replacements.

    def insert_driver_replacement(driver, one_driver, replacement)
      client_replacement = replacement.transform_keys { |k| k.dup.insert(1, "#{driver.short}.") }
      @replacement_map.merge!(client_replacement)

      # If there is only one driver, we can use generic keys without driver short name.
      if one_driver
        @replacement_map.merge!({ '@driver_short_name@' => driver.short })
        @replacement_map.merge!(replacement)
      end
    end


def install_drivers
one_driver = @project.engine.drivers.one?

@project.engine.drivers&.each do |driver|
if driver.install_method == AutoHCK::Models::DriverInstallMethods::NoDrviver
@project.logger.info("Driver installation skipped for #{driver.name} in #{@name}")
next
end

driver_replacement = install_driver(driver)
client_replacement = driver_replacement.transform_keys { |k| k.dup.insert(1, "#{driver.short}.") }
@replacement_map.merge!(client_replacement)
insert_driver_replacement(driver, one_driver, driver_replacement)
end

@logger.debug("Driver replacement list: #{@replacement_map.dump_string}")
Expand Down
Loading