ace-test-runner-e2e 0.29.6 → 0.38.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.ace-defaults/e2e-runner/config.yml +14 -2
  3. data/CHANGELOG.md +187 -0
  4. data/README.md +2 -2
  5. data/exe/ace-test-e2e-sh +9 -4
  6. data/handbook/guides/e2e-testing.g.md +43 -9
  7. data/handbook/guides/scenario-yml-reference.g.md +16 -8
  8. data/handbook/guides/tc-authoring.g.md +12 -5
  9. data/handbook/skills/as-e2e-fix/SKILL.md +2 -2
  10. data/handbook/skills/as-e2e-review/SKILL.md +2 -2
  11. data/handbook/templates/ace-taskflow-fixture.template.md +17 -17
  12. data/handbook/templates/agent-experience-report.template.md +3 -2
  13. data/handbook/templates/scenario.yml.template.yml +13 -2
  14. data/handbook/templates/tc-file.template.md +14 -4
  15. data/handbook/workflow-instructions/e2e/analyze-failures.wf.md +53 -6
  16. data/handbook/workflow-instructions/e2e/create.wf.md +139 -23
  17. data/handbook/workflow-instructions/e2e/execute.wf.md +11 -7
  18. data/handbook/workflow-instructions/e2e/fix.wf.md +65 -15
  19. data/handbook/workflow-instructions/e2e/plan-changes.wf.md +17 -1
  20. data/handbook/workflow-instructions/e2e/review.wf.md +44 -28
  21. data/handbook/workflow-instructions/e2e/rewrite.wf.md +17 -3
  22. data/handbook/workflow-instructions/e2e/run.wf.md +50 -26
  23. data/handbook/workflow-instructions/e2e/setup-sandbox.wf.md +4 -4
  24. data/lib/ace/test/end_to_end_runner/atoms/skill_prompt_builder.rb +7 -5
  25. data/lib/ace/test/end_to_end_runner/atoms/skill_result_parser.rb +73 -7
  26. data/lib/ace/test/end_to_end_runner/cli/commands/run_test.rb +21 -8
  27. data/lib/ace/test/end_to_end_runner/models/test_case.rb +8 -2
  28. data/lib/ace/test/end_to_end_runner/models/test_result.rb +9 -3
  29. data/lib/ace/test/end_to_end_runner/models/test_scenario.rb +4 -2
  30. data/lib/ace/test/end_to_end_runner/molecules/affected_detector.rb +7 -2
  31. data/lib/ace/test/end_to_end_runner/molecules/bwrap_sandbox_backend.rb +271 -0
  32. data/lib/ace/test/end_to_end_runner/molecules/config_loader.rb +28 -1
  33. data/lib/ace/test/end_to_end_runner/molecules/integration_runner.rb +122 -0
  34. data/lib/ace/test/end_to_end_runner/molecules/pipeline_executor.rb +165 -25
  35. data/lib/ace/test/end_to_end_runner/molecules/pipeline_prompt_bundler.rb +121 -8
  36. data/lib/ace/test/end_to_end_runner/molecules/pipeline_report_generator.rb +91 -19
  37. data/lib/ace/test/end_to_end_runner/molecules/pipeline_sandbox_builder.rb +119 -18
  38. data/lib/ace/test/end_to_end_runner/molecules/report_writer.rb +13 -12
  39. data/lib/ace/test/end_to_end_runner/molecules/sandbox_runtime_builder.rb +282 -0
  40. data/lib/ace/test/end_to_end_runner/molecules/scenario_loader.rb +85 -5
  41. data/lib/ace/test/end_to_end_runner/molecules/setup_executor.rb +98 -16
  42. data/lib/ace/test/end_to_end_runner/molecules/suite_report_writer.rb +241 -97
  43. data/lib/ace/test/end_to_end_runner/molecules/test_discoverer.rb +38 -13
  44. data/lib/ace/test/end_to_end_runner/molecules/test_executor.rb +27 -5
  45. data/lib/ace/test/end_to_end_runner/organisms/suite_orchestrator.rb +73 -15
  46. data/lib/ace/test/end_to_end_runner/organisms/test_orchestrator.rb +120 -19
  47. data/lib/ace/test/end_to_end_runner/version.rb +1 -1
  48. data/lib/ace/test/end_to_end_runner.rb +2 -0
  49. metadata +19 -2
