megatest 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +156 -0
- data/TODO.md +17 -0
- data/exe/megatest +7 -0
- data/lib/megatest/assertions.rb +474 -0
- data/lib/megatest/backtrace.rb +70 -0
- data/lib/megatest/cli.rb +249 -0
- data/lib/megatest/compat.rb +74 -0
- data/lib/megatest/config.rb +281 -0
- data/lib/megatest/differ.rb +136 -0
- data/lib/megatest/dsl.rb +164 -0
- data/lib/megatest/executor.rb +104 -0
- data/lib/megatest/multi_process.rb +263 -0
- data/lib/megatest/output.rb +158 -0
- data/lib/megatest/patience_diff.rb +340 -0
- data/lib/megatest/pretty_print.rb +309 -0
- data/lib/megatest/queue.rb +239 -0
- data/lib/megatest/queue_monitor.rb +35 -0
- data/lib/megatest/queue_reporter.rb +42 -0
- data/lib/megatest/redis_queue.rb +459 -0
- data/lib/megatest/reporters.rb +266 -0
- data/lib/megatest/runner.rb +119 -0
- data/lib/megatest/runtime.rb +168 -0
- data/lib/megatest/selector.rb +293 -0
- data/lib/megatest/state.rb +708 -0
- data/lib/megatest/subprocess/main.rb +8 -0
- data/lib/megatest/subprocess.rb +48 -0
- data/lib/megatest/test.rb +115 -0
- data/lib/megatest/test_task.rb +132 -0
- data/lib/megatest/version.rb +5 -0
- data/lib/megatest.rb +123 -0
- metadata +80 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Megatest
|
|
4
|
+
class Subprocess
|
|
5
|
+
class << self
|
|
6
|
+
def spawn(read, write, action)
|
|
7
|
+
Process.spawn(
|
|
8
|
+
RbConfig.ruby,
|
|
9
|
+
File.expand_path("../subprocess/main.rb", __FILE__),
|
|
10
|
+
read.fileno.to_s,
|
|
11
|
+
write.fileno.to_s,
|
|
12
|
+
action,
|
|
13
|
+
read.fileno => read,
|
|
14
|
+
write.fileno => write,
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(read, write)
|
|
20
|
+
@read = read
|
|
21
|
+
@write = write
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def run(action)
|
|
25
|
+
case action
|
|
26
|
+
when "run_test"
|
|
27
|
+
config = Marshal.load(@read)
|
|
28
|
+
Megatest.init(config)
|
|
29
|
+
|
|
30
|
+
test_path = Marshal.load(@read)
|
|
31
|
+
test_cases = Megatest.load_tests(config, [test_path])
|
|
32
|
+
test_id = Marshal.load(@read)
|
|
33
|
+
@read.close
|
|
34
|
+
test_case = test_cases.find { |t| t.id == test_id }
|
|
35
|
+
unless test_case
|
|
36
|
+
exit!(1) # TODO: error
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
result = Runner.new(config).run(test_case)
|
|
40
|
+
Marshal.dump(result, @write)
|
|
41
|
+
@write.close
|
|
42
|
+
else
|
|
43
|
+
exit!(1)
|
|
44
|
+
end
|
|
45
|
+
exit!(0)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "megatest/assertions"
|
|
4
|
+
|
|
5
|
+
module Megatest
|
|
6
|
+
##
|
|
7
|
+
# Subclass Test to define a test suite
|
|
8
|
+
#
|
|
9
|
+
# See Megatest::Assertions and Megatest::DSL
|
|
10
|
+
class Test
|
|
11
|
+
# :stopdoc:
|
|
12
|
+
# Megatest::Test is meant to be subclassed by users, as such it's written in an
|
|
13
|
+
# adversarial way, we expose as little methods, instance variable and constants
|
|
14
|
+
# as possible, and always reference our own constants with their fully qualified name.
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
if respond_to?(:const_source_location)
|
|
18
|
+
def inherited(subclass)
|
|
19
|
+
super
|
|
20
|
+
const_source_location = if subclass.name
|
|
21
|
+
::Object.const_source_location(subclass.name)
|
|
22
|
+
else
|
|
23
|
+
location = caller_locations.find { |l| l.base_label != "inherited" && !l.path.start_with?("<internal:") }
|
|
24
|
+
[location.path, location.lineno]
|
|
25
|
+
end
|
|
26
|
+
::Megatest.registry.register_suite(subclass, const_source_location)
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
def inherited(subclass)
|
|
30
|
+
super
|
|
31
|
+
location = caller_locations.find { |l| l.base_label != "inherited" && !l.path.start_with?("<internal:") }
|
|
32
|
+
const_source_location = [location.path, location.lineno]
|
|
33
|
+
::Megatest.registry.register_suite(subclass, const_source_location)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if Thread.respond_to?(:each_caller_location)
|
|
38
|
+
def include(*modules)
|
|
39
|
+
super
|
|
40
|
+
|
|
41
|
+
location = Thread.each_caller_location do |l|
|
|
42
|
+
break l if l.base_label != "include" && !l.path.start_with?("<internal:")
|
|
43
|
+
end
|
|
44
|
+
include_location = [location.path, location.lineno]
|
|
45
|
+
|
|
46
|
+
modules.each do |mod|
|
|
47
|
+
if mod.is_a?(::Megatest::DSL) || mod.instance_methods.any? { |m| m.start_with?("test_") }
|
|
48
|
+
::Megatest.registry.shared_suite(mod).included_by(self, include_location)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
using Compat::StartWith unless Symbol.method_defined?(:start_with?)
|
|
54
|
+
|
|
55
|
+
def include(*modules)
|
|
56
|
+
super
|
|
57
|
+
|
|
58
|
+
location = caller_locations.find do |l|
|
|
59
|
+
l if l.base_label != "include"
|
|
60
|
+
end
|
|
61
|
+
include_location = [location.path, location.lineno]
|
|
62
|
+
|
|
63
|
+
modules.each do |mod|
|
|
64
|
+
if mod.is_a?(::Megatest::DSL) || mod.instance_methods.any? { |m| m.start_with?("test_") }
|
|
65
|
+
::Megatest.registry.shared_suite(mod).included_by(self, include_location)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def initialize(runtime)
|
|
73
|
+
@__m = runtime
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def before_setup
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# For Minitest compatibility
|
|
80
|
+
def name
|
|
81
|
+
@__m.test_case.name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def setup
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def after_setup
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def before_teardown
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def teardown
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def after_teardown
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# :startdoc:
|
|
100
|
+
extend DSL
|
|
101
|
+
include Assertions
|
|
102
|
+
|
|
103
|
+
# Returns the current Megatest::State::TestCase instance
|
|
104
|
+
# Can be used for self introspection
|
|
105
|
+
def __test__
|
|
106
|
+
@__m.test_case
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns the current Megatest::TestCaseResult instance
|
|
110
|
+
# Can be used for self introspection during teardown
|
|
111
|
+
def __result__
|
|
112
|
+
@__m.result
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "rake/tasklib"
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
warn e.message
|
|
7
|
+
return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Megatest # :nodoc:
|
|
11
|
+
##
|
|
12
|
+
# Megatest::TestTask is a rake helper that generates several rake
|
|
13
|
+
# tasks under the main test task's name-space.
|
|
14
|
+
#
|
|
15
|
+
# task <name> :: the main test task
|
|
16
|
+
# task <name>:cmd :: prints the command to use
|
|
17
|
+
#
|
|
18
|
+
# Examples:
|
|
19
|
+
#
|
|
20
|
+
# Megatest::TestTask.create
|
|
21
|
+
#
|
|
22
|
+
# The most basic and default setup.
|
|
23
|
+
#
|
|
24
|
+
# Megatest::TestTask.create :my_tests
|
|
25
|
+
#
|
|
26
|
+
# The most basic/default setup, but with a custom name
|
|
27
|
+
#
|
|
28
|
+
# Megatest::TestTask.create :unit do |t|
|
|
29
|
+
# t.warning = true
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# Customize the name and only run unit tests.
|
|
33
|
+
|
|
34
|
+
class TestTask < Rake::TaskLib
|
|
35
|
+
WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc:
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Create several test-oriented tasks under +name+. Takes an
|
|
39
|
+
# optional block to customize variables.
|
|
40
|
+
|
|
41
|
+
def self.create(name = :test, &block)
|
|
42
|
+
task = new name
|
|
43
|
+
task.instance_eval(&block) if block
|
|
44
|
+
task.define
|
|
45
|
+
task
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Extra arguments to pass to the tests. Defaults empty.
|
|
50
|
+
|
|
51
|
+
attr_accessor :extra_args
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Extra library directories to include.
|
|
55
|
+
|
|
56
|
+
attr_accessor :libs
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# The name of the task and base name for the other tasks generated.
|
|
60
|
+
|
|
61
|
+
attr_accessor :name
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# Test files or directories to run. Defaults to +test/+
|
|
65
|
+
|
|
66
|
+
attr_accessor :tests
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# Turn on ruby warnings (-w flag). Defaults to true.
|
|
70
|
+
|
|
71
|
+
attr_accessor :warning
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Print out commands as they run. Defaults to Rake's +trace+ (-t
|
|
75
|
+
# flag) option.
|
|
76
|
+
|
|
77
|
+
attr_accessor :verbose
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Show full backtraces on error
|
|
81
|
+
|
|
82
|
+
attr_accessor :full_backtrace
|
|
83
|
+
|
|
84
|
+
# Task prerequisites.
|
|
85
|
+
attr_accessor :deps
|
|
86
|
+
|
|
87
|
+
attr_accessor :command
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# Use TestTask.create instead.
|
|
91
|
+
|
|
92
|
+
def initialize(name = :test) # :nodoc:
|
|
93
|
+
super()
|
|
94
|
+
@libs = []
|
|
95
|
+
@name = name
|
|
96
|
+
@tests = ["test/"]
|
|
97
|
+
@extra_args = []
|
|
98
|
+
@verbose = Rake.application.options.trace || Rake.verbose == true
|
|
99
|
+
@warning = true
|
|
100
|
+
@deps = []
|
|
101
|
+
if @name.is_a?(Hash)
|
|
102
|
+
@deps = @name.values.first
|
|
103
|
+
@name = @name.keys.first
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def define # :nodoc:
|
|
108
|
+
desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
|
|
109
|
+
task name => Array(deps) do
|
|
110
|
+
sh(*make_test_cmd, verbose: verbose)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc "Print out the test command. Good for profiling and other tools."
|
|
114
|
+
task "#{name}:cmd" do
|
|
115
|
+
puts make_test_cmd.join(" ")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Generate the test command-line.
|
|
121
|
+
|
|
122
|
+
def make_test_cmd
|
|
123
|
+
cmd = [command || "megatest"]
|
|
124
|
+
cmd << "-I#{libs.join(File::PATH_SEPARATOR)}" unless libs.empty?
|
|
125
|
+
# cmd << "-w" if warning
|
|
126
|
+
cmd << "--backtrace" if full_backtrace
|
|
127
|
+
cmd.concat(extra_args)
|
|
128
|
+
cmd.concat(tests)
|
|
129
|
+
cmd
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
data/lib/megatest.rb
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "megatest/version"
|
|
4
|
+
|
|
5
|
+
module Megatest
|
|
6
|
+
Error = Class.new(StandardError)
|
|
7
|
+
AlreadyDefinedError = Class.new(Error)
|
|
8
|
+
LoadError = Class.new(Error)
|
|
9
|
+
|
|
10
|
+
# :stopdoc:
|
|
11
|
+
|
|
12
|
+
ROOT = -File.expand_path("../", __FILE__)
|
|
13
|
+
PWD = File.join(Dir.pwd, "/")
|
|
14
|
+
IGNORED_ERRORS = [NoMemoryError, SignalException, SystemExit].freeze
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def fork?
|
|
18
|
+
Process.respond_to?(:fork) && !ENV["NO_FORK"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def now
|
|
22
|
+
Process.clock_gettime(Process::CLOCK_REALTIME)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def relative_path(absolute_path)
|
|
26
|
+
absolute_path&.delete_prefix(PWD)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def append_load_path(config)
|
|
30
|
+
config.load_paths.each do |path|
|
|
31
|
+
abs_path = File.absolute_path(path)
|
|
32
|
+
$LOAD_PATH.unshift(abs_path) unless $LOAD_PATH.include?(abs_path)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def init(config)
|
|
37
|
+
if config.deprecations && ::Warning.respond_to?(:[]=)
|
|
38
|
+
::Warning[:deprecated] = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# We initiale the seed in case there is some Random use
|
|
42
|
+
# at code loading time.
|
|
43
|
+
Random.srand(config.seed)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def load_tests(config, paths = nil)
|
|
47
|
+
registry = with_registry do
|
|
48
|
+
append_load_path(config)
|
|
49
|
+
load_test_helper(config.selectors.main_paths)
|
|
50
|
+
|
|
51
|
+
paths ||= config.selectors.paths(random: config.random)
|
|
52
|
+
paths.each do |path|
|
|
53
|
+
Kernel.require(path)
|
|
54
|
+
rescue LoadError
|
|
55
|
+
raise InvalidArgument, "Failed to load #{relative_path(path)}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
config.selectors.select(registry, random: config.random)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def load_config(config)
|
|
63
|
+
load_files(config.selectors.main_paths, "test_config.rb")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def load_test_helper(paths)
|
|
67
|
+
load_files(paths, "test_helper.rb")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_files(paths, name)
|
|
71
|
+
scaned = {}
|
|
72
|
+
paths.each do |path|
|
|
73
|
+
path = File.dirname(path) unless File.directory?(path)
|
|
74
|
+
|
|
75
|
+
while path.start_with?(PWD)
|
|
76
|
+
break if scaned[path]
|
|
77
|
+
|
|
78
|
+
scaned[path] = true
|
|
79
|
+
|
|
80
|
+
config_path = File.join(path, name)
|
|
81
|
+
if File.exist?(config_path)
|
|
82
|
+
require(config_path)
|
|
83
|
+
break
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
path = File.dirname(path)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if Dir.method(:glob).parameters.include?(%i(key sort)) # Ruby 2.7+
|
|
93
|
+
def glob(path)
|
|
94
|
+
Dir.glob(File.join(path, "**/{test_*,*_test}.rb"))
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
def glob(path)
|
|
98
|
+
paths = Dir.glob(File.join(path, "**/{test_*,*_test}.rb"))
|
|
99
|
+
paths.sort!
|
|
100
|
+
paths
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
require "megatest/compat"
|
|
107
|
+
require "megatest/patience_diff"
|
|
108
|
+
require "megatest/differ"
|
|
109
|
+
require "megatest/pretty_print"
|
|
110
|
+
require "megatest/output"
|
|
111
|
+
require "megatest/backtrace"
|
|
112
|
+
require "megatest/config"
|
|
113
|
+
require "megatest/selector"
|
|
114
|
+
require "megatest/runner"
|
|
115
|
+
require "megatest/runtime"
|
|
116
|
+
require "megatest/state"
|
|
117
|
+
require "megatest/reporters"
|
|
118
|
+
require "megatest/queue"
|
|
119
|
+
require "megatest/queue_reporter"
|
|
120
|
+
require "megatest/executor"
|
|
121
|
+
require "megatest/subprocess"
|
|
122
|
+
require "megatest/dsl"
|
|
123
|
+
require "megatest/test"
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: megatest
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jean Boussier
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-08-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Largely API compatible with test-unit / minitest, but with lots of extra
|
|
14
|
+
modern niceties like a proper CLI, test distribution, etc.
|
|
15
|
+
email:
|
|
16
|
+
- jean.boussier@gmail.com
|
|
17
|
+
executables:
|
|
18
|
+
- megatest
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- README.md
|
|
24
|
+
- TODO.md
|
|
25
|
+
- exe/megatest
|
|
26
|
+
- lib/megatest.rb
|
|
27
|
+
- lib/megatest/assertions.rb
|
|
28
|
+
- lib/megatest/backtrace.rb
|
|
29
|
+
- lib/megatest/cli.rb
|
|
30
|
+
- lib/megatest/compat.rb
|
|
31
|
+
- lib/megatest/config.rb
|
|
32
|
+
- lib/megatest/differ.rb
|
|
33
|
+
- lib/megatest/dsl.rb
|
|
34
|
+
- lib/megatest/executor.rb
|
|
35
|
+
- lib/megatest/multi_process.rb
|
|
36
|
+
- lib/megatest/output.rb
|
|
37
|
+
- lib/megatest/patience_diff.rb
|
|
38
|
+
- lib/megatest/pretty_print.rb
|
|
39
|
+
- lib/megatest/queue.rb
|
|
40
|
+
- lib/megatest/queue_monitor.rb
|
|
41
|
+
- lib/megatest/queue_reporter.rb
|
|
42
|
+
- lib/megatest/redis_queue.rb
|
|
43
|
+
- lib/megatest/reporters.rb
|
|
44
|
+
- lib/megatest/runner.rb
|
|
45
|
+
- lib/megatest/runtime.rb
|
|
46
|
+
- lib/megatest/selector.rb
|
|
47
|
+
- lib/megatest/state.rb
|
|
48
|
+
- lib/megatest/subprocess.rb
|
|
49
|
+
- lib/megatest/subprocess/main.rb
|
|
50
|
+
- lib/megatest/test.rb
|
|
51
|
+
- lib/megatest/test_task.rb
|
|
52
|
+
- lib/megatest/version.rb
|
|
53
|
+
homepage: https://github.com/byroot/megatest
|
|
54
|
+
licenses: []
|
|
55
|
+
metadata:
|
|
56
|
+
allowed_push_host: https://rubygems.org/
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
58
|
+
homepage_uri: https://github.com/byroot/megatest
|
|
59
|
+
source_code_uri: https://github.com/byroot/megatest
|
|
60
|
+
changelog_uri: https://github.com/byroot/megatest/blob/main/CHANGELOG.md
|
|
61
|
+
post_install_message:
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: 2.6.0
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
requirements: []
|
|
76
|
+
rubygems_version: 3.5.11
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Modern test-unit style test framework
|
|
80
|
+
test_files: []
|