ntl-orchestra 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +539 -0
- data/Rakefile +21 -0
- data/bin/rake +16 -0
- data/lib/orchestra/conductor.rb +119 -0
- data/lib/orchestra/configuration.rb +12 -0
- data/lib/orchestra/dsl/nodes.rb +72 -0
- data/lib/orchestra/dsl/object_adapter.rb +134 -0
- data/lib/orchestra/dsl/operations.rb +108 -0
- data/lib/orchestra/errors.rb +44 -0
- data/lib/orchestra/node/output.rb +61 -0
- data/lib/orchestra/node.rb +130 -0
- data/lib/orchestra/operation.rb +49 -0
- data/lib/orchestra/performance.rb +137 -0
- data/lib/orchestra/recording.rb +83 -0
- data/lib/orchestra/run_list.rb +171 -0
- data/lib/orchestra/thread_pool.rb +163 -0
- data/lib/orchestra/util.rb +98 -0
- data/lib/orchestra/version.rb +3 -0
- data/lib/orchestra.rb +35 -0
- data/orchestra.gemspec +26 -0
- data/test/examples/fizz_buzz.rb +32 -0
- data/test/examples/invitation_service.rb +118 -0
- data/test/integration/multithreading_test.rb +38 -0
- data/test/integration/recording_telemetry_test.rb +86 -0
- data/test/integration/replayable_operation_test.rb +53 -0
- data/test/lib/console.rb +103 -0
- data/test/lib/test_runner.rb +19 -0
- data/test/support/telemetry_recorder.rb +49 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/conductor_test.rb +25 -0
- data/test/unit/dsl_test.rb +122 -0
- data/test/unit/node_test.rb +122 -0
- data/test/unit/object_adapter_test.rb +100 -0
- data/test/unit/operation_test.rb +224 -0
- data/test/unit/run_list_test.rb +131 -0
- data/test/unit/thread_pool_test.rb +105 -0
- data/test/unit/util_test.rb +20 -0
- data/tmp/.keep +0 -0
- metadata +159 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
class DSLTest < Minitest::Test
|
2
|
+
def test_failing_to_supply_perform_block
|
3
|
+
error = assert_raises ArgumentError do
|
4
|
+
Orchestra::Node::InlineNode.build do
|
5
|
+
provides :foo
|
6
|
+
depends_on :bar
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_equal "expected inline node to define a perform block", error.message
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_two_nodes_one_name
|
14
|
+
error = assert_raises ArgumentError do
|
15
|
+
Orchestra.define do
|
16
|
+
node :foo do
|
17
|
+
depends_on :bar
|
18
|
+
perform do bar + bar end
|
19
|
+
end
|
20
|
+
node :foo do
|
21
|
+
depends_on :qux
|
22
|
+
perform do qux * qux end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
assert_equal "There are duplicate nodes named :foo", error.message
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_result_node
|
31
|
+
operation = Orchestra.define do
|
32
|
+
result :foo do perform do 'foo' end end
|
33
|
+
end
|
34
|
+
assert_equal :foo, operation.result
|
35
|
+
|
36
|
+
error = assert_raises ArgumentError do
|
37
|
+
operation = Orchestra.define do
|
38
|
+
result do perform do 'foo' end end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
assert_equal "Could not infer name for node from a provision", error.message
|
42
|
+
|
43
|
+
operation = Orchestra.define do
|
44
|
+
result do
|
45
|
+
provides :foo
|
46
|
+
perform do 'foo' end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
assert_equal :foo, operation.result
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_command_operations_using_finally
|
53
|
+
operation = Orchestra.define do
|
54
|
+
node :unnecessary do
|
55
|
+
provides :baz
|
56
|
+
perform do raise "Can't get here" end
|
57
|
+
end
|
58
|
+
|
59
|
+
node :necessary do
|
60
|
+
depends_on :baz
|
61
|
+
provides :bar
|
62
|
+
perform do baz + 1 end
|
63
|
+
end
|
64
|
+
|
65
|
+
finally do
|
66
|
+
depends_on :bar
|
67
|
+
perform do bar * 2 end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
test_observer = Module.new do
|
72
|
+
extend self
|
73
|
+
attr :result
|
74
|
+
def update event, *args
|
75
|
+
return unless event == :operation_exited
|
76
|
+
_, @result = args
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
conductor = Orchestra::Conductor.new
|
81
|
+
conductor.add_observer test_observer
|
82
|
+
|
83
|
+
assert_equal nil, conductor.perform(operation, :baz => 3)
|
84
|
+
assert_equal 8, test_observer.result
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_modifies
|
88
|
+
operation = Orchestra.define do
|
89
|
+
result do
|
90
|
+
modifies :list
|
91
|
+
perform do list << :foo end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
ary = []
|
96
|
+
Orchestra.perform operation, :list => ary
|
97
|
+
|
98
|
+
assert_equal [:foo], ary
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_must_supply_result
|
102
|
+
error = assert_raises ArgumentError do
|
103
|
+
Orchestra.define do
|
104
|
+
node :foo do
|
105
|
+
perform do 'foo' end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
assert_equal "Must supply a result", error.message
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_must_contain_at_least_one_node
|
114
|
+
error = assert_raises ArgumentError do
|
115
|
+
Orchestra.define do
|
116
|
+
self.result = :foo
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
assert_equal "Must supply at least one node", error.message
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
class NodeTest < Minitest::Test
|
2
|
+
def test_performing_a_node
|
3
|
+
node = build_simple_node
|
4
|
+
|
5
|
+
assert_equal(
|
6
|
+
{ :bar => 4 },
|
7
|
+
node.perform(:foo => 2, :bar => 2),
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_providing_a_single_hash
|
12
|
+
node = Orchestra::Node::InlineNode.new(
|
13
|
+
:dependencies => [:foo],
|
14
|
+
:provides => [:bar],
|
15
|
+
:perform_block => lambda { { :bar => (foo * 2) } },
|
16
|
+
)
|
17
|
+
|
18
|
+
assert_equal(
|
19
|
+
{ :bar => 4 },
|
20
|
+
node.perform(:foo => 2),
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_providing_a_single_hash_that_is_not_the_output
|
25
|
+
node = Orchestra::Node::InlineNode.new(
|
26
|
+
:dependencies => [:foo],
|
27
|
+
:provides => [:bar],
|
28
|
+
:perform_block => lambda { { :baz => (foo * 2) } },
|
29
|
+
)
|
30
|
+
|
31
|
+
assert_equal(
|
32
|
+
{ :bar => { :baz => 4 } },
|
33
|
+
node.perform(:foo => 2),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_performing_a_collection_node
|
38
|
+
node = Orchestra::Node::InlineNode.new(
|
39
|
+
:dependencies => [:foo],
|
40
|
+
:provides => [:bar],
|
41
|
+
:perform_block => lambda { |e| e * 2 },
|
42
|
+
:collection => :foo,
|
43
|
+
)
|
44
|
+
|
45
|
+
assert_equal(
|
46
|
+
{ :bar => [2, 4, 6, 8] },
|
47
|
+
node.perform(:foo => [1, 2, 3, 4]),
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_defaulting
|
52
|
+
node = build_simple_node
|
53
|
+
|
54
|
+
assert_equal(
|
55
|
+
{ :bar => 8 },
|
56
|
+
node.perform(:foo => 2),
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_introspecting_dependencies
|
61
|
+
node = build_simple_node
|
62
|
+
|
63
|
+
assert_equal [:foo, :bar], node.dependencies
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_introspecting_mandatory_dependencies
|
67
|
+
node = build_simple_node
|
68
|
+
|
69
|
+
assert_equal [:foo], node.required_dependencies
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_node_fails_to_supply_provisions
|
73
|
+
node = Orchestra::Node::InlineNode.new(
|
74
|
+
:provides => [:foo, :bar, :baz],
|
75
|
+
:perform_block => lambda { nil },
|
76
|
+
)
|
77
|
+
|
78
|
+
error = assert_raises Orchestra::MissingProvisionError do node.perform end
|
79
|
+
|
80
|
+
assert_equal(
|
81
|
+
"failed to supply output: :foo, :bar and :baz",
|
82
|
+
error.message,
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_cannot_return_nil
|
87
|
+
node = Orchestra::Node::InlineNode.new(
|
88
|
+
:provides => [:foo],
|
89
|
+
:perform_block => lambda do nil end
|
90
|
+
)
|
91
|
+
|
92
|
+
error = assert_raises Orchestra::MissingProvisionError do node.perform end
|
93
|
+
|
94
|
+
assert_equal(
|
95
|
+
"failed to supply output: :foo",
|
96
|
+
error.message,
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_node_provides_extra_provisions
|
101
|
+
node = Orchestra::Node::InlineNode.new(
|
102
|
+
:provides => [:foo],
|
103
|
+
:perform_block => lambda do { :foo => :bar, :baz => :qux } end,
|
104
|
+
)
|
105
|
+
|
106
|
+
assert_equal(
|
107
|
+
{ :foo => :bar },
|
108
|
+
node.perform,
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def build_simple_node
|
115
|
+
Orchestra::Node::InlineNode.new(
|
116
|
+
:defaults => { :bar => lambda { 4 } },
|
117
|
+
:dependencies => [:foo, :bar],
|
118
|
+
:provides => [:bar],
|
119
|
+
:perform_block => lambda { foo * bar },
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class ObjectAdapterTest < Minitest::Test
|
2
|
+
def setup
|
3
|
+
@builder = Orchestra::DSL::Operations::Builder.new
|
4
|
+
end
|
5
|
+
|
6
|
+
def test_method_does_not_exist_on_singleton
|
7
|
+
error = assert_raises NotImplementedError do
|
8
|
+
@builder.add_node Splitter, :provides => :words, :method => :foo
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_equal "ObjectAdapterTest::Splitter does not implement method `foo'", error.message
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_method_does_not_exist_on_object
|
15
|
+
error = assert_raises NotImplementedError do
|
16
|
+
@builder.add_node Upcaser, :provides => :words
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal "ObjectAdapterTest::Upcaser does not implement instance method `perform'", error.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_dependencies_inferred_from_method_defaults
|
23
|
+
node = @builder.add_node Upcaser, :iterates_over => :words, :provides => :upcased_words, :method => :call
|
24
|
+
|
25
|
+
assert_equal [:words, :transform], node.dependencies
|
26
|
+
assert_equal [:words], node.required_dependencies
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_performing_an_operation_with_integrated_objects
|
30
|
+
operation = Orchestra.define do
|
31
|
+
node Splitter, :provides => :words
|
32
|
+
node Upcaser, :iterates_over => :words, :provides => :upcased_words, :method => :call
|
33
|
+
node Bolder, :iterates_over => :upcased_words, :provides => :bolded_words, :method => :call
|
34
|
+
node Joiner, :method => :join
|
35
|
+
self.result = :joiner
|
36
|
+
end
|
37
|
+
|
38
|
+
result = Orchestra.perform(
|
39
|
+
operation,
|
40
|
+
:sentence => "the quick brown fox jumps over the lazy dog",
|
41
|
+
:bold_text => "*",
|
42
|
+
)
|
43
|
+
|
44
|
+
assert_equal(
|
45
|
+
%(*THE* *QUICK* *BROWN* *FOX* *JUMPS* *OVER* *THE* *LAZY* *DOG*),
|
46
|
+
result,
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_provent_singleton_objects_from_handling_collections
|
51
|
+
error = assert_raises ArgumentError do
|
52
|
+
Orchestra.define do
|
53
|
+
node Splitter, :iterates_over => :sentence
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_equal(
|
58
|
+
"ObjectAdapterTest::Splitter is a singleton; cannot iterate over collection :sentence",
|
59
|
+
error.message
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
module Splitter
|
64
|
+
def self.perform sentence
|
65
|
+
sentence.split %r{[[:space:]]+}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Upcaser
|
70
|
+
def initialize transform = :upcase
|
71
|
+
@transform = transform
|
72
|
+
end
|
73
|
+
|
74
|
+
def call element
|
75
|
+
element.public_send @transform
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Bolder
|
80
|
+
attr :bold_text
|
81
|
+
|
82
|
+
def initialize bold_text = "**"
|
83
|
+
@bold_text = bold_text
|
84
|
+
end
|
85
|
+
|
86
|
+
def call word
|
87
|
+
"#{bold_text}#{word}#{bold_text}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Joiner
|
92
|
+
def initialize bolded_words
|
93
|
+
@bolded_words = bolded_words
|
94
|
+
end
|
95
|
+
|
96
|
+
def join
|
97
|
+
@bolded_words.join ' '
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
module OperationTest
|
2
|
+
class PerformTest < Minitest::Test
|
3
|
+
def test_simple_operation
|
4
|
+
operation = build_simple_operation
|
5
|
+
|
6
|
+
assert_equal(
|
7
|
+
%(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG),
|
8
|
+
operation.perform(:sentence => %(the quick brown fox jumps over the lazy dog)),
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_performing_operation_without_inputs
|
13
|
+
operation = build_simple_operation
|
14
|
+
|
15
|
+
error = assert_raises Orchestra::MissingInputError do
|
16
|
+
operation.perform
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal %(Missing input :sentence), error.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_mutating_inputs
|
23
|
+
operation = build_mutator
|
24
|
+
|
25
|
+
shopping_list = [%(1 clove garlic)]
|
26
|
+
operation.perform :shopping_list => shopping_list
|
27
|
+
|
28
|
+
assert_equal(
|
29
|
+
[%(1 clove garlic), %(2 bunches of carrots), %(1 stalk of celery), %(3 yellow onions)],
|
30
|
+
shopping_list,
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_skipping_unnecessary_steps
|
35
|
+
operation = build_simple_operation
|
36
|
+
|
37
|
+
assert_equal(
|
38
|
+
%(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG),
|
39
|
+
operation.perform(:upcased_word_list => %w(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG)),
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_passing_conductor_into_nodes
|
44
|
+
conductor = Orchestra::Conductor.new
|
45
|
+
|
46
|
+
node = Orchestra::Node::InlineNode.build do
|
47
|
+
depends_on :conductor
|
48
|
+
provides :conductor_id
|
49
|
+
perform do conductor.object_id end
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_equal conductor.object_id, node.perform[:conductor_id]
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_missing_input_errors
|
56
|
+
operation = Orchestra.define do
|
57
|
+
node :foo do
|
58
|
+
depends_on :bar
|
59
|
+
perform do bar + bar end
|
60
|
+
end
|
61
|
+
node :baz do
|
62
|
+
depends_on :qux
|
63
|
+
perform do qux * qux end
|
64
|
+
end
|
65
|
+
node :result do
|
66
|
+
depends_on :foo, :baz
|
67
|
+
perform do baz - foo end
|
68
|
+
end
|
69
|
+
self.result = :result
|
70
|
+
end
|
71
|
+
|
72
|
+
error = assert_raises Orchestra::MissingInputError do
|
73
|
+
Orchestra.perform operation, :bar => nil
|
74
|
+
end
|
75
|
+
|
76
|
+
assert_equal "Missing inputs :bar and :qux", error.message
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def build_simple_operation
|
82
|
+
Orchestra.define do
|
83
|
+
node :split do
|
84
|
+
depends_on :sentence
|
85
|
+
provides :word_list
|
86
|
+
perform do sentence.split %r{[[:space:]]+} end
|
87
|
+
end
|
88
|
+
|
89
|
+
node :upcase do
|
90
|
+
depends_on :word_list
|
91
|
+
provides :upcased_word_list
|
92
|
+
perform do word_list.map &:upcase end
|
93
|
+
end
|
94
|
+
|
95
|
+
node :join do
|
96
|
+
depends_on :upcased_word_list
|
97
|
+
perform do upcased_word_list.join ' ' end
|
98
|
+
end
|
99
|
+
|
100
|
+
self.result = :join
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_mutator
|
105
|
+
Orchestra.define do
|
106
|
+
node :carrots do
|
107
|
+
depends_on :shopping_list
|
108
|
+
provides :shopping_list
|
109
|
+
perform do shopping_list << "2 bunches of carrots" end
|
110
|
+
end
|
111
|
+
|
112
|
+
node :celery do
|
113
|
+
depends_on :shopping_list
|
114
|
+
provides :shopping_list
|
115
|
+
perform do shopping_list << "1 stalk of celery" end
|
116
|
+
end
|
117
|
+
|
118
|
+
node :onions do
|
119
|
+
depends_on :shopping_list
|
120
|
+
provides :shopping_list
|
121
|
+
perform do shopping_list << "3 yellow onions" end
|
122
|
+
end
|
123
|
+
|
124
|
+
self.result = :shopping_list
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
class IntrospectionTest < Minitest::Test
|
131
|
+
def test_introspecting_dependencies
|
132
|
+
node = Orchestra::Node::InlineNode.build do
|
133
|
+
depends_on :foo, :bar => :baz
|
134
|
+
provides :baz
|
135
|
+
perform do :noop end
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_equal [:foo, :bar], node.dependencies
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_introspecting_optional_dependencies
|
142
|
+
node = Orchestra::Node::InlineNode.build do
|
143
|
+
depends_on :foo, :bar => :baz
|
144
|
+
provides :qux
|
145
|
+
perform do :noop end
|
146
|
+
end
|
147
|
+
|
148
|
+
assert_equal [:bar], node.optional_dependencies
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_introspecting_mandatory_dependencies
|
152
|
+
node = Orchestra::Node::InlineNode.build do
|
153
|
+
depends_on :foo, :bar => :baz
|
154
|
+
provides :baz
|
155
|
+
perform do :noop end
|
156
|
+
end
|
157
|
+
|
158
|
+
assert_equal [:foo], node.required_dependencies
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
class EmbeddingOperationsTest < Minitest::Test
|
164
|
+
def test_embedding_operations
|
165
|
+
inner = Orchestra.define do
|
166
|
+
node :double do
|
167
|
+
depends_on :number
|
168
|
+
provides :doubled
|
169
|
+
perform do number * 2 end
|
170
|
+
end
|
171
|
+
|
172
|
+
result :plus_one do
|
173
|
+
depends_on :doubled
|
174
|
+
perform do doubled + 1 end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
outer = Orchestra.define do
|
179
|
+
node inner
|
180
|
+
|
181
|
+
result :squared do
|
182
|
+
depends_on :plus_one
|
183
|
+
perform do plus_one ** 2 end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
telemetry = {}
|
188
|
+
|
189
|
+
conductor = Orchestra::Conductor.new
|
190
|
+
conductor.add_observer TelemetryRecorder.new telemetry
|
191
|
+
|
192
|
+
result = conductor.perform outer, :number => 4
|
193
|
+
|
194
|
+
assert_equal 81, result
|
195
|
+
|
196
|
+
assert_equal expected_telemetry, telemetry
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def expected_telemetry
|
202
|
+
{
|
203
|
+
:input => { :number => 4 },
|
204
|
+
:movements => {
|
205
|
+
:double => {
|
206
|
+
:input => { :number => 4 },
|
207
|
+
:output => { :doubled => 8 },
|
208
|
+
},
|
209
|
+
:plus_one => {
|
210
|
+
:input => { :doubled => 8 },
|
211
|
+
:output => { :plus_one => 9 },
|
212
|
+
},
|
213
|
+
:squared => {
|
214
|
+
:input => { :plus_one => 9 },
|
215
|
+
:output => { :squared => 81 },
|
216
|
+
},
|
217
|
+
},
|
218
|
+
:output => 81,
|
219
|
+
:performance_name => nil,
|
220
|
+
:service_calls => [],
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
class RunListTest < Minitest::Test
|
2
|
+
def test_all_are_required
|
3
|
+
builder.input_names << :foo
|
4
|
+
|
5
|
+
run_list = builder.build
|
6
|
+
|
7
|
+
assert_equal %w(foo⇒bar bar⇒baz baz⇒qux qux⇒res), run_list.node_names
|
8
|
+
assert_includes run_list.dependencies, :foo
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_discards_unnecessary_nodes
|
12
|
+
builder['aba⇒cab'] = OpenStruct.new :required_dependencies => [:aba], :optional_dependencies => [], :provisions => [:cab]
|
13
|
+
|
14
|
+
run_list = builder.build
|
15
|
+
|
16
|
+
assert_equal %w(foo⇒bar bar⇒baz baz⇒qux qux⇒res), run_list.node_names
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_supplying_dependencies
|
20
|
+
builder.input_names << :baz
|
21
|
+
|
22
|
+
run_list = builder.build
|
23
|
+
|
24
|
+
assert_equal %w(baz⇒qux qux⇒res), run_list.node_names
|
25
|
+
refute_includes run_list.dependencies, :foo
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_nodes_that_modify
|
29
|
+
assemble_builder modifying_nodes
|
30
|
+
|
31
|
+
run_list = builder.build
|
32
|
+
|
33
|
+
assert_equal %w(foo bar baz), run_list.node_names
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_reorders_optional_deps_before_mandatory_deps_when_possible
|
37
|
+
assemble_builder order_changes_because_of_optional_deps
|
38
|
+
|
39
|
+
run_list = builder.build
|
40
|
+
|
41
|
+
assert_equal %w(baz+foo bar+baz foo+bar final), run_list.node_names
|
42
|
+
assert_equal [], run_list.required_dependencies
|
43
|
+
assert_equal [:bar, :baz, :foo], run_list.optional_dependencies
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_wrap_tsort_cycle_errors
|
47
|
+
assemble_builder circular_dependency_tree
|
48
|
+
|
49
|
+
error = assert_raises Orchestra::CircularDependencyError do
|
50
|
+
builder.build
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_equal(
|
54
|
+
"Circular dependency detected! Check your dependencies/provides",
|
55
|
+
error.message
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def assemble_builder nodes = default_nodes
|
62
|
+
@builder ||= begin
|
63
|
+
builder = Orchestra::RunList::Builder.new :res
|
64
|
+
builder.merge! nodes
|
65
|
+
builder
|
66
|
+
end
|
67
|
+
end
|
68
|
+
alias_method :builder, :assemble_builder
|
69
|
+
|
70
|
+
def default_nodes
|
71
|
+
{
|
72
|
+
'foo⇒bar' => OpenStruct.new(:required_dependencies => [:foo], :provisions => [:bar], optional_dependencies: []),
|
73
|
+
'bar⇒baz' => OpenStruct.new(:required_dependencies => [:bar], :provisions => [:baz], optional_dependencies: []),
|
74
|
+
'baz⇒qux' => OpenStruct.new(:required_dependencies => [:baz], :provisions => [:qux], optional_dependencies: []),
|
75
|
+
'qux⇒res' => OpenStruct.new(:required_dependencies => [:qux], :provisions => [:res], optional_dependencies: []),
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def modifying_nodes
|
80
|
+
{
|
81
|
+
'foo' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared], optional_dependencies: []),
|
82
|
+
'bar' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared], optional_dependencies: []),
|
83
|
+
'baz' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared, :res], optional_dependencies: []),
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def circular_dependency_tree
|
88
|
+
{
|
89
|
+
'foo+bar' => OpenStruct.new(
|
90
|
+
:optional_dependencies => [:bar],
|
91
|
+
:required_dependencies => [:foo],
|
92
|
+
:provisions => [:aba]
|
93
|
+
),
|
94
|
+
'bar+baz' => OpenStruct.new(
|
95
|
+
:optional_dependencies => [:foo],
|
96
|
+
:required_dependencies => [:bar],
|
97
|
+
:provisions => [:cab],
|
98
|
+
),
|
99
|
+
'final' => OpenStruct.new(
|
100
|
+
:optional_dependencies => [],
|
101
|
+
:required_dependencies => [:aba, :cab],
|
102
|
+
:provisions => [:res],
|
103
|
+
)
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def order_changes_because_of_optional_deps
|
108
|
+
{
|
109
|
+
'foo+bar' => OpenStruct.new(
|
110
|
+
:optional_dependencies => [],
|
111
|
+
:required_dependencies => [:foo, :bar],
|
112
|
+
:provisions => [:aba]
|
113
|
+
),
|
114
|
+
'bar+baz' => OpenStruct.new(
|
115
|
+
:optional_dependencies => [:bar],
|
116
|
+
:required_dependencies => [:baz],
|
117
|
+
:provisions => [:cab],
|
118
|
+
),
|
119
|
+
'baz+foo' => OpenStruct.new(
|
120
|
+
:optional_dependencies => [:baz, :foo],
|
121
|
+
:required_dependencies => [],
|
122
|
+
:provisions => [:bra],
|
123
|
+
),
|
124
|
+
'final' => OpenStruct.new(
|
125
|
+
:optional_dependencies => [],
|
126
|
+
:required_dependencies => [:aba, :cab, :bra],
|
127
|
+
:provisions => [:res],
|
128
|
+
),
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|