@@ -3,6 +3,7 @@
3
3
  require "open3"
4
4
  require "fileutils"
5
5
  require "yaml"
6
+ require "set"
6
7
  require "ace/b36ts"
7
8
 
8
9
  module Ace
@@ -57,6 +58,7 @@ module Ace
57
58
  # @option options [Integer] :timeout Timeout per test in seconds
58
59
  # @return [Hash] Summary of results
59
60
  def run(options = {})
61
+ pre_run_worktree = git_status_snapshot
60
62
  packages = @discoverer.list_packages(base_dir: @base_dir)
61
63
 
62
64
  if packages.empty?
@@ -135,9 +137,9 @@ module Ace
135
137
 
136
138
  # Execute tests
137
139
  if options[:parallel]
138
- run_parallel(package_tests, options)
140
+ run_parallel(package_tests, options, pre_run_worktree)
139
141
  else
140
- run_sequential(package_tests, options)
142
+ run_sequential(package_tests, options, pre_run_worktree)
141
143
  end
142
144
  end
143
145
 
@@ -210,7 +212,7 @@ module Ace
210
212
  # @param package_tests [Hash] Package to tests mapping
211
213
  # @param options [Hash] Execution options
212
214
  # @return [Hash] Summary of results
213
- def run_sequential(package_tests, options)
215
+ def run_sequential(package_tests, options, pre_run_worktree)
214
216
  results = {total: 0, passed: 0, failed: 0, errors: 0, total_cases: 0, passed_cases: 0, packages: {}}
215
217
  start_time = Time.now
216
218
 
@@ -265,7 +267,7 @@ module Ace
265
267
  done = true
266
268
  refresh_thread&.join
267
269
 
268
- finalize_run(results, package_tests, start_time)
270
+ finalize_run(results, package_tests, start_time, pre_run_worktree)
269
271
  end
270
272
 
271
273
  # Run tests in parallel using subprocesses
@@ -273,7 +275,7 @@ module Ace
273
275
  # @param package_tests [Hash] Package to tests mapping
274
276
  # @param options [Hash] Execution options
275
277
  # @return [Hash] Summary of results
276
- def run_parallel(package_tests, options)
278
+ def run_parallel(package_tests, options, pre_run_worktree)
277
279
  results = {total: 0, passed: 0, failed: 0, errors: 0, total_cases: 0, passed_cases: 0, packages: {}}
278
280
  queue = build_test_queue(package_tests)
279
281
  run_ids = generate_run_ids(queue.size)
@@ -297,7 +299,7 @@ module Ace
297
299
  check_running_processes(running, results)
298
300
  end
299
301
 
300
- finalize_run(results, package_tests, start_time)
302
+ finalize_run(results, package_tests, start_time, pre_run_worktree)
301
303
  end
302
304
 
303
305
  # Build a flat queue of test items
@@ -497,6 +499,7 @@ module Ace
497
499
  # @return [Hash] Parsed result with :passed_cases and :total_cases
498
500
  def parse_subprocess_result(process)
499
501
  result = parse_test_output(process[:output], process[:thread].value.exitstatus, extract_test_name(process[:test_file]))
502
+ result[:report_dir] = normalize_report_dir(result[:report_dir], result[:test_name])
500
503
  result[:raw_output] = process[:output]
501
504
 
502
505
  # For non-pass results, check agent-written metadata as authoritative source
@@ -510,6 +513,34 @@ module Ace
510
513
  {status: "error", error: "Failed to parse result: #{e.message}"}
511
514
  end
512
515
 
