simmer 3.0.0 → 4.0.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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +31 -0
- data/lib/simmer.rb +17 -105
- data/lib/simmer/bootstrap.rb +133 -0
- data/lib/simmer/configuration.rb +16 -2
- data/lib/simmer/configuration/callback_dsl.rb +79 -0
- data/lib/simmer/re_runner.rb +46 -0
- data/lib/simmer/runner.rb +20 -23
- data/lib/simmer/runner/result.rb +6 -1
- data/lib/simmer/runner/timeout_error.rb +23 -0
- data/lib/simmer/suite.rb +9 -7
- data/lib/simmer/version.rb +1 -1
- data/simmer.gemspec +1 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcaddd54b4d50b90701d91f9a62fdfd163a249565d88c850386caf22135a0af3
|
4
|
+
data.tar.gz: 143b6629e67a14e365d6e16a0d795b607f1f4f29aadc270be6458e715a7b68b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6645db0960863e2490964b56228969bd3b4a061cc8ad61b069a239dee8920d2861bdbeb125b758861426079529b3c7faed1bb645c67bb3ebbbbeb7a5a9fc11bf
|
7
|
+
data.tar.gz: 257ab72c85cca10bc54a6e23d451b3da17bd056c8d0a2b96a5b493c0f58a2d38f98bd7e6a4fcd14bafe190a8cafecfabf70a713be37777b86befef88e34e7220
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Simmer Change Log
|
2
2
|
|
3
|
+
## 4.0.0 (TBD, 2020)
|
4
|
+
|
5
|
+
Additions:
|
6
|
+
|
7
|
+
* Support for custom test lifecycle hooks to be run before and after a every test or the entire suite.
|
8
|
+
* Simmer can now be configured to re-run tests which have failed due to a timeout a custom number of times.
|
9
|
+
|
10
|
+
Breaking Changes:
|
11
|
+
|
12
|
+
* The `callback_configuration` and `make_runner` methods have been removed from the `Simmer` module.
|
13
|
+
* `Simmer::Runner::Result#errors` now contains error/exception instances instead of strings.
|
14
|
+
* `Simmer::Suite.new` and `Simmer::Runner.run` now takes a `Simmer::Configuration` instance instead of a hash of configuration values.
|
15
|
+
* The config keyword parameter of `Simmer::Runner.run` is now mandatory.
|
16
|
+
|
3
17
|
## 3.0.0 (June 8th, 2020)
|
4
18
|
|
5
19
|
Breaking Changes:
|
data/README.md
CHANGED
@@ -53,6 +53,9 @@ The configuration file contains information about external systems, such as:
|
|
53
53
|
Copy this configuration template into your project's root to: `config/simmer.yaml`:
|
54
54
|
|
55
55
|
````yaml
|
56
|
+
# Automatically retry a test when it has failed this many times due to a timeout error:
|
57
|
+
timeout_failure_retry_count: 0
|
58
|
+
|
56
59
|
mysql_database:
|
57
60
|
database:
|
58
61
|
username:
|
@@ -270,6 +273,34 @@ You can also omit the path altogether to execute all specs:
|
|
270
273
|
bundle exec simmer
|
271
274
|
````
|
272
275
|
|
276
|
+
## Custom Configuration
|
277
|
+
|
278
|
+
It is possible to define custom test lifecycle hooks. These are very similar to [Rspec](https://relishapp.com/rspec/rspec-core/v/3-9/docs/hooks/before-and-after-hooks). Here is an example of how to ensure that code called before and after the entire suite:
|
279
|
+
|
280
|
+
````ruby
|
281
|
+
Simmer.configure do |config|
|
282
|
+
config.before(:suite) { puts 'about to run the entire suite' }
|
283
|
+
config.after(:suite) do |result|
|
284
|
+
result_msg = result.passed? ? 'passed' : 'failed'
|
285
|
+
puts "The suite #{result_msg}."
|
286
|
+
end
|
287
|
+
end
|
288
|
+
````
|
289
|
+
|
290
|
+
Not that after callbacks taken an optional parameter which is the result object.
|
291
|
+
|
292
|
+
It is also possible to specify custom code which runs before and after each individual specification.
|
293
|
+
|
294
|
+
````ruby
|
295
|
+
Simmer.configure do |config|
|
296
|
+
config.before(:each) { puts 'I will run before each test' }
|
297
|
+
config.after(:each) do |result|
|
298
|
+
result_msg = result.passed? ? 'passed' : 'failed'
|
299
|
+
puts "The specification #{result_msg}."
|
300
|
+
end
|
301
|
+
end
|
302
|
+
````
|
303
|
+
|
273
304
|
## Contributing
|
274
305
|
|
275
306
|
### Development Environment Configuration
|
data/lib/simmer.rb
CHANGED
@@ -22,7 +22,7 @@ require 'set'
|
|
22
22
|
require 'stringento'
|
23
23
|
require 'yaml'
|
24
24
|
|
25
|
-
# Monkey-patching core
|
25
|
+
# Monkey-patching core libraries
|
26
26
|
require_relative 'simmer/core_ext/hash'
|
27
27
|
Hash.include Simmer::CoreExt::Hash
|
28
28
|
|
@@ -30,10 +30,12 @@ Hash.include Simmer::CoreExt::Hash
|
|
30
30
|
require_relative 'simmer/util'
|
31
31
|
|
32
32
|
# Core code
|
33
|
+
require_relative 'simmer/bootstrap'
|
33
34
|
require_relative 'simmer/configuration'
|
34
35
|
require_relative 'simmer/database'
|
35
36
|
require_relative 'simmer/externals'
|
36
37
|
require_relative 'simmer/runner'
|
38
|
+
require_relative 'simmer/re_runner'
|
37
39
|
require_relative 'simmer/specification'
|
38
40
|
require_relative 'simmer/suite'
|
39
41
|
|
@@ -49,118 +51,28 @@ module Simmer
|
|
49
51
|
out: $stdout,
|
50
52
|
simmer_dir: DEFAULT_SIMMER_DIR
|
51
53
|
)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
Bootstrap.new(
|
55
|
+
spec_path: path,
|
56
|
+
config_path: config_path,
|
57
|
+
simmer_dir: simmer_dir,
|
58
|
+
callback_configuration: callback_configuration,
|
59
|
+
console_out: out
|
60
|
+
).run_suite
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
62
|
-
config_path:
|
63
|
-
simmer_dir: DEFAULT_SIMMER_DIR
|
64
|
-
)
|
65
|
-
raw_config = yaml_reader.smash(config_path)
|
66
|
-
|
67
|
-
Configuration.new(raw_config, simmer_dir)
|
63
|
+
def configuration(config_path: DEFAULT_CONFIG_PATH, simmer_dir: DEFAULT_SIMMER_DIR)
|
64
|
+
Bootstrap.new(config_path: config_path, simmer_dir: simmer_dir).configuration
|
68
65
|
end
|
66
|
+
alias make_configuration configuration
|
69
67
|
|
70
|
-
def
|
71
|
-
|
72
|
-
file_system = make_file_system(configuration)
|
73
|
-
fixture_set = make_fixture_set(configuration)
|
74
|
-
spoon_client = make_spoon_client(configuration)
|
75
|
-
|
76
|
-
Runner.new(
|
77
|
-
database: database,
|
78
|
-
file_system: file_system,
|
79
|
-
fixture_set: fixture_set,
|
80
|
-
out: out_router,
|
81
|
-
spoon_client: spoon_client
|
82
|
-
)
|
68
|
+
def configure
|
69
|
+
yield callback_configuration
|
83
70
|
end
|
84
71
|
|
85
72
|
private
|
86
73
|
|
87
|
-
def
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
def make_specifications(path, tests_dir)
|
92
|
-
path = path.to_s.empty? ? tests_dir : path
|
93
|
-
|
94
|
-
Util::YamlReader.new.read(path).map do |file|
|
95
|
-
config = (file.data || {}).merge(path: file.path)
|
96
|
-
|
97
|
-
Specification.make(config)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def make_fixture_set(configuration)
|
102
|
-
config = Util::YamlReader.new.smash(configuration.fixtures_dir)
|
103
|
-
|
104
|
-
Database::FixtureSet.new(config)
|
105
|
-
end
|
106
|
-
|
107
|
-
def make_mysql_database(configuration)
|
108
|
-
config = configuration.mysql_database_config.symbolize_keys
|
109
|
-
client = Mysql2::Client.new(config)
|
110
|
-
exclude_tables = config[:exclude_tables]
|
111
|
-
|
112
|
-
Externals::MysqlDatabase.new(client, exclude_tables)
|
113
|
-
end
|
114
|
-
|
115
|
-
def make_file_system(configuration)
|
116
|
-
if configuration.aws_file_system?
|
117
|
-
make_aws_file_system(configuration)
|
118
|
-
elsif configuration.local_file_system?
|
119
|
-
make_local_file_system(configuration)
|
120
|
-
else
|
121
|
-
raise ArgumentError, 'cannot determine file system'
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def make_aws_file_system(configuration)
|
126
|
-
config = configuration.aws_file_system_config.symbolize_keys
|
127
|
-
client_args = config.slice(:access_key_id, :secret_access_key, :region)
|
128
|
-
client = Aws::S3::Client.new(client_args)
|
129
|
-
|
130
|
-
Externals::AwsFileSystem.new(
|
131
|
-
client,
|
132
|
-
config[:bucket],
|
133
|
-
config[:encryption],
|
134
|
-
configuration.files_dir
|
135
|
-
)
|
136
|
-
end
|
137
|
-
|
138
|
-
def make_local_file_system(configuration)
|
139
|
-
config = configuration.local_file_system_config.symbolize_keys
|
140
|
-
|
141
|
-
Externals::LocalFileSystem.new(config[:dir], configuration.files_dir)
|
142
|
-
end
|
143
|
-
|
144
|
-
def make_spoon_client(configuration)
|
145
|
-
config = (configuration.spoon_client_config || {}).symbolize_keys
|
146
|
-
spoon_args = config.slice(:args, :dir, :kitchen, :pan, :timeout_in_seconds)
|
147
|
-
spoon = Pdi::Spoon.new(spoon_args)
|
148
|
-
|
149
|
-
Externals::SpoonClient.new(configuration.files_dir, spoon)
|
150
|
-
end
|
151
|
-
|
152
|
-
def make_suite(configuration, out, runner)
|
153
|
-
Suite.new(
|
154
|
-
config: configuration.config,
|
155
|
-
out: out,
|
156
|
-
results_dir: configuration.results_dir,
|
157
|
-
runner: runner
|
158
|
-
)
|
159
|
-
end
|
160
|
-
|
161
|
-
def make_output_router(configuration, console_out)
|
162
|
-
pdi_out = Suite::PdiOutputWriter.new(configuration.results_dir)
|
163
|
-
Simmer::Suite::OutputRouter.new(console_out, pdi_out)
|
74
|
+
def callback_configuration
|
75
|
+
@callback_configuration ||= Configuration::CallbackDsl.new
|
164
76
|
end
|
165
77
|
end
|
166
78
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2020-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Simmer
|
11
|
+
# :nodoc:
|
12
|
+
# Responsible for creating core objects needed to run tests.
|
13
|
+
class Bootstrap
|
14
|
+
attr_reader :callback_configuration, :configuration, :console_out, :spec_path
|
15
|
+
|
16
|
+
def initialize(
|
17
|
+
config_path:,
|
18
|
+
simmer_dir:,
|
19
|
+
spec_path: nil,
|
20
|
+
console_out: $stdout,
|
21
|
+
callback_configuration: Configuration::CallbackDsl.new
|
22
|
+
)
|
23
|
+
@spec_path = spec_path.to_s
|
24
|
+
raw_config = yaml_reader.smash(config_path)
|
25
|
+
@configuration = Configuration.new(raw_config, simmer_dir, callbacks: callback_configuration)
|
26
|
+
@console_out = console_out
|
27
|
+
@callback_configuration = callback_configuration
|
28
|
+
|
29
|
+
freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_suite
|
33
|
+
suite.run(specs)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def specs
|
39
|
+
path = spec_path.empty? ? configuration.tests_dir : spec_path
|
40
|
+
|
41
|
+
Util::YamlReader.new.read(path).map do |file|
|
42
|
+
config = (file.data || {}).merge(path: file.path)
|
43
|
+
|
44
|
+
Specification.make(config)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def suite
|
49
|
+
Suite.new(
|
50
|
+
config: configuration,
|
51
|
+
out: console_out,
|
52
|
+
results_dir: configuration.results_dir,
|
53
|
+
runner: runner
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def runner
|
58
|
+
runner = Runner.new(
|
59
|
+
database: mysql_database,
|
60
|
+
file_system: file_system,
|
61
|
+
fixture_set: fixture_set,
|
62
|
+
out: output_router,
|
63
|
+
spoon_client: spoon_client
|
64
|
+
)
|
65
|
+
|
66
|
+
ReRunner.new(
|
67
|
+
runner,
|
68
|
+
output_router,
|
69
|
+
timeout_failure_retry_count: configuration.timeout_failure_retry_count
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def yaml_reader
|
74
|
+
Util::YamlReader.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def fixture_set
|
78
|
+
config = Util::YamlReader.new.smash(configuration.fixtures_dir)
|
79
|
+
|
80
|
+
Database::FixtureSet.new(config)
|
81
|
+
end
|
82
|
+
|
83
|
+
def mysql_database
|
84
|
+
config = configuration.mysql_database_config.symbolize_keys
|
85
|
+
client = Mysql2::Client.new(config)
|
86
|
+
exclude_tables = config[:exclude_tables]
|
87
|
+
|
88
|
+
Externals::MysqlDatabase.new(client, exclude_tables)
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_system
|
92
|
+
if configuration.aws_file_system?
|
93
|
+
aws_file_system
|
94
|
+
elsif configuration.local_file_system?
|
95
|
+
local_file_system
|
96
|
+
else
|
97
|
+
raise ArgumentError, 'cannot determine file system'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def aws_file_system
|
102
|
+
config = configuration.aws_file_system_config.symbolize_keys
|
103
|
+
client_args = config.slice(:access_key_id, :secret_access_key, :region)
|
104
|
+
client = Aws::S3::Client.new(client_args)
|
105
|
+
|
106
|
+
Externals::AwsFileSystem.new(
|
107
|
+
client,
|
108
|
+
config[:bucket],
|
109
|
+
config[:encryption],
|
110
|
+
configuration.files_dir
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def local_file_system
|
115
|
+
config = configuration.local_file_system_config.symbolize_keys
|
116
|
+
|
117
|
+
Externals::LocalFileSystem.new(config[:dir], configuration.files_dir)
|
118
|
+
end
|
119
|
+
|
120
|
+
def spoon_client
|
121
|
+
config = (configuration.spoon_client_config || {}).symbolize_keys
|
122
|
+
spoon_args = config.slice(:args, :dir, :kitchen, :pan, :timeout_in_seconds)
|
123
|
+
spoon = Pdi::Spoon.new(spoon_args)
|
124
|
+
|
125
|
+
Externals::SpoonClient.new(configuration.files_dir, spoon)
|
126
|
+
end
|
127
|
+
|
128
|
+
def output_router
|
129
|
+
pdi_out = Suite::PdiOutputWriter.new(configuration.results_dir)
|
130
|
+
Simmer::Suite::OutputRouter.new(console_out, pdi_out)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/simmer/configuration.rb
CHANGED
@@ -7,14 +7,19 @@
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
8
8
|
#
|
9
9
|
|
10
|
+
require_relative 'configuration/callback_dsl'
|
11
|
+
|
10
12
|
module Simmer
|
11
13
|
# Reads in the Simmer configuration file and options and provides it to the rest of the
|
12
14
|
# Simmer implementation.
|
13
15
|
class Configuration
|
16
|
+
extend Forwardable
|
17
|
+
|
14
18
|
# Configuration Keys
|
15
19
|
AWS_FILE_SYSTEM_KEY = :aws_file_system
|
16
20
|
LOCAL_FILE_SYSTEM_KEY = :local_file_system
|
17
21
|
MYSQL_DATABASE_KEY = :mysql_database
|
22
|
+
TIMEOUT_RETRY_KEY = :timeout_failure_retry_count
|
18
23
|
SPOON_CLIENT_KEY = :spoon_client
|
19
24
|
|
20
25
|
# Paths
|
@@ -32,10 +37,14 @@ module Simmer
|
|
32
37
|
|
33
38
|
attr_reader :config
|
34
39
|
|
35
|
-
|
40
|
+
# :nodoc:
|
41
|
+
def_delegators :callbacks, :run_single_test_with_callbacks, :run_suite_with_callbacks
|
42
|
+
|
43
|
+
def initialize(config, simmer_dir, resolver: Objectable.resolver, callbacks: nil)
|
36
44
|
@config = config || {}
|
37
45
|
@resolver = resolver
|
38
46
|
@simmer_dir = simmer_dir
|
47
|
+
@callbacks = callbacks
|
39
48
|
|
40
49
|
freeze
|
41
50
|
end
|
@@ -60,6 +69,10 @@ module Simmer
|
|
60
69
|
config.key?(LOCAL_FILE_SYSTEM_KEY.to_s)
|
61
70
|
end
|
62
71
|
|
72
|
+
def timeout_failure_retry_count
|
73
|
+
get(TIMEOUT_RETRY_KEY) || 0
|
74
|
+
end
|
75
|
+
|
63
76
|
def spoon_client_config
|
64
77
|
get(SPOON_CLIENT_KEY) || {}
|
65
78
|
end
|
@@ -82,7 +95,8 @@ module Simmer
|
|
82
95
|
|
83
96
|
private
|
84
97
|
|
85
|
-
attr_reader :
|
98
|
+
attr_reader :callbacks,
|
99
|
+
:resolver,
|
86
100
|
:simmer_dir,
|
87
101
|
:yaml_reader
|
88
102
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2020-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Simmer
|
11
|
+
class Configuration
|
12
|
+
# Defines lifecycle hooks which can be run before and after the entire
|
13
|
+
# suite or just a single test. Very similar to Rspec
|
14
|
+
# (https://relishapp.com/rspec/rspec-core/v/3-9/docs/hooks/before-and-after-hooks).
|
15
|
+
class CallbackDsl
|
16
|
+
def initialize
|
17
|
+
@before_suite = []
|
18
|
+
@after_suite = []
|
19
|
+
@before_each = []
|
20
|
+
@after_each = []
|
21
|
+
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
# Used to create a before callback. This accepts and optional level
|
26
|
+
# parameter which can either be :suite or :each. ":each" is implied if no
|
27
|
+
# level is provided.
|
28
|
+
def before(level = LEVEL_EACH, &block)
|
29
|
+
verify_level!(level)
|
30
|
+
|
31
|
+
level == LEVEL_SUITE ? before_suite.push(block) : before_each.push(block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Used to create an after callback. This accepts and optional level
|
35
|
+
# parameter which can either be :suite or :each. ":each" is implied if no
|
36
|
+
# level is provided.
|
37
|
+
def after(level = LEVEL_EACH, &block)
|
38
|
+
verify_level!(level)
|
39
|
+
|
40
|
+
level == LEVEL_SUITE ? after_suite.push(block) : after_each.push(block)
|
41
|
+
end
|
42
|
+
|
43
|
+
# :nodoc:
|
44
|
+
def run_single_test_with_callbacks
|
45
|
+
before_each.each(&:call)
|
46
|
+
|
47
|
+
result = yield
|
48
|
+
|
49
|
+
after_each.each { |block| block.call(result) }
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
# :nodoc:
|
55
|
+
def run_suite_with_callbacks
|
56
|
+
before_suite.each(&:call)
|
57
|
+
|
58
|
+
result = yield
|
59
|
+
|
60
|
+
after_suite.each { |block| block.call(result) }
|
61
|
+
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def verify_level!(level)
|
68
|
+
raise ArgumentError, "unknown test level: #{level}" unless CALLBACK_LEVELS.include?(level)
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :after_each, :before_each, :after_suite, :before_suite
|
72
|
+
|
73
|
+
LEVEL_EACH = :each
|
74
|
+
LEVEL_SUITE = :suite
|
75
|
+
CALLBACK_LEVELS = Set.new([LEVEL_EACH, LEVEL_SUITE])
|
76
|
+
private_constant :LEVEL_EACH, :LEVEL_SUITE, :CALLBACK_LEVELS
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2020-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'judge'
|
11
|
+
require_relative 'runner/result'
|
12
|
+
|
13
|
+
module Simmer
|
14
|
+
# :nodoc:
|
15
|
+
# Wraps a <tt>Simmer::Runner</tt> and knows how to re-run tests based
|
16
|
+
# on certain failure cases.
|
17
|
+
class ReRunner < SimpleDelegator
|
18
|
+
attr_reader :timeout_failure_retry_count
|
19
|
+
|
20
|
+
def initialize(runner, out, timeout_failure_retry_count: 0)
|
21
|
+
@timeout_failure_retry_count = timeout_failure_retry_count.to_i
|
22
|
+
@out = out
|
23
|
+
|
24
|
+
super(runner)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(*args)
|
28
|
+
rerun_on_timeout(args, timeout_failure_retry_count)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :out
|
34
|
+
|
35
|
+
def rerun_on_timeout(run_args, times)
|
36
|
+
result = __getobj__.run(*run_args)
|
37
|
+
|
38
|
+
if result.timed_out? && times.positive?
|
39
|
+
out.console_puts('Retrying due to a timeout...')
|
40
|
+
rerun_on_timeout(run_args, times - 1)
|
41
|
+
else
|
42
|
+
result
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/simmer/runner.rb
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
require_relative 'judge'
|
11
11
|
require_relative 'runner/result'
|
12
|
+
require_relative 'runner/timeout_error'
|
12
13
|
|
13
14
|
module Simmer
|
14
15
|
# Runs a single specification.
|
@@ -26,28 +27,24 @@ module Simmer
|
|
26
27
|
freeze
|
27
28
|
end
|
28
29
|
|
29
|
-
def run(specification, config
|
30
|
+
def run(specification, config:, id: SecureRandom.uuid)
|
30
31
|
out.announce_start(id, specification)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
specification: specification,
|
48
|
-
errors: e.message
|
49
|
-
).tap do |result|
|
50
|
-
out.final_verdict(result)
|
32
|
+
|
33
|
+
config.run_single_test_with_callbacks do
|
34
|
+
clean_and_seed(specification)
|
35
|
+
|
36
|
+
spoon_client_result = execute_spoon(specification, config)
|
37
|
+
judge_result = assert(specification, spoon_client_result)
|
38
|
+
|
39
|
+
Result.new(
|
40
|
+
id: id,
|
41
|
+
judge_result: judge_result,
|
42
|
+
specification: specification,
|
43
|
+
spoon_client_result: spoon_client_result
|
44
|
+
).tap { |result| out.final_verdict(result) }
|
45
|
+
rescue Database::FixtureSet::FixtureMissingError, Simmer::Runner::TimeoutError => e
|
46
|
+
Result.new(id: id, specification: specification, errors: e)
|
47
|
+
.tap { |result| out.final_verdict(result) }
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
@@ -107,7 +104,7 @@ module Simmer
|
|
107
104
|
def execute_spoon(specification, config)
|
108
105
|
out.waiting('Act', 'Executing Spoon')
|
109
106
|
|
110
|
-
spoon_client_result = spoon_client.run(specification, config) do |output|
|
107
|
+
spoon_client_result = spoon_client.run(specification, config.config) do |output|
|
111
108
|
out.capture_spoon_output(output)
|
112
109
|
end
|
113
110
|
|
@@ -117,7 +114,7 @@ module Simmer
|
|
117
114
|
spoon_client_result
|
118
115
|
rescue Timeout::Error => e
|
119
116
|
out.console_puts('Timed out')
|
120
|
-
raise e
|
117
|
+
raise Simmer::Runner::TimeoutError, e
|
121
118
|
end
|
122
119
|
|
123
120
|
def assert(specification, spoon_client_result)
|
data/lib/simmer/runner/result.rb
CHANGED
@@ -44,11 +44,16 @@ module Simmer
|
|
44
44
|
errors.empty?,
|
45
45
|
].all?
|
46
46
|
end
|
47
|
+
alias passing? pass?
|
47
48
|
|
48
49
|
def fail?
|
49
50
|
!pass?
|
50
51
|
end
|
51
52
|
|
53
|
+
def timed_out?
|
54
|
+
errors.any? { |e| e.is_a?(Simmer::Runner::TimeoutError) }
|
55
|
+
end
|
56
|
+
|
52
57
|
def to_h
|
53
58
|
{
|
54
59
|
'name' => specification.name,
|
@@ -58,7 +63,7 @@ module Simmer
|
|
58
63
|
'pass' => pass?,
|
59
64
|
'spoon_client_result' => spoon_client_result.to_h,
|
60
65
|
'judge_result' => judge_result.to_h,
|
61
|
-
'errors' => errors,
|
66
|
+
'errors' => errors.map(&:message),
|
62
67
|
}
|
63
68
|
end
|
64
69
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2020-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Simmer
|
11
|
+
class Runner
|
12
|
+
# This error used when a specification times out. It is stored in
|
13
|
+
# <tt>Simmer::Runner::Results#errors</tt> when a specification times out.
|
14
|
+
class TimeoutError < RuntimeError
|
15
|
+
def message
|
16
|
+
cause ? cause.message : DEFAULT_MESSAGE
|
17
|
+
end
|
18
|
+
|
19
|
+
DEFAULT_MESSAGE = 'a timeout occurred'
|
20
|
+
private_constant :DEFAULT_MESSAGE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/simmer/suite.rb
CHANGED
@@ -24,7 +24,7 @@ module Simmer
|
|
24
24
|
results_dir:,
|
25
25
|
runner:
|
26
26
|
)
|
27
|
-
@config = config
|
27
|
+
@config = config
|
28
28
|
@out = out
|
29
29
|
@resolver = resolver
|
30
30
|
@results_dir = results_dir
|
@@ -34,15 +34,17 @@ module Simmer
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def run(specifications)
|
37
|
-
|
38
|
-
|
37
|
+
config.run_suite_with_callbacks do
|
38
|
+
runner_results = run_all_specs(specifications)
|
39
|
+
runner.complete
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
Result.new(runner_results).tap do |result|
|
42
|
+
output_summary(result.pass?)
|
42
43
|
|
43
|
-
|
44
|
+
ResulstWriter.new(result, results_dir).write!
|
44
45
|
|
45
|
-
|
46
|
+
out.puts("Results can be viewed at #{results_dir}")
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
data/lib/simmer/version.rb
CHANGED
data/simmer.gemspec
CHANGED
@@ -43,4 +43,5 @@ Gem::Specification.new do |s|
|
|
43
43
|
s.add_development_dependency('rubocop', '~>0.79.0')
|
44
44
|
s.add_development_dependency('simplecov', '~>0.17.0')
|
45
45
|
s.add_development_dependency('simplecov-console', '~>0.6.0')
|
46
|
+
s.add_development_dependency('terminal-notifier-guard')
|
46
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-06-
|
12
|
+
date: 2020-06-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: acts_as_hashable
|
@@ -207,6 +207,20 @@ dependencies:
|
|
207
207
|
- - "~>"
|
208
208
|
- !ruby/object:Gem::Version
|
209
209
|
version: 0.6.0
|
210
|
+
- !ruby/object:Gem::Dependency
|
211
|
+
name: terminal-notifier-guard
|
212
|
+
requirement: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - ">="
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
type: :development
|
218
|
+
prerelease: false
|
219
|
+
version_requirements: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
210
224
|
description: " Provides a harness for testing Pentaho Data Integration jobs and
|
211
225
|
transformations.\n"
|
212
226
|
email:
|
@@ -231,7 +245,9 @@ files:
|
|
231
245
|
- bin/console
|
232
246
|
- exe/simmer
|
233
247
|
- lib/simmer.rb
|
248
|
+
- lib/simmer/bootstrap.rb
|
234
249
|
- lib/simmer/configuration.rb
|
250
|
+
- lib/simmer/configuration/callback_dsl.rb
|
235
251
|
- lib/simmer/core_ext/hash.rb
|
236
252
|
- lib/simmer/database.rb
|
237
253
|
- lib/simmer/database/fixture.rb
|
@@ -246,8 +262,10 @@ files:
|
|
246
262
|
- lib/simmer/externals/sql_writers/sql_fixture.rb
|
247
263
|
- lib/simmer/judge.rb
|
248
264
|
- lib/simmer/judge/result.rb
|
265
|
+
- lib/simmer/re_runner.rb
|
249
266
|
- lib/simmer/runner.rb
|
250
267
|
- lib/simmer/runner/result.rb
|
268
|
+
- lib/simmer/runner/timeout_error.rb
|
251
269
|
- lib/simmer/specification.rb
|
252
270
|
- lib/simmer/specification/act.rb
|
253
271
|
- lib/simmer/specification/act/params.rb
|