loupe 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +71 -0
- data/README.md +101 -0
- data/Rakefile +16 -0
- data/exe/loupe +9 -0
- data/lib/loupe/cli.rb +83 -0
- data/lib/loupe/color.rb +31 -0
- data/lib/loupe/executor.rb +46 -0
- data/lib/loupe/expectation.rb +538 -0
- data/lib/loupe/failure.rb +49 -0
- data/lib/loupe/paged_reporter.rb +195 -0
- data/lib/loupe/plain_reporter.rb +27 -0
- data/lib/loupe/process_executor.rb +56 -0
- data/lib/loupe/queue_server.rb +49 -0
- data/lib/loupe/ractor_executor.rb +52 -0
- data/lib/loupe/rake_task.rb +47 -0
- data/lib/loupe/reporter.rb +106 -0
- data/lib/loupe/test.rb +270 -0
- data/lib/loupe/version.rb +6 -0
- data/lib/loupe.rb +18 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9c82c36cf7a0a2f22151a36571f568c879b490dd8ffdb1804b00305714059da5
|
4
|
+
data.tar.gz: c2f949d723a8df44fab033ba868475b9f8f61405ad7f0ff729c5fb3ee27c8f3a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1053f8e5eac94340b90915e05686a31854cb54d15914e773eb0a8979a0b64b257e7fd706735654395e88f02e148cc4bb3082f5b380e0165b328fbaba8b88aa22
|
7
|
+
data.tar.gz: d6dec0e1c3db33b37cfe4ad5847e6218702e42d93867702d945763b53b45dc925476d34b2103f0f95567a68a43ebcb087f63de22bcf66a54f75038f74cec87d6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Vinicius Stock
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
23
|
+
|
24
|
+
Includes portions from the Minitest project
|
25
|
+
|
26
|
+
https://github.com/seattlerb/minitest
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright © Ryan Davis, seattle.rb
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
32
|
+
this software and associated documentation files (the 'Software'), to deal in
|
33
|
+
the Software without restriction, including without limitation the rights to
|
34
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
35
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
36
|
+
subject to the following conditions:
|
37
|
+
|
38
|
+
The above copyright notice and this permission notice shall be included in all
|
39
|
+
copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
42
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
43
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
44
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
45
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
46
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
47
|
+
|
48
|
+
|
49
|
+
Includes portions from the rspec-expectations project
|
50
|
+
|
51
|
+
https://github.com/rspec/rspec-expectations The MIT License (MIT)
|
52
|
+
|
53
|
+
Copyright © 2012 David Chelimsky, Myron Marston Copyright © 2006 David
|
54
|
+
Chelimsky, The RSpec Development Team Copyright © 2005 Steven Baker
|
55
|
+
|
56
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
57
|
+
this software and associated documentation files (the "Software"), to deal in
|
58
|
+
the Software without restriction, including without limitation the rights to
|
59
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
60
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
61
|
+
subject to the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be included in all
|
64
|
+
copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
67
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
68
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
69
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
70
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
71
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
<div align="center">
|
2
|
+
<img src="https://user-images.githubusercontent.com/18742907/131183084-82c617d9-b83d-49cd-84f3-447ebb90ca50.png" alt="loupe" height="200px">
|
3
|
+
</div>
|
4
|
+
|
5
|
+
# Loupe
|
6
|
+
|
7
|
+
[Loupe](https://en.wikipedia.org/wiki/Loupe) is a test framework with built in parallel execution supporting Ractor or forked process modes.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add the gem to the `Gemfile`.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "loupe"
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```shell
|
20
|
+
bundle install
|
21
|
+
```
|
22
|
+
|
23
|
+
Install bundler binstubs in your application.
|
24
|
+
|
25
|
+
```shell
|
26
|
+
bundle binstub loupe
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
Currently, Loupe only supports writing tests using the test methods syntax. Tests must inherit from `Loupe::Test`, but do not need to explicitly require `test_helper`, like the example below.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# frozen_string_literal: true
|
35
|
+
|
36
|
+
class MyTest < Loupe::Test
|
37
|
+
def before
|
38
|
+
@author = Author.create(name: "John")
|
39
|
+
@post = Post.create(author: @author)
|
40
|
+
end
|
41
|
+
|
42
|
+
def after
|
43
|
+
@author.destroy
|
44
|
+
@post.destroy
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_post_is_linked_to_author
|
48
|
+
expect(@post.author.name).to_be_equal_to("John")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
To run the test suite, invoke the executable using the binstub generated by bundler.
|
54
|
+
|
55
|
+
```shell
|
56
|
+
bin/loupe test/post_test.rb test/author_test.rb
|
57
|
+
```
|
58
|
+
|
59
|
+
Tests can run in parallel using Ractor or process mode. When using Ractors, the application's code must be Ractor compatible.
|
60
|
+
|
61
|
+
```shell
|
62
|
+
bin/loupe --ractor # [default] run tests using Ractor workers
|
63
|
+
bin/loupe --process # run tests using forked processes
|
64
|
+
bin/loupe --interactive # [default] use an interactive reporter to display test results
|
65
|
+
bin/loupe --plain # use a plain reporter to display test results
|
66
|
+
bin/loupe --color, --no-color # enable/disable output colors
|
67
|
+
bin/loupe --editor=EDITOR # which editor to use for opening files when using interactive mode. The default is the environment variable $EDITOR
|
68
|
+
```
|
69
|
+
|
70
|
+
To hook Loupe into Rake, use the provided rake task as in the example below.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
# Rakefile
|
74
|
+
|
75
|
+
require "loupe/rake_task"
|
76
|
+
|
77
|
+
# Instantiate the task and append any desired CLI options
|
78
|
+
Loupe::RakeTask.new do |options|
|
79
|
+
options << "--plain"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Optionally, set the default task to be test
|
83
|
+
task default: :test
|
84
|
+
```
|
85
|
+
|
86
|
+
Then run
|
87
|
+
|
88
|
+
```shell
|
89
|
+
bundle exec rake test
|
90
|
+
```
|
91
|
+
|
92
|
+
## Credits
|
93
|
+
|
94
|
+
This project draws a lot of inspiration from other Ruby test frameworks, namely
|
95
|
+
|
96
|
+
- [Minitest](https://github.com/seattlerb/minitest)
|
97
|
+
- [rspec](https://github.com/rspec/rspec)
|
98
|
+
|
99
|
+
## Contributing
|
100
|
+
|
101
|
+
Please refer to the guidelines in [contributing](https://github.com/vinistock/loupe/blob/master/CONTRIBUTING.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rubocop/rake_task"
|
13
|
+
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
16
|
+
task default: %i[test rubocop]
|
data/exe/loupe
ADDED
data/lib/loupe/cli.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module Loupe
|
6
|
+
# Cli
|
7
|
+
#
|
8
|
+
# The Cli class defines all available
|
9
|
+
# commands and their options
|
10
|
+
class Cli
|
11
|
+
# @return [String]
|
12
|
+
USAGE = <<~TEXT
|
13
|
+
Usage: [test_list] [options]
|
14
|
+
TEXT
|
15
|
+
|
16
|
+
# @return [void]
|
17
|
+
def initialize(args = ARGV)
|
18
|
+
@options = {
|
19
|
+
color: true,
|
20
|
+
interactive: true,
|
21
|
+
mode: :ractor
|
22
|
+
}
|
23
|
+
|
24
|
+
parse_options(@options, args)
|
25
|
+
@options.freeze
|
26
|
+
|
27
|
+
@files = args
|
28
|
+
start
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# @param options [Hash<Symbol, BasicObject>]
|
34
|
+
# @param args [Array<String>]
|
35
|
+
# @return [void]
|
36
|
+
def parse_options(options, args) # rubocop:disable Metrics/AbcSize
|
37
|
+
OptionParser.new do |opts|
|
38
|
+
opts.banner = USAGE
|
39
|
+
|
40
|
+
opts.on("--version", "Print Loupe's version") do
|
41
|
+
warn Loupe::VERSION
|
42
|
+
exit(0)
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("--color", "--[no-]color", "Enable or disable color in the output") { |value| options[:color] = value }
|
46
|
+
opts.on("--interactive", "Use interactive output") { options[:interactive] = true }
|
47
|
+
opts.on("--plain", "Use plain non-interactive output") { options[:interactive] = false }
|
48
|
+
opts.on("--process", "Execute in process mode") { @options[:mode] = :process }
|
49
|
+
opts.on("--ractor", "Execute in ractor mode") do
|
50
|
+
raise ArgumentError, "Ractor mode can only be used in Ruby 3.0 and forward" if RUBY_VERSION < "3.0.0"
|
51
|
+
|
52
|
+
@options[:mode] = :ractor
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("--editor=EDITOR", "The editor to open test files with in interactive mode") do |value|
|
56
|
+
options[:editor] = value
|
57
|
+
end
|
58
|
+
end.parse!(args)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [void]
|
62
|
+
def start
|
63
|
+
require_tests
|
64
|
+
executor = @options[:mode] == :ractor ? RactorExecutor.new(@options) : ProcessExecutor.new(@options)
|
65
|
+
exit(executor.run)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [void]
|
69
|
+
def require_tests
|
70
|
+
require "#{Dir.pwd}/test/test_helper"
|
71
|
+
|
72
|
+
if @files.empty?
|
73
|
+
Dir["#{Dir.pwd}/test/**/*_test.rb"].each { |f| require f }
|
74
|
+
else
|
75
|
+
@files.each do |f|
|
76
|
+
file, line_number = f.split(":")
|
77
|
+
require File.expand_path(file)
|
78
|
+
Test.add_line_number(line_number) if line_number
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/loupe/color.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Loupe
|
4
|
+
# Color
|
5
|
+
#
|
6
|
+
# This class is responsible for coloring
|
7
|
+
# strings.
|
8
|
+
class Color
|
9
|
+
# @return [Hash<Symbol, String>]
|
10
|
+
COLORS = {
|
11
|
+
red: "31",
|
12
|
+
green: "32",
|
13
|
+
yellow: "33"
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
# @param enabled [Boolean]
|
17
|
+
def initialize(enabled)
|
18
|
+
@enabled = enabled
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param string [String, Symbol]
|
22
|
+
# @param color [Symbol]
|
23
|
+
# @return [String]
|
24
|
+
def p(string, color)
|
25
|
+
return string unless @enabled
|
26
|
+
|
27
|
+
color_code = COLORS[color]
|
28
|
+
"\033[1;#{color_code}m#{string}\033[0m"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "etc"
|
4
|
+
|
5
|
+
module Loupe
|
6
|
+
# Executor
|
7
|
+
#
|
8
|
+
# This abstract parent class is responsible for providing the basics
|
9
|
+
# for executors. Concrete classes are the process and ractor executors.
|
10
|
+
class Executor
|
11
|
+
# @param options [Hash<Symbol, BasicObject>]
|
12
|
+
# @return [Loupe::Executor]
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
@queue = populate_queue
|
16
|
+
@reporter = options[:interactive] ? PagedReporter.new(options) : PlainReporter.new(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer]
|
20
|
+
def run
|
21
|
+
raise NotImplementedError, "Concrete implementations of executors should implement the run method"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Populate the test queue with entries including
|
27
|
+
# the class and the test method to be executed.
|
28
|
+
# E.g.:
|
29
|
+
# [[MyTest, :test_something], [AnotherTest, :test_another_thing]]
|
30
|
+
#
|
31
|
+
# @return [Array<Array<Class, Symbol>>]
|
32
|
+
def populate_queue
|
33
|
+
Test.classes.flat_map do |klass, line_numbers|
|
34
|
+
list = klass.test_list
|
35
|
+
|
36
|
+
unless line_numbers.empty?
|
37
|
+
list.select! do |method_name|
|
38
|
+
line_numbers.include?(klass.instance_method(method_name).source_location.last.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
list.map! { |method| [klass, method] }.shuffle!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|