516
+ def normalize_report_dir(report_dir, test_name)
517
+ return report_dir if report_dir.nil? || report_dir.empty?
518
+ return report_dir if File.directory?(report_dir)
519
+ return report_dir unless File.file?(report_dir)
520
+
521
+ resolved = resolve_report_dir_from_suite_report(report_dir, canonical_test_id(test_name))
522
+ resolved || report_dir
523
+ rescue
524
+ report_dir
525
+ end
526
+
527
+ def resolve_report_dir_from_suite_report(report_path, test_id)
528
+ return nil unless report_path.end_with?(".md")
529
+ return nil if test_id.nil? || test_id.empty?
530
+
531
+ content = File.read(report_path)
532
+ escaped = Regexp.escape(test_id)
533
+ table_match = content.match(/^\|\s*#{escaped}\s*\|\s*`([^`]+)`\s*\|$/m)
534
+ return nil unless table_match
535
+
536
+ File.expand_path(table_match[1], File.dirname(report_path))
537
+ end
538
+
539
+ def canonical_test_id(test_name)
540
+ match = test_name.to_s.match(/\A(TS-[A-Z0-9]+-\d+[a-z]*)/i)
541
+ match ? match[1].upcase : test_name
542
+ end
543
+
513
544
  # Override result from agent-written metadata.yml when subprocess exit code is misleading
514
545
  #
515
546
  # @param result [Hash] Parsed result with :report_dir
@@ -576,7 +607,9 @@ module Ace
576
607
  error_msg ||= "Test execution returned ERROR status"
577
608
  base.merge(status: "error", error: error_msg)
578
609
  else
579
- summary = output.match(/(\d+)\/(\d+) passed/)&.captures&.join("/") || "Test failed"
610
+ summary = output.lines.filter_map { |line| line[/^(Preflight failed: .+?)\s*$/, 1] }.last
611
+ summary ||= output.match(/(\d+)\/(\d+) passed/)&.captures&.join("/")
612
+ summary ||= "Test failed"
580
613
  base.merge(status: "fail", summary: summary)
581
614
  end
582
615
  rescue => e
@@ -589,8 +622,9 @@ module Ace
589
622
  # @param package_tests [Hash] Package to test files mapping
590
623
  # @param start_time [Time] When the run started
591
624
  # @return [Hash] Results with optional :report_path
592
- def finalize_run(results, package_tests, start_time)
625
+ def finalize_run(results, package_tests, start_time, pre_run_worktree)
593
626
  write_failure_stubs(results, package_tests)
627
+ results[:suite_diagnostics] = build_suite_diagnostics(pre_run_worktree)
594
628
 
595
629
  @display.show_summary(results, Time.now - start_time)
596
630
  warn_on_lingering_claude_processes
@@ -641,6 +675,7 @@ module Ace
641
675
  "status" => result[:status]
642
676
  }
643
677
  File.write(File.join(stub_dir, "metadata.yml"), YAML.dump(stub_data))
678
+ result[:report_dir] = stub_dir
644
679
 
645
680
  if result[:raw_output] && !result[:raw_output].empty?
646
681
  File.write(File.join(stub_dir, "subprocess_output.log"), result[:raw_output])
@@ -709,7 +744,9 @@ module Ace
709
744
  all_results, all_scenarios,
710
745
  package: "suite",
711
746
  timestamp: timestamp,
712
- base_dir: @base_dir
747
+ base_dir: @base_dir,
748
+ report_kind: :suite,
749
+ diagnostics: results[:suite_diagnostics]
713
750
  )
714
751
  rescue => e
715
752
  warn "Warning: Suite report generation failed (#{e.class}: #{e.message})"
@@ -726,19 +763,40 @@ module Ace
726
763
  total = result_hash[:total_cases] || 0
727
764
  failed = [total - passed, 0].max
728
765
 
