summaryrefslogtreecommitdiff
path: root/spec/unit/node/attribute_spec.rb
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2017-10-09 09:54:25 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2017-12-06 12:44:05 -0800
commit0c29acc106ca774e230c8ef45694c8bffd166b69 (patch)
treea9c2f20d4da4d279163a24aeae2f725ac4ed6c2e /spec/unit/node/attribute_spec.rb
parenta987ec745a70ff683e2ee80af044b9f5e32854c9 (diff)
downloadchef-0c29acc106ca774e230c8ef45694c8bffd166b69.tar.gz
Per-container deep merge caching
Replaces the one-big top level deep merge cache with individual deep merge caches in every container. The Immutable container state becomes the deep merge cache. - Does not use a pure decorator style approach since that failed before because of ruby internals breaking things like `===` and `=~` on decorated objects, so we inherit (ultimately from Hash + Array). - The state being the container state is useful in ruby since APIs on Hash and Array poke around in internal state to make things fast. If we inherit from Hash/Array but don't have the correct internal state things go wonky. - Throwing away the internal state is equivalent to flushing the cache. - Since we throw away all linked objects when we do that, we flush at every level below the level being flushed (which is correct semantics). - If a user has a pointer to an old immutable object from a sub-level, that isn't mutated so the old object still contains the old view of the data (which I think is correct, although I have some doubts that its necessary, but it came along free for the ride). - When we reset the cache we do mutate the cache being reset, which might change data in held references. If this becomes an issue the fix would be to reset the cache at the level above by creating a new, "empty" ImmutableHash/ImmutableArray object and inserting it into the deep_merge_cache datastructure instead of clearing the internal state of the child object. I don't know practically how anyone would hit this, though, so would prefer to wait on doing that work until we see an actual bug report. - Because of the way ruby pokes around internally there's some weirdnesses like the pre-generation of the cache for all the values of a subarray when #each is called, which is due to the way that ruby walks through array-serialized hashes when called like: `Array#each { key, value| ... }` (which is an undocumented(?) thing in ruby). Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
Diffstat (limited to 'spec/unit/node/attribute_spec.rb')
-rw-r--r--spec/unit/node/attribute_spec.rb79
1 files changed, 55 insertions, 24 deletions
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index cf8d4d4a4f..90b3f1fe51 100644
--- a/spec/unit/node/attribute_spec.rb
+++ b/spec/unit/node/attribute_spec.rb
@@ -171,6 +171,7 @@ describe Chef::Node::Attribute do
}
@automatic_hash = { "week" => "friday" }
@attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
describe "initialize" do
@@ -179,13 +180,14 @@ describe Chef::Node::Attribute do
end
it "should take an Automatioc, Normal, Default and Override hash" do
- expect { Chef::Node::Attribute.new({}, {}, {}, {}) }.not_to raise_error
+ expect { Chef::Node::Attribute.new({}, {}, {}, {}, node) }.not_to raise_error
end
[ :normal, :default, :override, :automatic ].each do |accessor|
it "should set #{accessor}" do
- na = Chef::Node::Attribute.new({ :normal => true }, { :default => true }, { :override => true }, { :automatic => true })
- expect(na.send(accessor)).to eq({ accessor.to_s => true })
+ @attributes = Chef::Node::Attribute.new({ :normal => true }, { :default => true }, { :override => true }, { :automatic => true }, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
+ expect(@attributes.send(accessor)).to eq({ accessor.to_s => true })
end
end
@@ -330,7 +332,8 @@ describe Chef::Node::Attribute do
end
it "merges nested hashes between precedence levels" do
- @attributes = Chef::Node::Attribute.new({}, {}, {}, {})
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
@attributes.env_default = { "a" => { "b" => { "default" => "default" } } }
@attributes.normal = { "a" => { "b" => { "normal" => "normal" } } }
@attributes.override = { "a" => { "override" => "role" } }
@@ -584,8 +587,10 @@ describe Chef::Node::Attribute do
"one" => { "six" => "seven" },
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should yield each top level key" do
@@ -632,8 +637,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should yield each top level key and value, post merge rules" do
@@ -670,8 +677,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to each_key" do
@@ -706,8 +715,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to each_pair" do
@@ -742,8 +753,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to each_value" do
@@ -786,9 +799,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
- @empty = Chef::Node::Attribute.new({}, {}, {}, {})
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to empty?" do
@@ -796,7 +810,9 @@ describe Chef::Node::Attribute do
end
it "should return true when there are no keys" do
- expect(@empty.empty?).to eq(true)
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
+ expect(@attributes.empty?).to eq(true)
end
it "should return false when there are keys" do
@@ -820,8 +836,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to fetch" do
@@ -877,8 +895,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to has_value?" do
@@ -922,8 +942,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to index" do
@@ -963,8 +985,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to values" do
@@ -999,8 +1023,10 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
end
it "should respond to select" do
@@ -1049,10 +1075,11 @@ describe Chef::Node::Attribute do
"one" => "six",
"snack" => "cookies",
},
- {}
+ {},
+ node
)
+ allow(node).to receive(:attributes).and_return(@attributes)
- @empty = Chef::Node::Attribute.new({}, {}, {}, {})
end
it "should respond to size" do
@@ -1064,7 +1091,9 @@ describe Chef::Node::Attribute do
end
it "should return 0 for an empty attribute" do
- expect(@empty.size).to eq(0)
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
+ expect(@attributes.size).to eq(0)
end
it "should return the number of pairs" do
@@ -1092,8 +1121,9 @@ describe Chef::Node::Attribute do
describe "to_s" do
it "should output simple attributes" do
- attributes = Chef::Node::Attribute.new(nil, nil, nil, nil)
- expect(attributes.to_s).to eq("{}")
+ @attributes = Chef::Node::Attribute.new(nil, nil, nil, nil, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
+ expect(@attributes.to_s).to eq("{}")
end
it "should output merged attributes" do
@@ -1105,8 +1135,9 @@ describe Chef::Node::Attribute do
"b" => 3,
"c" => 4,
}
- attributes = Chef::Node::Attribute.new(nil, default_hash, override_hash, nil)
- expect(attributes.to_s).to eq('{"a"=>1, "b"=>3, "c"=>4}')
+ @attributes = Chef::Node::Attribute.new(nil, default_hash, override_hash, nil, node)
+ allow(node).to receive(:attributes).and_return(@attributes)
+ expect(@attributes.to_s).to eq('{"b"=>3, "c"=>4, "a"=>1}')
end
end