simmer 2.0.0.pre.alpha → 3.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/.rubocop.yml +7 -1
- data/.ruby-version +1 -1
- data/.travis.yml +3 -3
- data/CHANGELOG.md +33 -0
- data/README.md +9 -0
- data/lib/simmer.rb +23 -17
- data/lib/simmer/database/fixture_set.rb +3 -1
- data/lib/simmer/externals/mysql_database.rb +11 -3
- data/lib/simmer/externals/spoon_client.rb +8 -5
- data/lib/simmer/judge/result.rb +1 -1
- data/lib/simmer/runner.rb +56 -51
- data/lib/simmer/runner/result.rb +20 -6
- data/lib/simmer/suite.rb +14 -7
- data/lib/simmer/suite/output_router.rb +73 -0
- data/lib/simmer/suite/pdi_output_writer.rb +56 -0
- data/lib/simmer/suite/result.rb +1 -0
- data/lib/simmer/suite/results_writer.rb +44 -0
- data/lib/simmer/util.rb +1 -0
- data/lib/simmer/util/file_system.rb +25 -0
- data/lib/simmer/version.rb +1 -1
- data/simmer.gemspec +3 -2
- metadata +27 -9
- data/lib/simmer/suite/reporter.rb +0 -83
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c2be2ad790619cfd4a18f34df9c05b9a575a67b80a8d8be4a4e2a0fae4f1271
|
|
4
|
+
data.tar.gz: 5467d82cf7c7be7c1aab033b45302d6b97d74b56447194ddd3b8ac4760a7ff11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 28076b241e673455e18ffc482829425f81754ffde566e58fd9b011cb5d0dd40cb1143b6a35f51f59789096406e5ace09e707e72fa7de640f786b55863a0d7242
|
|
7
|
+
data.tar.gz: eb7a544f07c273e659aa313877741ddb9628ec1e03f04002f63e26663000f47efa8161582a61146f5a5a059130202bafd469047d00fabfcb1371fa1b323d01f2
|
data/.rubocop.yml
CHANGED
|
@@ -13,7 +13,7 @@ Metrics/BlockLength:
|
|
|
13
13
|
- define
|
|
14
14
|
|
|
15
15
|
Metrics/MethodLength:
|
|
16
|
-
Max:
|
|
16
|
+
Max: 30
|
|
17
17
|
|
|
18
18
|
AllCops:
|
|
19
19
|
TargetRubyVersion: 2.5
|
|
@@ -23,3 +23,9 @@ Metrics/AbcSize:
|
|
|
23
23
|
|
|
24
24
|
Metrics/ClassLength:
|
|
25
25
|
Max: 125
|
|
26
|
+
|
|
27
|
+
Style/TrailingCommaInHashLiteral:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/TrailingCommaInArrayLiteral:
|
|
31
|
+
Enabled: false
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.6.
|
|
1
|
+
2.6.6
|
data/.travis.yml
CHANGED
|
@@ -6,9 +6,9 @@ services:
|
|
|
6
6
|
- mysql
|
|
7
7
|
rvm:
|
|
8
8
|
# Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- 2.5.8
|
|
10
|
+
- 2.6.6
|
|
11
|
+
- 2.7.1
|
|
12
12
|
cache: bundler
|
|
13
13
|
before_script:
|
|
14
14
|
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
data/CHANGELOG.md
CHANGED
|
@@ -1 +1,34 @@
|
|
|
1
|
+
# Simmer Change Log
|
|
1
2
|
|
|
3
|
+
## 3.0.0 (June 8th, 2020)
|
|
4
|
+
|
|
5
|
+
Breaking Changes:
|
|
6
|
+
|
|
7
|
+
* `Simmer::Runner` now accepts a `Simmer::Suite::OutputRouter` instead of an `IO` instance as its 'out' parameter.
|
|
8
|
+
* The `execution_output` and `execution_result` methods have been removed from `Simmer::Runner::Result`.
|
|
9
|
+
|
|
10
|
+
Additions:
|
|
11
|
+
|
|
12
|
+
* pdi_out.txt is written to throughout test execution instead of at the end.
|
|
13
|
+
|
|
14
|
+
Fixes:
|
|
15
|
+
|
|
16
|
+
* Fixtures now handle identifiers which are MySQL reserved words.
|
|
17
|
+
|
|
18
|
+
## 2.1.0 (May 13th, 2020)
|
|
19
|
+
|
|
20
|
+
Additions:
|
|
21
|
+
|
|
22
|
+
* Do not make missing fixtures short-circuit the rest of the test suite.
|
|
23
|
+
* Do not make PDI timeouts short-circuit the rest of the test suite.
|
|
24
|
+
* Report PDI's exit code and execution time to the console.
|
|
25
|
+
|
|
26
|
+
## 2.0.0 (May 11th, 2020)
|
|
27
|
+
|
|
28
|
+
Breaking Changes:
|
|
29
|
+
|
|
30
|
+
* Do not emit error file, standard error has been combined with the standard output within the underlying pdi library.
|
|
31
|
+
|
|
32
|
+
Additions:
|
|
33
|
+
|
|
34
|
+
* Enhanced Simmer configuration for `spoon_client` to account for `timeout_in_seconds` option.
|
data/README.md
CHANGED
|
@@ -281,6 +281,15 @@ Basic steps to take to get this repository compiling:
|
|
|
281
281
|
3. Clone the repository (git clone git@github.com:bluemarblepayroll/simmer.git)
|
|
282
282
|
4. Navigate to the root folder (cd simmer)
|
|
283
283
|
5. Install dependencies (bundle)
|
|
284
|
+
6. Create the 'simmer_test' MySQL database as defined in `spec/db/tables.sql`.
|
|
285
|
+
7. Add the tables from `spec/db/tables.sql` to this database.
|
|
286
|
+
8. Configure your test simmer.yaml:
|
|
287
|
+
|
|
288
|
+
````bash
|
|
289
|
+
cp spec/config/simmer.yaml.ci spec/config/simmer.yaml
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
9. Edit `spec/config/simmer.yaml` so that it can connect to the database created in step seven.
|
|
284
293
|
|
|
285
294
|
### Running Tests
|
|
286
295
|
|
data/lib/simmer.rb
CHANGED
|
@@ -51,7 +51,8 @@ module Simmer
|
|
|
51
51
|
)
|
|
52
52
|
configuration = make_configuration(config_path: config_path, simmer_dir: simmer_dir)
|
|
53
53
|
specs = make_specifications(path, configuration.tests_dir)
|
|
54
|
-
|
|
54
|
+
out_router = make_output_router(configuration, out)
|
|
55
|
+
runner = make_runner(configuration, out_router)
|
|
55
56
|
suite = make_suite(configuration, out, runner)
|
|
56
57
|
|
|
57
58
|
suite.run(specs)
|
|
@@ -66,6 +67,21 @@ module Simmer
|
|
|
66
67
|
Configuration.new(raw_config, simmer_dir)
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
def make_runner(configuration, out_router)
|
|
71
|
+
database = make_mysql_database(configuration)
|
|
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
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
69
85
|
private
|
|
70
86
|
|
|
71
87
|
def yaml_reader
|
|
@@ -82,21 +98,6 @@ module Simmer
|
|
|
82
98
|
end
|
|
83
99
|
end
|
|
84
100
|
|
|
85
|
-
def make_runner(configuration, out)
|
|
86
|
-
database = make_mysql_database(configuration)
|
|
87
|
-
file_system = make_file_system(configuration)
|
|
88
|
-
fixture_set = make_fixture_set(configuration)
|
|
89
|
-
spoon_client = make_spoon_client(configuration)
|
|
90
|
-
|
|
91
|
-
Runner.new(
|
|
92
|
-
database: database,
|
|
93
|
-
file_system: file_system,
|
|
94
|
-
fixture_set: fixture_set,
|
|
95
|
-
out: out,
|
|
96
|
-
spoon_client: spoon_client
|
|
97
|
-
)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
101
|
def make_fixture_set(configuration)
|
|
101
102
|
config = Util::YamlReader.new.smash(configuration.fixtures_dir)
|
|
102
103
|
|
|
@@ -142,7 +143,7 @@ module Simmer
|
|
|
142
143
|
|
|
143
144
|
def make_spoon_client(configuration)
|
|
144
145
|
config = (configuration.spoon_client_config || {}).symbolize_keys
|
|
145
|
-
spoon_args = config.slice(:args, :dir, :kitchen, :pan)
|
|
146
|
+
spoon_args = config.slice(:args, :dir, :kitchen, :pan, :timeout_in_seconds)
|
|
146
147
|
spoon = Pdi::Spoon.new(spoon_args)
|
|
147
148
|
|
|
148
149
|
Externals::SpoonClient.new(configuration.files_dir, spoon)
|
|
@@ -156,5 +157,10 @@ module Simmer
|
|
|
156
157
|
runner: runner
|
|
157
158
|
)
|
|
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)
|
|
164
|
+
end
|
|
159
165
|
end
|
|
160
166
|
end
|
|
@@ -13,6 +13,8 @@ module Simmer
|
|
|
13
13
|
module Database
|
|
14
14
|
# Hydrate a collection of Fixture instances from configuration.
|
|
15
15
|
class FixtureSet
|
|
16
|
+
class FixtureMissingError < StandardError; end
|
|
17
|
+
|
|
16
18
|
def initialize(config = {})
|
|
17
19
|
@fixtures_by_name = config_to_fixures_by_name(config)
|
|
18
20
|
|
|
@@ -22,7 +24,7 @@ module Simmer
|
|
|
22
24
|
def get!(name)
|
|
23
25
|
key = name.to_s
|
|
24
26
|
|
|
25
|
-
raise
|
|
27
|
+
raise FixtureMissingError, "fixture missing: #{name}" unless fixtures_by_name.key?(key)
|
|
26
28
|
|
|
27
29
|
fixtures_by_name[key]
|
|
28
30
|
end
|
|
@@ -27,7 +27,7 @@ module Simmer
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def records(table, columns = [])
|
|
30
|
-
query = "SELECT #{sql_select_params(columns)} FROM #{table}"
|
|
30
|
+
query = "SELECT #{sql_select_params(columns)} FROM #{qualify(table)}"
|
|
31
31
|
|
|
32
32
|
client.query(query).to_a
|
|
33
33
|
end
|
|
@@ -53,7 +53,11 @@ module Simmer
|
|
|
53
53
|
attr_reader :client, :fixture_set, :table_names
|
|
54
54
|
|
|
55
55
|
def sql_select_params(columns)
|
|
56
|
-
Array(columns).any?
|
|
56
|
+
if Array(columns).any?
|
|
57
|
+
Array(columns).map { |c| qualify(client.escape(c)).to_s }.join(',')
|
|
58
|
+
else
|
|
59
|
+
'*'
|
|
60
|
+
end
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
def seed_sql_statements(fixtures)
|
|
@@ -62,7 +66,7 @@ module Simmer
|
|
|
62
66
|
|
|
63
67
|
def clean_sql_statements
|
|
64
68
|
table_names.map do |table_name|
|
|
65
|
-
"TRUNCATE #{table_name}"
|
|
69
|
+
"TRUNCATE #{qualify(table_name)}"
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -110,6 +114,10 @@ module Simmer
|
|
|
110
114
|
|
|
111
115
|
raise ArgumentError, "database (#{name}) must end in #{DATABASE_SUFFIX}"
|
|
112
116
|
end
|
|
117
|
+
|
|
118
|
+
def qualify(identifier)
|
|
119
|
+
"`#{identifier}`"
|
|
120
|
+
end
|
|
113
121
|
end
|
|
114
122
|
end
|
|
115
123
|
end
|
|
@@ -13,6 +13,8 @@ module Simmer
|
|
|
13
13
|
module Externals
|
|
14
14
|
# Wraps up Pdi::Spoon at a higher-level for Simmer to consume.
|
|
15
15
|
class SpoonClient
|
|
16
|
+
attr_reader :spoon
|
|
17
|
+
|
|
16
18
|
def initialize(files_dir, spoon)
|
|
17
19
|
raise ArgumentError, 'spoon is required' unless spoon
|
|
18
20
|
|
|
@@ -22,13 +24,13 @@ module Simmer
|
|
|
22
24
|
freeze
|
|
23
25
|
end
|
|
24
26
|
|
|
25
|
-
def run(specification, config)
|
|
27
|
+
def run(specification, config, &output_capturer)
|
|
26
28
|
execution_result = nil
|
|
27
29
|
time_in_seconds = nil
|
|
28
30
|
|
|
29
31
|
begin
|
|
30
32
|
time_in_seconds = Benchmark.measure do
|
|
31
|
-
execution_result = execute!(specification, config)
|
|
33
|
+
execution_result = execute!(specification, config, &output_capturer)
|
|
32
34
|
end.real
|
|
33
35
|
rescue Pdi::Spoon::PanError, Pdi::Spoon::KitchenError => e
|
|
34
36
|
return Result.new(
|
|
@@ -46,16 +48,17 @@ module Simmer
|
|
|
46
48
|
|
|
47
49
|
private
|
|
48
50
|
|
|
49
|
-
attr_reader :files_dir
|
|
51
|
+
attr_reader :files_dir
|
|
50
52
|
|
|
51
|
-
def execute!(specification, config)
|
|
53
|
+
def execute!(specification, config, &output_capturer)
|
|
52
54
|
act = specification.act
|
|
53
55
|
|
|
54
56
|
spoon.run(
|
|
55
57
|
repository: act.repository,
|
|
56
58
|
name: act.name,
|
|
57
59
|
params: act.compiled_params(files_dir, config),
|
|
58
|
-
type: act.type
|
|
60
|
+
type: act.type,
|
|
61
|
+
&output_capturer
|
|
59
62
|
)
|
|
60
63
|
end
|
|
61
64
|
end
|
data/lib/simmer/judge/result.rb
CHANGED
data/lib/simmer/runner.rb
CHANGED
|
@@ -13,6 +13,8 @@ require_relative 'runner/result'
|
|
|
13
13
|
module Simmer
|
|
14
14
|
# Runs a single specification.
|
|
15
15
|
class Runner
|
|
16
|
+
attr_reader :spoon_client
|
|
17
|
+
|
|
16
18
|
def initialize(database:, file_system:, fixture_set:, out:, spoon_client:)
|
|
17
19
|
@database = database
|
|
18
20
|
@file_system = file_system
|
|
@@ -25,109 +27,112 @@ module Simmer
|
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def run(specification, config: {}, id: SecureRandom.uuid)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
clean_db
|
|
32
|
-
seed_db(specification)
|
|
33
|
-
clean_file_system
|
|
34
|
-
seed_file_system(specification)
|
|
30
|
+
out.announce_start(id, specification)
|
|
31
|
+
clean_and_seed(specification)
|
|
35
32
|
|
|
36
33
|
spoon_client_result = execute_spoon(specification, config)
|
|
37
34
|
judge_result = assert(specification, spoon_client_result)
|
|
38
35
|
|
|
39
|
-
Result.new(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
Result.new(
|
|
37
|
+
id: id,
|
|
38
|
+
judge_result: judge_result,
|
|
39
|
+
specification: specification,
|
|
40
|
+
spoon_client_result: spoon_client_result
|
|
41
|
+
).tap do |result|
|
|
42
|
+
out.final_verdict(result)
|
|
43
|
+
end
|
|
44
|
+
rescue Database::FixtureSet::FixtureMissingError, Timeout::Error => e
|
|
45
|
+
Result.new(
|
|
46
|
+
id: id,
|
|
47
|
+
specification: specification,
|
|
48
|
+
errors: e.message
|
|
49
|
+
).tap do |result|
|
|
50
|
+
out.final_verdict(result)
|
|
43
51
|
end
|
|
44
52
|
end
|
|
45
53
|
|
|
54
|
+
def complete
|
|
55
|
+
out.close
|
|
56
|
+
end
|
|
57
|
+
|
|
46
58
|
private
|
|
47
59
|
|
|
48
|
-
attr_reader :database, :file_system, :fixture_set, :judge, :out
|
|
60
|
+
attr_reader :database, :file_system, :fixture_set, :judge, :out
|
|
61
|
+
|
|
62
|
+
def clean_and_seed(specification)
|
|
63
|
+
clean_db
|
|
64
|
+
seed_db(specification)
|
|
65
|
+
clean_file_system
|
|
66
|
+
seed_file_system(specification)
|
|
67
|
+
end
|
|
49
68
|
|
|
50
69
|
def clean_db
|
|
51
|
-
|
|
70
|
+
out.waiting('Stage', 'Cleaning database')
|
|
52
71
|
count = database.clean!
|
|
53
|
-
|
|
72
|
+
out.console_puts("#{count} table(s) emptied")
|
|
54
73
|
|
|
55
74
|
count
|
|
56
75
|
end
|
|
57
76
|
|
|
58
77
|
def seed_db(specification)
|
|
59
|
-
|
|
78
|
+
out.waiting('Stage', 'Seeding database')
|
|
60
79
|
|
|
61
80
|
fixtures = specification.stage.fixtures.map { |f| fixture_set.get!(f) }
|
|
62
81
|
count = database.seed!(fixtures)
|
|
63
82
|
|
|
64
|
-
|
|
83
|
+
out.console_puts("#{count} record(s) inserted")
|
|
65
84
|
|
|
66
85
|
count
|
|
86
|
+
rescue Database::FixtureSet::FixtureMissingError => e
|
|
87
|
+
out.console_puts('Missing Fixture(s)')
|
|
88
|
+
raise e
|
|
67
89
|
end
|
|
68
90
|
|
|
69
91
|
def clean_file_system
|
|
70
|
-
|
|
92
|
+
out.waiting('Stage', 'Cleaning File System')
|
|
71
93
|
count = file_system.clean!
|
|
72
|
-
|
|
94
|
+
out.console_puts("#{count} file(s) deleted")
|
|
73
95
|
|
|
74
96
|
count
|
|
75
97
|
end
|
|
76
98
|
|
|
77
99
|
def seed_file_system(specification)
|
|
78
|
-
|
|
100
|
+
out.waiting('Stage', 'Seeding File System')
|
|
79
101
|
count = file_system.write!(specification.stage.files)
|
|
80
|
-
|
|
102
|
+
out.console_puts("#{count} file(s) uploaded")
|
|
81
103
|
|
|
82
104
|
count
|
|
83
105
|
end
|
|
84
106
|
|
|
85
107
|
def execute_spoon(specification, config)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
out.waiting('Act', 'Executing Spoon')
|
|
109
|
+
|
|
110
|
+
spoon_client_result = spoon_client.run(specification, config) do |output|
|
|
111
|
+
out.capture_spoon_output(output)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
out.finish_spec
|
|
115
|
+
out.spoon_execution_detail_message(spoon_client_result)
|
|
90
116
|
|
|
91
117
|
spoon_client_result
|
|
118
|
+
rescue Timeout::Error => e
|
|
119
|
+
out.console_puts('Timed out')
|
|
120
|
+
raise e
|
|
92
121
|
end
|
|
93
122
|
|
|
94
123
|
def assert(specification, spoon_client_result)
|
|
95
|
-
|
|
124
|
+
out.waiting('Assert', 'Checking results')
|
|
96
125
|
|
|
97
126
|
if spoon_client_result.fail?
|
|
98
|
-
|
|
127
|
+
out.console_puts('Skipped')
|
|
99
128
|
return nil
|
|
100
129
|
end
|
|
101
130
|
|
|
102
131
|
output = spoon_client_result.execution_result.out
|
|
103
132
|
judge_result = judge.assert(specification, output)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
print(msg)
|
|
133
|
+
out.result(judge_result)
|
|
107
134
|
|
|
108
135
|
judge_result
|
|
109
136
|
end
|
|
110
|
-
|
|
111
|
-
def print(msg)
|
|
112
|
-
out.puts(msg)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def print_waiting(stage, msg)
|
|
116
|
-
max = 25
|
|
117
|
-
char = '.'
|
|
118
|
-
msg = " > #{pad_right(stage, 6)} - #{pad_right(msg, max, char)}"
|
|
119
|
-
|
|
120
|
-
out.print(msg)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def pad_right(msg, len, char = ' ')
|
|
124
|
-
missing = len - msg.length
|
|
125
|
-
|
|
126
|
-
"#{msg}#{char * missing}"
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def pass_message(obj)
|
|
130
|
-
obj.pass? ? 'Pass' : 'Fail'
|
|
131
|
-
end
|
|
132
137
|
end
|
|
133
138
|
end
|
data/lib/simmer/runner/result.rb
CHANGED
|
@@ -13,23 +13,36 @@ module Simmer
|
|
|
13
13
|
class Result
|
|
14
14
|
extend Forwardable
|
|
15
15
|
|
|
16
|
-
attr_reader :id, :judge_result, :specification, :spoon_client_result
|
|
17
|
-
|
|
18
|
-
def_delegators :spoon_client_result, :time_in_seconds
|
|
16
|
+
attr_reader :errors, :id, :judge_result, :specification, :spoon_client_result
|
|
19
17
|
|
|
20
18
|
def_delegators :specification, :name
|
|
21
19
|
|
|
22
|
-
def initialize(
|
|
20
|
+
def initialize(
|
|
21
|
+
id:,
|
|
22
|
+
specification:,
|
|
23
|
+
judge_result: nil,
|
|
24
|
+
spoon_client_result: nil,
|
|
25
|
+
errors: []
|
|
26
|
+
)
|
|
23
27
|
@id = id.to_s
|
|
24
28
|
@judge_result = judge_result
|
|
25
29
|
@specification = specification
|
|
26
30
|
@spoon_client_result = spoon_client_result
|
|
31
|
+
@errors = Array(errors)
|
|
27
32
|
|
|
28
33
|
freeze
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
def time_in_seconds
|
|
37
|
+
spoon_client_result&.time_in_seconds || 0
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
def pass?
|
|
32
|
-
|
|
41
|
+
[
|
|
42
|
+
judge_result&.pass?,
|
|
43
|
+
spoon_client_result&.pass?,
|
|
44
|
+
errors.empty?,
|
|
45
|
+
].all?
|
|
33
46
|
end
|
|
34
47
|
|
|
35
48
|
def fail?
|
|
@@ -44,7 +57,8 @@ module Simmer
|
|
|
44
57
|
'time_in_seconds' => time_in_seconds,
|
|
45
58
|
'pass' => pass?,
|
|
46
59
|
'spoon_client_result' => spoon_client_result.to_h,
|
|
47
|
-
'judge_result' => judge_result.to_h
|
|
60
|
+
'judge_result' => judge_result.to_h,
|
|
61
|
+
'errors' => errors,
|
|
48
62
|
}
|
|
49
63
|
end
|
|
50
64
|
end
|
data/lib/simmer/suite.rb
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
|
8
8
|
#
|
|
9
9
|
|
|
10
|
-
require_relative 'suite/
|
|
10
|
+
require_relative 'suite/output_router'
|
|
11
|
+
require_relative 'suite/pdi_output_writer'
|
|
12
|
+
require_relative 'suite/results_writer'
|
|
11
13
|
require_relative 'suite/result'
|
|
12
14
|
|
|
13
15
|
module Simmer
|
|
@@ -33,15 +35,12 @@ module Simmer
|
|
|
33
35
|
|
|
34
36
|
def run(specifications)
|
|
35
37
|
runner_results = run_all_specs(specifications)
|
|
38
|
+
runner.complete
|
|
36
39
|
|
|
37
40
|
Result.new(runner_results).tap do |result|
|
|
38
|
-
|
|
39
|
-
out.puts('Suite ended successfully')
|
|
40
|
-
else
|
|
41
|
-
out.puts('Suite ended but was not successful')
|
|
42
|
-
end
|
|
41
|
+
output_summary(result.pass?)
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
ResulstWriter.new(result, results_dir).write!
|
|
45
44
|
|
|
46
45
|
out.puts("Results can be viewed at #{results_dir}")
|
|
47
46
|
end
|
|
@@ -77,5 +76,13 @@ module Simmer
|
|
|
77
76
|
def print_line
|
|
78
77
|
out.puts('-' * LINE_LENGTH)
|
|
79
78
|
end
|
|
79
|
+
|
|
80
|
+
def output_summary(passed)
|
|
81
|
+
if passed
|
|
82
|
+
out.puts('Suite ended successfully')
|
|
83
|
+
else
|
|
84
|
+
out.puts('Suite ended but was not successful')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
80
87
|
end
|
|
81
88
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Simmer
|
|
4
|
+
class Suite
|
|
5
|
+
# Routes output either to the console or the <tt>PdiOutputWriter</tt>. It
|
|
6
|
+
# also provides some methods to help format output.
|
|
7
|
+
class OutputRouter
|
|
8
|
+
extend Forwardable
|
|
9
|
+
|
|
10
|
+
attr_reader :console, :pdi_out
|
|
11
|
+
|
|
12
|
+
def_delegator :console, :puts, :console_puts
|
|
13
|
+
def_delegators :pdi_out, :close, :finish_spec
|
|
14
|
+
def_delegator :pdi_out, :write, :capture_spoon_output
|
|
15
|
+
|
|
16
|
+
def initialize(console, pdi_out)
|
|
17
|
+
@console = console || raise(ArgumentError, 'console is required')
|
|
18
|
+
@pdi_out = pdi_out || raise(ArgumentError, 'pdi_out is required')
|
|
19
|
+
|
|
20
|
+
freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def announce_start(id, specification)
|
|
24
|
+
console_puts("Name: #{specification.name}")
|
|
25
|
+
console_puts("Path: #{specification.path}")
|
|
26
|
+
pdi_out.demarcate_spec(id, specification.name)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def result(result)
|
|
30
|
+
console_puts(pass_message(result))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def final_verdict(result)
|
|
34
|
+
msg = pass_message(result)
|
|
35
|
+
waiting('Done', 'Final verdict')
|
|
36
|
+
console_puts(msg)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def waiting(stage, msg)
|
|
40
|
+
# This is not for debugging.
|
|
41
|
+
# rubocop:disable Lint/Debugger
|
|
42
|
+
console.print(
|
|
43
|
+
" > #{pad_right(stage, 6)} - #{pad_right(msg, WAITING_MAX_WIDTH, WAITING_PADDING_CHAR)}"
|
|
44
|
+
)
|
|
45
|
+
# rubocop:enable Lint/Debugger
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def spoon_execution_detail_message(spoon_client_result)
|
|
49
|
+
code = spoon_client_result.execution_result.status.code
|
|
50
|
+
detail = "(Exited with code #{code} after #{spoon_client_result.time_in_seconds} seconds)"
|
|
51
|
+
|
|
52
|
+
console_puts("#{pass_message(spoon_client_result)} #{detail}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
WAITING_MAX_WIDTH = 25
|
|
58
|
+
WAITING_PADDING_CHAR = '.'
|
|
59
|
+
|
|
60
|
+
private_constant :WAITING_MAX_WIDTH, :WAITING_PADDING_CHAR
|
|
61
|
+
|
|
62
|
+
def pad_right(msg, len, char = ' ')
|
|
63
|
+
missing = len - msg.length
|
|
64
|
+
|
|
65
|
+
"#{msg}#{char * missing}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def pass_message(obj)
|
|
69
|
+
obj.pass? ? 'Pass' : 'Fail'
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
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 Suite
|
|
12
|
+
# Captures PDI output from multiple specifications to a single file.
|
|
13
|
+
class PdiOutputWriter # :nodoc:
|
|
14
|
+
PDI_OUT_FILE = 'pdi_out.txt'
|
|
15
|
+
|
|
16
|
+
attr_reader :results_dir
|
|
17
|
+
|
|
18
|
+
def initialize(results_dir)
|
|
19
|
+
raise ArgumentError, 'results_dir is required' unless results_dir
|
|
20
|
+
|
|
21
|
+
results_dir = Util::FileSystem.setup_directory(results_dir)
|
|
22
|
+
@out = File.new(File.join(results_dir, PDI_OUT_FILE), 'w')
|
|
23
|
+
|
|
24
|
+
freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def demarcate_spec(runner_id, spec_name)
|
|
28
|
+
out.puts(LINE_OF_HYPHENS)
|
|
29
|
+
out.puts("Name: #{spec_name}")
|
|
30
|
+
out.puts("Runner ID: #{runner_id}")
|
|
31
|
+
out.puts(LINE_OF_HYPHENS)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def write(contents)
|
|
35
|
+
bytes_written = out.write(contents)
|
|
36
|
+
out.flush
|
|
37
|
+
bytes_written
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def finish_spec
|
|
41
|
+
out.puts
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def close
|
|
45
|
+
out.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
attr_reader :out
|
|
51
|
+
|
|
52
|
+
LINE_OF_HYPHENS = ('-' * 80).freeze
|
|
53
|
+
private_constant :LINE_OF_HYPHENS
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/simmer/suite/result.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
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 Suite
|
|
12
|
+
# Understands how to write a SessionResult instance to disk.
|
|
13
|
+
class ResulstWriter
|
|
14
|
+
DATA_FILE = 'data.yaml'
|
|
15
|
+
|
|
16
|
+
# Pass in dir here:
|
|
17
|
+
def initialize(session_result, results_dir)
|
|
18
|
+
raise ArgumentError, 'session_result is required' unless session_result
|
|
19
|
+
raise ArgumentError, 'results_directory is required' unless results_dir
|
|
20
|
+
|
|
21
|
+
@session_result = session_result
|
|
22
|
+
@results_directory = Util::FileSystem.setup_directory(results_dir)
|
|
23
|
+
|
|
24
|
+
freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def write!
|
|
28
|
+
dir = Util::FileSystem.setup_directory(results_directory)
|
|
29
|
+
|
|
30
|
+
IO.write(data_path(dir), session_result.to_h.to_yaml)
|
|
31
|
+
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
attr_reader :results_directory, :session_result
|
|
38
|
+
|
|
39
|
+
def data_path(dir)
|
|
40
|
+
File.join(dir, DATA_FILE)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/simmer/util.rb
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
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 'resolver'
|
|
11
|
+
|
|
12
|
+
module Simmer
|
|
13
|
+
module Util
|
|
14
|
+
# Provides convenience methods for working with the file system.
|
|
15
|
+
class FileSystem # :nodoc:
|
|
16
|
+
class << self
|
|
17
|
+
def setup_directory(dir_path)
|
|
18
|
+
File.expand_path(dir_path).tap do |expanded_dir|
|
|
19
|
+
FileUtils.mkdir_p(expanded_dir)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/simmer/version.rb
CHANGED
data/simmer.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
|
11
11
|
Provides a harness for testing Pentaho Data Integration jobs and transformations.
|
|
12
12
|
DESCRIPTION
|
|
13
13
|
|
|
14
|
-
s.authors = ['Matthew Ruggio']
|
|
14
|
+
s.authors = ['Matthew Ruggio', 'Ryan Gerry']
|
|
15
15
|
s.email = ['mruggio@bluemarblepayroll.com']
|
|
16
16
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
17
|
s.bindir = 'exe'
|
|
@@ -32,11 +32,12 @@ Gem::Specification.new do |s|
|
|
|
32
32
|
s.add_dependency('aws-sdk-s3', '~>1.6')
|
|
33
33
|
s.add_dependency('mysql2', '~>0.5')
|
|
34
34
|
s.add_dependency('objectable', '~>1')
|
|
35
|
-
s.add_dependency('pdi', '
|
|
35
|
+
s.add_dependency('pdi', '~>2.1')
|
|
36
36
|
s.add_dependency('stringento', '~>2')
|
|
37
37
|
|
|
38
38
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
|
39
39
|
s.add_development_dependency('pry', '~>0')
|
|
40
|
+
s.add_development_dependency 'pry-byebug'
|
|
40
41
|
s.add_development_dependency('rake', '~> 13')
|
|
41
42
|
s.add_development_dependency('rspec')
|
|
42
43
|
s.add_development_dependency('rubocop', '~>0.79.0')
|
metadata
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simmer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew Ruggio
|
|
8
|
+
- Ryan Gerry
|
|
8
9
|
autorequire:
|
|
9
10
|
bindir: exe
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
12
|
+
date: 2020-06-10 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: acts_as_hashable
|
|
@@ -70,16 +71,16 @@ dependencies:
|
|
|
70
71
|
name: pdi
|
|
71
72
|
requirement: !ruby/object:Gem::Requirement
|
|
72
73
|
requirements:
|
|
73
|
-
- - "
|
|
74
|
+
- - "~>"
|
|
74
75
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 2.
|
|
76
|
+
version: '2.1'
|
|
76
77
|
type: :runtime
|
|
77
78
|
prerelease: false
|
|
78
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
80
|
requirements:
|
|
80
|
-
- - "
|
|
81
|
+
- - "~>"
|
|
81
82
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 2.
|
|
83
|
+
version: '2.1'
|
|
83
84
|
- !ruby/object:Gem::Dependency
|
|
84
85
|
name: stringento
|
|
85
86
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -122,6 +123,20 @@ dependencies:
|
|
|
122
123
|
- - "~>"
|
|
123
124
|
- !ruby/object:Gem::Version
|
|
124
125
|
version: '0'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: pry-byebug
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
125
140
|
- !ruby/object:Gem::Dependency
|
|
126
141
|
name: rake
|
|
127
142
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -245,10 +260,13 @@ files:
|
|
|
245
260
|
- lib/simmer/specification/stage.rb
|
|
246
261
|
- lib/simmer/specification/stage/input_file.rb
|
|
247
262
|
- lib/simmer/suite.rb
|
|
248
|
-
- lib/simmer/suite/
|
|
263
|
+
- lib/simmer/suite/output_router.rb
|
|
264
|
+
- lib/simmer/suite/pdi_output_writer.rb
|
|
249
265
|
- lib/simmer/suite/result.rb
|
|
266
|
+
- lib/simmer/suite/results_writer.rb
|
|
250
267
|
- lib/simmer/util.rb
|
|
251
268
|
- lib/simmer/util/evaluator.rb
|
|
269
|
+
- lib/simmer/util/file_system.rb
|
|
252
270
|
- lib/simmer/util/record.rb
|
|
253
271
|
- lib/simmer/util/record_set.rb
|
|
254
272
|
- lib/simmer/util/resolver.rb
|
|
@@ -275,9 +293,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
275
293
|
version: '2.5'
|
|
276
294
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
277
295
|
requirements:
|
|
278
|
-
- - "
|
|
296
|
+
- - ">="
|
|
279
297
|
- !ruby/object:Gem::Version
|
|
280
|
-
version:
|
|
298
|
+
version: '0'
|
|
281
299
|
requirements: []
|
|
282
300
|
rubygems_version: 3.0.3
|
|
283
301
|
signing_key:
|
|
@@ -1,83 +0,0 @@
|
|
|
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 Suite
|
|
12
|
-
# Understands how to write a SessionResult instance to disk.
|
|
13
|
-
class Reporter
|
|
14
|
-
DATA_FILE = 'data.yaml'
|
|
15
|
-
PDI_OUT_FILE = 'pdi_out.txt'
|
|
16
|
-
|
|
17
|
-
def initialize(session_result)
|
|
18
|
-
raise ArgumentError, 'session_result is required' unless session_result
|
|
19
|
-
|
|
20
|
-
@session_result = session_result
|
|
21
|
-
|
|
22
|
-
freeze
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def write!(dir)
|
|
26
|
-
dir = setup_directory(dir)
|
|
27
|
-
|
|
28
|
-
IO.write(data_path(dir), session_result.to_h.to_yaml)
|
|
29
|
-
|
|
30
|
-
pdi_out_file = File.open(pdi_out_path(dir), 'w')
|
|
31
|
-
|
|
32
|
-
write_part(session_result.runner_results, pdi_out_file)
|
|
33
|
-
|
|
34
|
-
pdi_out_file.close
|
|
35
|
-
|
|
36
|
-
self
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
private
|
|
40
|
-
|
|
41
|
-
attr_reader :session_result
|
|
42
|
-
|
|
43
|
-
def data_path(dir)
|
|
44
|
-
File.join(dir, DATA_FILE)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def pdi_out_path(dir)
|
|
48
|
-
File.join(dir, PDI_OUT_FILE)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def setup_directory(dir)
|
|
52
|
-
File.expand_path(dir).tap do |expanded_dir|
|
|
53
|
-
FileUtils.mkdir_p(expanded_dir)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def write_part(runner_results, pdi_out_file)
|
|
58
|
-
runner_results.each do |runner_result|
|
|
59
|
-
name = runner_result.name
|
|
60
|
-
runner_id = runner_result.id
|
|
61
|
-
out_contents = runner_result.spoon_client_result.execution_result.out
|
|
62
|
-
|
|
63
|
-
write_block(pdi_out_file, name, runner_id, out_contents)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
nil
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def write_block(file, name, runner_id, contents)
|
|
70
|
-
hyphens = '-' * 80
|
|
71
|
-
|
|
72
|
-
file.write("#{hyphens}\n")
|
|
73
|
-
file.write("Name: #{name}\n")
|
|
74
|
-
file.write("Runner ID: #{runner_id}\n")
|
|
75
|
-
file.write("#{hyphens}\n")
|
|
76
|
-
file.write("#{contents}\n")
|
|
77
|
-
file.write("\n")
|
|
78
|
-
|
|
79
|
-
nil
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|