729
- test_cases = []
730
- passed.times { |i| test_cases << {id: "TC-#{format("%03d", i + 1)}", description: "", status: "pass"} }
731
- failed.times { |i| test_cases << {id: "TC-#{format("%03d", passed + i + 1)}", description: "", status: "fail"} }
732
-
733
766
  Models::TestResult.new(
734
767
  test_id: result_hash[:test_name] || "unknown",
735
768
  status: result_hash[:status] || "error",
736
- test_cases: test_cases,
769
+ test_cases: [],
737
770
  summary: result_hash[:summary] || result_hash[:error] || "",
738
- report_dir: result_hash[:report_dir]
771
+ report_dir: result_hash[:report_dir],
772
+ metadata: {"tcs-passed" => passed, "tcs-total" => total, "tcs-failed" => failed}
739
773
  )
740
774
  end
741
775
 
776
+ def git_status_snapshot
777
+ stdout, _stderr, status = Open3.capture3("git", "status", "--short", chdir: @base_dir)
778
+ return nil unless status.success?
779
+
780
+ stdout.lines.map(&:rstrip)
781
+ rescue
782
+ nil
783
+ end
784
+
785
+ def build_suite_diagnostics(pre_run_worktree)
786
+ post_run_worktree = git_status_snapshot
787
+ return {} unless pre_run_worktree && post_run_worktree
788
+
789
+ before = pre_run_worktree.to_set
790
+ new_entries = post_run_worktree.reject { |line| before.include?(line) }
791
+ new_tracked_entries = new_entries.reject { |line| line.start_with?("?? ") }
792
+ return {} if new_tracked_entries.empty?
793
+
794
+ {
795
+ dirty_worktree: true,
796
+ new_tracked_entries: new_tracked_entries
797
+ }
798
+ end
799
+
742
800
  # Load a scenario from file into a Models::TestScenario, with fallback
743
801
  #
744
802
  # @param package [String] Package name
@@ -4,6 +4,8 @@ require "fileutils"
4
4
  require "date"
5
5
  require "yaml"
6
6
  require "ace/b36ts"
7
+ require "ace/test_support/sandbox_package_copy"
8
+ require "ace/test/end_to_end_runner/molecules/integration_runner"
7
9
 
8
10
  module Ace
9
11
  module Test
@@ -28,19 +30,29 @@ module Ace
28
30
  # @param timestamp_generator [#call] Callable that returns a timestamp string
29
31
  # @param executor [#execute] Injectable test executor (for testing)
30
32
  # @param progress [Boolean] Enable animated progress display
31
- def initialize(provider: nil, timeout: nil, parallel: nil, base_dir: nil, timestamp_generator: nil, executor: nil, progress: false)
33
+ def initialize(provider: nil, timeout: nil, parallel: nil, base_dir: nil, timestamp_generator: nil,
34
+ executor: nil, progress: false, discoverer: nil, integration_runner: nil,
35
+ scenario_loader: nil, report_writer: nil, suite_report_writer: nil,
36
+ setup_executor_factory: nil, runtime_builder: nil)
32
37
  config = Molecules::ConfigLoader.load
33
- @provider = provider || config.dig("execution", "provider") || "claude:sonnet"
38
+ @provider = provider || config.dig("execution", "runner_provider") ||
39
+ config.dig("execution", "provider") || "claude:sonnet"
34
40
  @timeout = timeout || config.dig("execution", "timeout") || 300
35
41
  @parallel = parallel || config.dig("execution", "parallel") || 3
36
42
  @base_dir = base_dir || Dir.pwd
37
43
  @timestamp_generator = timestamp_generator || method(:default_timestamp)
38
44
  @progress = progress
39
- @discoverer = Molecules::TestDiscoverer.new
40
- @loader = Molecules::ScenarioLoader.new
45
+ @discoverer = discoverer || Molecules::TestDiscoverer.new
46
+ @integration_runner = integration_runner || Molecules::IntegrationRunner.new(base_dir: @base_dir)
47
+ @loader = scenario_loader || Molecules::ScenarioLoader.new
41
48
  @executor = executor || Molecules::TestExecutor.new(provider: @provider, timeout: @timeout, config: config)
