1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
require "dependency"
describe Dependency do
def build_dep(name, tags = [], deps = [])
dep = described_class.new(name.to_s, tags)
allow(dep).to receive(:to_formula).and_return(double(deps: deps, name: name))
dep
end
let(:foo) { build_dep(:foo) }
let(:bar) { build_dep(:bar) }
let(:baz) { build_dep(:baz) }
let(:qux) { build_dep(:qux) }
let(:deps) { [foo, bar, baz, qux] }
let(:formula) { double(deps: deps, name: "f") }
describe "::expand" do
it "yields dependent and dependency pairs" do
i = 0
described_class.expand(formula) do |dependent, dep|
expect(dependent).to eq(formula)
expect(deps[i]).to eq(dep)
i += 1
end
end
it "returns the dependencies" do
expect(described_class.expand(formula)).to eq(deps)
end
it "prunes all when given a block with ::prune" do
expect(described_class.expand(formula) { described_class.prune }).to be_empty
end
it "can prune selectively" do
deps = described_class.expand(formula) do |_, dep|
described_class.prune if dep.name == "foo"
end
expect(deps).to eq([bar, baz, qux])
end
it "preserves dependency order" do
allow(foo).to receive(:to_formula).and_return(double(name: "f", deps: [qux, baz]))
expect(described_class.expand(formula)).to eq([qux, baz, foo, bar])
end
end
it "skips optionals by default" do
deps = [build_dep(:foo, [:optional]), bar, baz, qux]
f = double(deps: deps, build: double(with?: false), name: "f")
expect(described_class.expand(f)).to eq([bar, baz, qux])
end
it "keeps recommended dependencies by default" do
deps = [build_dep(:foo, [:recommended]), bar, baz, qux]
f = double(deps: deps, build: double(with?: true), name: "f")
expect(described_class.expand(f)).to eq(deps)
end
it "merges repeated dependencies with differing options" do
foo2 = build_dep(:foo, ["option"])
baz2 = build_dep(:baz, ["option"])
deps << foo2 << baz2
deps = [foo2, bar, baz2, qux]
deps.zip(described_class.expand(formula)) do |expected, actual|
expect(expected.tags).to eq(actual.tags)
expect(expected).to eq(actual)
end
end
it "merges dependencies and perserves env_proc" do
env_proc = double
dep = described_class.new("foo", [], env_proc)
allow(dep).to receive(:to_formula).and_return(double(deps: [], name: "foo"))
deps.replace([dep])
expect(described_class.expand(formula).first.env_proc).to eq(env_proc)
end
it "merges tags without duplicating them" do
foo2 = build_dep(:foo, ["option"])
foo3 = build_dep(:foo, ["option"])
deps << foo2 << foo3
expect(described_class.expand(formula).first.tags).to eq(%w[option])
end
it "skips parent but yields children with ::skip" do
f = double(
name: "f",
deps: [
build_dep(:foo, [], [bar, baz]),
build_dep(:foo, [], [baz]),
],
)
deps = described_class.expand(f) do |_dependent, dep|
described_class.skip if %w[foo qux].include? dep.name
end
expect(deps).to eq([bar, baz])
end
it "keeps dependency but prunes recursive dependencies with ::keep_but_prune_recursive_deps" do
foo = build_dep(:foo, [:build], bar)
baz = build_dep(:baz, [:build])
f = double(name: "f", deps: [foo, baz])
deps = described_class.expand(f) do |_dependent, dep|
described_class.keep_but_prune_recursive_deps if dep.build?
end
expect(deps).to eq([foo, baz])
end
it "returns only the dependencies given as a collection as second argument" do
expect(formula.deps).to eq([foo, bar, baz, qux])
expect(described_class.expand(formula, [bar, baz])).to eq([bar, baz])
end
it "doesn't raise an error when a dependency is cyclic" do
foo = build_dep(:foo)
bar = build_dep(:bar, [], [foo])
allow(foo).to receive(:to_formula).and_return(double(deps: [bar], name: foo.name))
f = double(name: "f", deps: [foo, bar])
expect { described_class.expand(f) }.not_to raise_error
end
it "cleans the expand stack" do
foo = build_dep(:foo)
allow(foo).to receive(:to_formula).and_raise(FormulaUnavailableError, foo.name)
f = double(name: "f", deps: [foo])
expect { described_class.expand(f) }.to raise_error(FormulaUnavailableError)
expect(described_class.instance_variable_get(:@expand_stack)).to be_empty
end
end
|