mutest-rspec 0.0.2

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 (4) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/lib/mutest/integration/rspec.rb +149 -0
  4. metadata +103 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d815353a912cc998ffe6a9cb7bb3bf1f5a2e4634
4
+ data.tar.gz: 6130aab82b5a76037ef4a5b33129dac098cfecd1
5
+ SHA512:
6
+ metadata.gz: 63cd6c629d0ccdd37c48f44f626b8a2f7d3860c5a755f4152e4cdf2568d65b557b4288fe33de165f2358600f53b079164135ed228c3449c61b553862d2aaf59f
7
+ data.tar.gz: 3c23b9ed824243424b466c7758ffeed4ae52ef42c99e904974b8b12e8ab2b0a0e1d0ae0ea3775f7cca9d6089dc1ef4d314e1ce7b1761f13d482cc1ca605b74a8
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012-2017 Markus Schirp
2
+ Copyright (c) 2017 John Backus
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ require 'rspec/core'
2
+
3
+ module Mutest
4
+ class Integration
5
+ # Rspec integration
6
+ #
7
+ # This looks so complicated, because rspec:
8
+ #
9
+ # * Keeps its state global in RSpec.world and lots of other places
10
+ # * There is no API to "just run a subset of examples", the examples
11
+ # need to be selected in-place via mutating the `RSpec.filtered_examples`
12
+ # data structure
13
+ # * Does not maintain a unique identification for an example,
14
+ # aside the instances of `RSpec::Core::Example` objects itself.
15
+ # For that reason identifying examples by:
16
+ # * full description
17
+ # * location
18
+ # Is NOT enough. It would not be unique. So we add an "example index"
19
+ # for unique reference.
20
+ #
21
+ # :reek:TooManyConstants
22
+ class Rspec < self
23
+ ALL_EXPRESSION = Expression::Namespace::Recursive.new(scope_name: nil)
24
+ EXPRESSION_CANDIDATE = /\A([^ ]+)(?: )?/
25
+ LOCATION_DELIMITER = ':'.freeze
26
+ EXIT_SUCCESS = 0
27
+ CLI_OPTIONS = IceNine.deep_freeze(%w[spec --fail-fast])
28
+ TEST_ID_FORMAT = 'rspec:%<index>d:%<location>s/%<description>s'.freeze
29
+
30
+ private_constant(*constants(false))
31
+
32
+ # Initialize rspec integration
33
+ #
34
+ # @return [undefined]
35
+ def initialize(*)
36
+ super
37
+ @output = StringIO.new
38
+ @runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new(CLI_OPTIONS))
39
+ @world = RSpec.world
40
+ end
41
+
42
+ # Setup rspec integration
43
+ #
44
+ # @return [self]
45
+ def setup
46
+ @runner.setup($stderr, @output)
47
+ self
48
+ end
49
+ memoize :setup
50
+
51
+ # Run a collection of tests
52
+ #
53
+ # @param [Enumerable<Mutest::Test>] tests
54
+ #
55
+ # @return [Result::Test]
56
+ #
57
+ # rubocop:disable MethodLength
58
+ def call(tests)
59
+ examples = tests.map(&all_tests_index.method(:fetch))
60
+ filter_examples(&examples.method(:include?))
61
+ start = Time.now
62
+ passed = @runner.run_specs(@world.ordered_example_groups).equal?(EXIT_SUCCESS)
63
+ @output.rewind
64
+ Result::Test.new(
65
+ output: @output.read,
66
+ passed: passed,
67
+ runtime: Time.now - start,
68
+ tests: tests
69
+ )
70
+ end
71
+
72
+ # Available tests
73
+ #
74
+ # @return [Enumerable<Test>]
75
+ def all_tests
76
+ all_tests_index.keys
77
+ end
78
+ memoize :all_tests
79
+
80
+ private
81
+
82
+ # Index of available tests
83
+ #
84
+ # @return [Hash<Test, RSpec::Core::Example]
85
+ def all_tests_index
86
+ all_examples.each_with_index.each_with_object({}) do |(example, example_index), index|
87
+ index[parse_example(example, example_index)] = example
88
+ end
89
+ end
90
+ memoize :all_tests_index
91
+
92
+ # Parse example into test
93
+ #
94
+ # @param [RSpec::Core::Example] example
95
+ # @param [Fixnum] index
96
+ #
97
+ # @return [Test]
98
+ def parse_example(example, index)
99
+ metadata = example.metadata
100
+
101
+ id = format(
102
+ TEST_ID_FORMAT,
103
+ index: index,
104
+ location: metadata.fetch(:location),
105
+ description: metadata.fetch(:full_description)
106
+ )
107
+
108
+ Test.new(
109
+ expression: parse_expression(metadata),
110
+ id: id
111
+ )
112
+ end
113
+
114
+ # Parse metadata into expression
115
+ #
116
+ # @param [RSpec::Core::Example::MetaData] metadata
117
+ #
118
+ # @return [Expression]
119
+ def parse_expression(metadata)
120
+ if metadata.key?(:mutest_expression)
121
+ expression_parser.(metadata.fetch(:mutest_expression))
122
+ else
123
+ match = EXPRESSION_CANDIDATE.match(metadata.fetch(:full_description))
124
+ expression_parser.try_parse(match.captures.first) || ALL_EXPRESSION
125
+ end
126
+ end
127
+
128
+ # Available rspec examples
129
+ #
130
+ # @return [Array<String, RSpec::Core::Example]
131
+ def all_examples
132
+ @world.example_groups.flat_map(&:descendants).flat_map(&:examples).select do |example|
133
+ example.metadata.fetch(:mutest, true)
134
+ end
135
+ end
136
+
137
+ # Filter examples
138
+ #
139
+ # @param [#call] predicate
140
+ #
141
+ # @return [undefined]
142
+ def filter_examples(&predicate)
143
+ @world.filtered_examples.each_value do |examples|
144
+ examples.keep_if(&predicate)
145
+ end
146
+ end
147
+ end # Rspec
148
+ end # Integration
149
+ end # Mutest
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mutest-rspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Gollahon
8
+ - John Backus
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-01-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mutest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.0.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.0.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec-core
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 3.4.0
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: 3.6.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.4.0
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.6.0
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 1.3.5
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '1.3'
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 1.3.5
68
+ description: Rspec integration for mutest
69
+ email:
70
+ - johncbackus@gmail.com
71
+ - daniel.gollahon@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files:
75
+ - LICENSE
76
+ files:
77
+ - LICENSE
78
+ - lib/mutest/integration/rspec.rb
79
+ homepage: https://github.com/backus/mutest
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.5.1
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Rspec integration for mutest
103
+ test_files: []