42
- @report_writer = Molecules::ReportWriter.new
43
- @suite_report_writer = Molecules::SuiteReportWriter.new(config: config)
49
+ @report_writer = report_writer || Molecules::ReportWriter.new
50
+ @suite_report_writer = suite_report_writer || Molecules::SuiteReportWriter.new(config: config)
51
+ @setup_executor_factory = setup_executor_factory || ->(sandbox_backend: nil) { Molecules::SetupExecutor.new(sandbox_backend: sandbox_backend) }
52
+ @runtime_builder = runtime_builder || Molecules::SandboxRuntimeBuilder.new(
53
+ source_root: @base_dir,
54
+ ruby_version: config.dig("sandbox", "ruby_version") || Molecules::ConfigLoader.default_sandbox_ruby_version
55
+ )
44
56
  end
45
57
 
46
58
  # Run E2E tests for a package, optionally filtering by test ID
@@ -54,6 +66,11 @@ module Ace
54
66
  # @return [Array<Models::TestResult>] List of test results
55
67
  def run(package:, test_id: nil, test_cases: nil, verify: false, tags: nil,
56
68
  cli_args: nil, run_id: nil, report_dir: nil, output: $stdout)
69
+ integration_files = @discoverer.find_integration_tests(
70
+ package: package,
71
+ base_dir: @base_dir
72
+ )
73
+
57
74
  # Discover tests
58
75
  files = @discoverer.find_tests(
59
76
  package: package,
@@ -62,7 +79,7 @@ module Ace
62
79
  base_dir: @base_dir
63
80
  )
64
81
 
65
- if files.empty?
82
+ if files.empty? && integration_files.empty?
66
83
  output.puts "No E2E tests found in #{package}" +
67
84
  (test_id ? " matching #{test_id}" : "")
68
85
  return []
@@ -71,7 +88,7 @@ module Ace
71
88
  # Generate timestamp for this run (use external run_id when provided)
72
89
  timestamp = run_id || generate_timestamp
73
90
 
74
- if files.size == 1
91
+ if files.size == 1 && integration_files.empty?
75
92
  run_single_test(
76
93
  files.first,
77
94
  timestamp,
@@ -82,7 +99,16 @@ module Ace
82
99
  report_dir: report_dir
83
100
  )
84
101
  else
85
- run_package_tests(files, package, timestamp, cli_args, output, test_cases: test_cases, verify: verify)
102
+ run_package_tests(
103
+ files,
104
+ package,
105
+ timestamp,
106
+ cli_args,
107
+ output,
108
+ test_cases: test_cases,
109
+ verify: verify,
110
+ integration_files: integration_files
111
+ )
86
112
  end
87
113
  end
88
114
 
@@ -107,13 +133,43 @@ module Ace
107
133
  return [nil, nil, nil] unless cli_provider? && scenario.setup_steps.any?
108
134
 
109
135
  sandbox_dir = File.join(@base_dir, ".ace-local", "test-e2e", scenario.dir_name(timestamp))
110
- setup_executor = Molecules::SetupExecutor.new
136
+ package_copy = Ace::TestSupport::SandboxPackageCopy.new(source_root: @base_dir)
137
+ package_source = File.join(@base_dir, scenario.package.to_s)
138
+ package_copy_result = if File.directory?(package_source)
139
+ package_copy.prepare(
140
+ package_name: scenario.package,
141
+ sandbox_root: sandbox_dir
142
+ )
143
+ else
144
+ {
145
+ env: {
146
+ "PROJECT_ROOT_PATH" => File.expand_path(sandbox_dir),
147
+ "ACE_E2E_SOURCE_ROOT" => File.expand_path(@base_dir)
148
+ }
149
+ }
150
+ end
151
+ Molecules::PipelineSandboxBuilder.new(config_root: @base_dir).sync_protocol_sources_into(sandbox_dir)
152
+ runtime_result = @runtime_builder.prepare(
153
+ sandbox_root: sandbox_dir,
154
+ env: package_copy_result[:env],
155
+ tool_names: scenario.requires.fetch("tools", [])
156
+ )
157
+ sandbox_backend = Molecules::BwrapSandboxBackend.new(
158
+ sandbox_root: sandbox_dir,
159
+ source_root: runtime_result.dig(:env, "ACE_E2E_SOURCE_ROOT")
160
+ )
161
+ setup_executor = if @setup_executor_factory.arity.zero?
162
+ @setup_executor_factory.call
163
+ else
164
+ @setup_executor_factory.call(sandbox_backend: sandbox_backend)
165
+ end
111
166
  result = setup_executor.execute(
112
- setup_steps: scenario.setup_steps,
167
+ setup_steps: effective_setup_steps_for(scenario),
113
168
  sandbox_dir: sandbox_dir,
114
169
  fixture_source: scenario.fixture_path,
115
170
  scenario_name: scenario.test_id,
116
- run_id: timestamp
171
+ run_id: timestamp,
172
+ initial_env: runtime_result[:env]
117
173
  )
