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.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Megatest
4
+ VERSION = "0.1.0"
5
+ 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: []