118
174
 
119
175
  unless result[:success]
@@ -130,6 +186,29 @@ module Ace
130
186
  [File.expand_path(sandbox_dir), env, setup_executor]
131
187
  end
132
188
 
189
+ def effective_setup_steps_for(scenario)
190
+ steps = Array(scenario.setup_steps)
191
+ return steps unless scenario.sandbox_profile == "ace-default"
192
+
193
+ has_config_init = setup_contains_command?(steps, "ace-config init")
194
+ has_handbook_sync = setup_contains_command?(steps, "ace-handbook sync")
195
+ bootstrap = []
196
+ bootstrap << {"run" => "ace-config init"} unless has_config_init
197
+ bootstrap << {"run" => "ace-handbook sync"} unless has_handbook_sync
198
+ return steps if bootstrap.empty?
199
+
200
+ insert_after = steps.index("git-init")
201
+ return bootstrap + steps unless insert_after
202
+
203
+ steps.dup.insert(insert_after + 1, *bootstrap)
204
+ end
205
+
206
+ def setup_contains_command?(steps, fragment)
207
+ steps.any? do |step|
208
+ step.is_a?(Hash) && step["run"].to_s.include?(fragment)
209
+ end
210
+ end
211
+
133
212
  # Run a single test
134
213
  # @param test_cases [Array<String>, nil] Optional test case IDs to filter
135
214
  # @param report_dir [String, nil] Explicit report directory path (overrides computed path)
@@ -192,7 +271,23 @@ module Ace
192
271
  # Run all tests in a package
193
272
  # @param test_cases [Array<String>, nil] Optional test case IDs to filter
194
273
  # @return [Array<Models::TestResult>] Results for all tests
195
- def run_package_tests(files, package, timestamp, cli_args, output, test_cases: nil, verify: false)
274
+ def run_package_tests(files, package, timestamp, cli_args, output, test_cases: nil, verify: false,
275
+ integration_files: [])
276
+ integration_result = @integration_runner.run(
277
+ package: package,
278
+ files: integration_files,
279
+ timestamp: timestamp,
280
+ output: output
281
+ )
282
+ if integration_result && %w[fail error].include?(integration_result.status)
283
+ output.puts integration_result.summary
284
+ return [integration_result]
285
+ end
286
+
287
+ if files.empty?
288
+ return integration_result ? [integration_result] : []
289
+ end
290
+
196
291
  # Load scenarios upfront for titles and report generation
197
292
  scenarios = files.map { |f| @loader.load(File.dirname(f)) }
198
293
 
@@ -291,15 +386,17 @@ module Ace
291
386
  done = true
292
387
  refresh_thread&.join
293
388
 
389
+ combined_results = integration_result ? [integration_result] + results : results
390
+
294
391
  # Write suite report
295
392
  report_path = @suite_report_writer.write(
296
- results, scenarios,
297
- package: package, timestamp: timestamp, base_dir: @base_dir
393
+ combined_results, scenarios,
394
+ package: package, timestamp: timestamp, base_dir: @base_dir, report_kind: :package
298
395
  )
299
396
 
300
- display.show_summary(results, report_path)
397
+ display.show_summary(combined_results, report_path)
301
398
 
302
- results
399
+ combined_results
303
400
  end
304
401
 
305
402
  # Build the appropriate display manager for this run
@@ -332,12 +429,16 @@ module Ace
332
429
  # Uses Ace::B36ts library to encode unique IDs with 50ms precision,
333
430
  # ensuring distinct timestamps for parallel test runs.
334
431
  #
432
+ # Offset uses 0.1 (100ms) instead of 0.05 to avoid collisions with
433
+ # the 50ms encoder's approximate bucket size.
434
+ #
335
435
  # @param count [Integer] Number of unique timestamps needed
336
436
  # @return [Array<String>] Array of unique timestamp strings
337
437
  def generate_timestamps(count)
438
+ base_time = Time.now.utc
439
+
338
440
  count.times.map do |i|
339
- time = Time.now.utc + (i * 0.05) # 50ms offset per ID
340
- Ace::B36ts.encode(time, format: :"50ms")
441
+ Ace::B36ts.encode(base_time + (i * 0.1), format: :"50ms")
341
442
  end
342
443
  end
343
444
 
@@ -3,7 +3,7 @@
3
3
  module Ace
4
4
  module Test
5
5
  module EndToEndRunner
6
- VERSION = '0.29.6'
6
+ VERSION = '0.38.11'
7
7
  end
8
8
  end
9
9
  end
@@ -20,6 +20,8 @@ require_relative "end_to_end_runner/atoms/display_helpers"
20
20
  # Molecules
21
21
  require_relative "end_to_end_runner/molecules/fixture_copier"
22
22
  require_relative "end_to_end_runner/molecules/scenario_loader"
23
+ require_relative "end_to_end_runner/molecules/bwrap_sandbox_backend"
24
+ require_relative "end_to_end_runner/molecules/sandbox_runtime_builder"
23
25
  require_relative "end_to_end_runner/molecules/setup_executor"
24
26
  require_relative "end_to_end_runner/molecules/config_loader"
25
27
  require_relative "end_to_end_runner/molecules/test_discoverer"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-test-runner-e2e
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.6
4
+ version: 0.38.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-04-01 00:00:00.000000000 Z
10
+ date: 2026-04-20 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-cli
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.9'
54
+ - !ruby/object:Gem::Dependency
55
+ name: ace-support-test-helpers
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.14'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.14'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: ace-llm
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -172,15 +186,18 @@ files:
172
186
  - lib/ace/test/end_to_end_runner/models/test_result.rb
173
187
  - lib/ace/test/end_to_end_runner/models/test_scenario.rb
174
188
  - lib/ace/test/end_to_end_runner/molecules/affected_detector.rb
189
+ - lib/ace/test/end_to_end_runner/molecules/bwrap_sandbox_backend.rb
175
190
  - lib/ace/test/end_to_end_runner/molecules/config_loader.rb
176
191
  - lib/ace/test/end_to_end_runner/molecules/failure_finder.rb
177
192
  - lib/ace/test/end_to_end_runner/molecules/fixture_copier.rb
193
+ - lib/ace/test/end_to_end_runner/molecules/integration_runner.rb
178
194
  - lib/ace/test/end_to_end_runner/molecules/pipeline_executor.rb
179
195
  - lib/ace/test/end_to_end_runner/molecules/pipeline_prompt_bundler.rb
180
196
  - lib/ace/test/end_to_end_runner/molecules/pipeline_report_generator.rb
181
197
  - lib/ace/test/end_to_end_runner/molecules/pipeline_sandbox_builder.rb
182
198
  - lib/ace/test/end_to_end_runner/molecules/progress_display_manager.rb
183
199
  - lib/ace/test/end_to_end_runner/molecules/report_writer.rb
200
+ - lib/ace/test/end_to_end_runner/molecules/sandbox_runtime_builder.rb
184
201
  - lib/ace/test/end_to_end_runner/molecules/scenario_loader.rb
185
202
  - lib/ace/test/end_to_end_runner/molecules/setup_executor.rb
186
203
  - lib/ace/test/end_to_end_runner/molecules/simple_display_manager.rb