public void TestNodeMatcher() { INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); EditPatch patch = new EditPatch(UrlBuilder.CreateConfig("abc/def", new ConfigNode()), nodeMatcher, Substitute.For <IPassSpecifier>()); Assert.Same(nodeMatcher, patch.NodeMatcher); }
private void AssertNodeMatcher(INodeMatcher matcher) { Assert.True(matcher.IsMatch(new TestConfigNode("NODE") { { "name", "foo" }, { "bar", "baz" }, })); Assert.False(matcher.IsMatch(new TestConfigNode("NODE") { { "name", "foo" }, })); Assert.False(matcher.IsMatch(new TestConfigNode("NODE") { { "name", "boo" }, { "bar", "baz" }, })); Assert.False(matcher.IsMatch(new ConfigNode("NODE"))); Assert.False(matcher.IsMatch(new TestConfigNode("NADE") { { "name", "foo" }, { "bar", "baz" }, })); Assert.False(matcher.IsMatch(new TestConfigNode("NODE") { { "name", "boo" }, { "bar", "baz" }, })); }
public EditPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier) { UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher)); PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); loop = urlConfig.config.HasNode("MM_PATCH_LOOP"); }
/// <summary> /// Returns a verbose selector that will select this node. /// </summary> public INodeMatcher GenerateCssSelector(bool includeNthChild) { AndSelector selector = new AndSelector(); // name if (this.Name != null) { selector.Selectors.Add(new NameSelector(this.Name)); } // attributes foreach (string s in this.AttributeNames) { string[] parts = this[s].Split(' '); if (parts.Length == 1) { // 'class' is the only attribute I know of that allows mix and match, everything else should be exact match var op = (s != "class") ? AttributeSelector.EqualToOp // exact match : AttributeSelector.ContainsWordOp; // mix and match - also allows string to have .class for single classed elements selector.Selectors.Add(new AttributeSelector(s, op, parts[0])); } else { foreach (string part in parts) { selector.Selectors.Add(new AttributeSelector(s, AttributeSelector.ContainsWordOp, part)); } } } if (this.SiblingCount > 1 && includeNthChild) { selector.Selectors.Add(new SiblingSelector(0, this.SiblingIndex + 1)); } // : selectors var candidates = new INodeMatcher[] { // new ColonSelector(":first-child") // ,new ColonSelector(":last-child") // ,new ColonSelector(":empty") // ,new ColonSelector(":parent") }; foreach (var c in candidates) { if (c.IsMatch(this)) { selector.Selectors.Add(c); } } return(selector); }
public void TestApply() { ConfigNode config1 = new ConfigNode("NODE"); ConfigNode config2 = new ConfigNode("NODE"); ConfigNode config3 = new ConfigNode("NODE"); ConfigNode config4 = new ConfigNode("NODE"); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(config1).Returns(false); nodeMatcher.IsMatch(config2).Returns(true); nodeMatcher.IsMatch(config3).Returns(false); nodeMatcher.IsMatch(config4).Returns(true); DeletePatch patch = new DeletePatch(UrlBuilder.CreateConfig("ghi/jkl", new ConfigNode("!NODE")), nodeMatcher, Substitute.For <IPassSpecifier>()); IProtoUrlConfig urlConfig1 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig2 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig3 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig4 = Substitute.For <IProtoUrlConfig>(); urlConfig1.Node.Returns(config1); urlConfig2.Node.Returns(config2); urlConfig3.Node.Returns(config3); urlConfig4.Node.Returns(config4); LinkedList <IProtoUrlConfig> configs = new LinkedList <IProtoUrlConfig>(); configs.AddLast(urlConfig1); configs.AddLast(urlConfig2); configs.AddLast(urlConfig3); configs.AddLast(urlConfig4); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(configs, progress, logger); Assert.Equal(new[] { urlConfig1, urlConfig3 }, configs); Received.InOrder(delegate { progress.ApplyingDelete(urlConfig2, patch.UrlConfig); progress.ApplyingDelete(urlConfig4, patch.UrlConfig); }); progress.DidNotReceiveWithAnyArgs().ApplyingUpdate(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
internal void AddParent(INodeMatcher parentSelector) { AndSelector a = this.Selector as AndSelector; if (a == null) { this.Selector = a = new AndSelector(this.Selector); } // add at begining so it .ToString()s in the correct place a.Selectors.Insert(0, new HasParentSelector(parentSelector)); }
internal void AddSibling(INodeMatcher sibSelector, bool any) { AndSelector a = this.Selector as AndSelector; if (a == null) { this.Selector = a = new AndSelector(this.Selector); } // add at begining so it .ToString()s in the correct place a.Selectors.Insert(0, new HasPreviousSiblingSelector(sibSelector, any)); }
/// <summary> /// used for when a parent node must have a specific child /// </summary> internal void AddChild(INodeMatcher childSelector) { AndSelector a = this.Selector as AndSelector; if (a == null) { this.Selector = a = new AndSelector(this.Selector); } // add at begining so it .ToString()s in the correct place a.Selectors.Insert(0, new HasChildSelector(childSelector)); this.HasChildSelector = true; }
public void TestApply__NameChanged() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig(new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }, file); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(urlConfig.config).Returns(true); CopyPatch patch = new CopyPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("@NODE") { { "@name", "001" }, { "@foo", "baz" }, { "pqr", "stw" }, }), nodeMatcher, Substitute.For <IPassSpecifier>()); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(file, progress, logger); Assert.Equal(2, file.configs.Count); Assert.Same(urlConfig, file.configs[0]); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }, file.configs[0].config); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "001" }, { "foo", "baz" }, { "pqr", "stw" }, }, file.configs[1].config); progress.Received().ApplyingCopy(urlConfig, patch.UrlConfig); progress.DidNotReceiveWithAnyArgs().ApplyingUpdate(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
public void TestApply__NameNotChanged() { ConfigNode config = new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }; INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(config).Returns(true); CopyPatch patch = new CopyPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("+NODE") { { "@foo", "baz" }, { "pqr", "stw" }, }), nodeMatcher, Substitute.For <IPassSpecifier>()); IProtoUrlConfig protoConfig = Substitute.For <IProtoUrlConfig>(); protoConfig.Node.Returns(config); protoConfig.FullUrl.Returns("abc/def.cfg/NODE"); LinkedList <IProtoUrlConfig> configs = new LinkedList <IProtoUrlConfig>(); configs.AddLast(protoConfig); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(configs, progress, logger); Assert.Single(configs); Assert.Same(protoConfig, configs.First.Value); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }, configs.First.Value.Node); progress.Received().Error(patch.UrlConfig, "Error - when applying copy ghi/jkl/+NODE to abc/def.cfg/NODE - the copy needs to have a different name than the parent (use @name = xxx)"); progress.DidNotReceiveWithAnyArgs().ApplyingUpdate(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
public void TestApply() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); UrlDir.UrlConfig urlConfig1 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); UrlDir.UrlConfig urlConfig2 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); UrlDir.UrlConfig urlConfig3 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); UrlDir.UrlConfig urlConfig4 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(urlConfig1.config).Returns(false); nodeMatcher.IsMatch(urlConfig2.config).Returns(true); nodeMatcher.IsMatch(urlConfig3.config).Returns(false); nodeMatcher.IsMatch(urlConfig4.config).Returns(true); DeletePatch patch = new DeletePatch(UrlBuilder.CreateConfig("ghi/jkl", new ConfigNode("!NODE")), nodeMatcher, Substitute.For <IPassSpecifier>()); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(file, progress, logger); Assert.Equal(new[] { urlConfig1, urlConfig3 }, file.configs); Received.InOrder(delegate { progress.ApplyingDelete(urlConfig2, patch.UrlConfig); progress.ApplyingDelete(urlConfig4, patch.UrlConfig); }); progress.DidNotReceiveWithAnyArgs().ApplyingUpdate(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
/// <summary> /// Sets the strategy for selecting nodes to compare. /// </summary> /// <remarks> /// <para> /// Example with org.xmlunit.diff.DefaultNodeMatcher: /// <pre> /// .WithNodeMatcher(new DefaultNodeMatcher(ElementSelectors.ByNameAndText)) /// </pre> /// </para> /// </remarks> /* @see org.xmlunit.diff.DifferenceEngine#setNodeMatcher(NodeMatcher) */ public DiffBuilder WithNodeMatcher(INodeMatcher nodeMatcher) { this.nodeMatcher = nodeMatcher; return(this); }
public DeepStepSelector(INodeMatcher selector) : base(selector) { }
/// <param name="sibSelector"></param> /// <param name="any">true=>any sibling can match, false=>only immediate sibling can match</param> public HasPreviousSiblingSelector(INodeMatcher sibSelector, bool any) { this._sibSelector = sibSelector; this._any = any; }
public void TestApply() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); UrlDir.UrlConfig urlConfig1 = UrlBuilder.CreateConfig(new TestConfigNode("NODE") { { "foo", "bar" }, }, file); UrlDir.UrlConfig urlConfig2 = UrlBuilder.CreateConfig(new TestConfigNode("NODE") { { "foo", "bar" }, }, file); UrlDir.UrlConfig urlConfig3 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); UrlDir.UrlConfig urlConfig4 = UrlBuilder.CreateConfig(new ConfigNode("NODE"), file); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(urlConfig1.config).Returns(false); nodeMatcher.IsMatch(urlConfig2.config).Returns(true); nodeMatcher.IsMatch(urlConfig3.config).Returns(false); nodeMatcher.IsMatch(urlConfig4.config).Returns(true); EditPatch patch = new EditPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("@NODE") { { "@foo", "baz" }, { "pqr", "stw" }, }), nodeMatcher, Substitute.For <IPassSpecifier>()); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(file, progress, logger); Assert.Equal(4, file.configs.Count); Assert.Same(urlConfig1, file.configs[0]); AssertNodesEqual(new TestConfigNode("NODE") { { "foo", "bar" }, }, file.configs[0].config); Assert.NotSame(urlConfig2, file.configs[1]); AssertNodesEqual(new TestConfigNode("NODE") { { "foo", "baz" }, { "pqr", "stw" }, }, file.configs[1].config); Assert.Same(urlConfig3, file.configs[2]); AssertNodesEqual(new ConfigNode("NODE"), file.configs[2].config); Assert.NotSame(urlConfig4, file.configs[3]); AssertNodesEqual(new TestConfigNode("NODE") { { "pqr", "stw" }, }, file.configs[3].config); Received.InOrder(delegate { progress.ApplyingUpdate(urlConfig2, patch.UrlConfig); progress.ApplyingUpdate(urlConfig4, patch.UrlConfig); }); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
public void TestApply__Loop() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig(new TestConfigNode("NODE") { { "name", "000" }, { "aaa", "1" }, }, file); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(Arg.Is <ConfigNode>(node => int.Parse(node.GetValue("aaa")) < 10)).Returns(true); EditPatch patch = new EditPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("@NODE") { { "@aaa *", "2" }, { "bbb", "002" }, new ConfigNode("MM_PATCH_LOOP"), }), nodeMatcher, Substitute.For <IPassSpecifier>()); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); List <UrlDir.UrlConfig> modifiedUrlConfigs = new List <UrlDir.UrlConfig>(); progress.ApplyingUpdate(Arg.Do <UrlDir.UrlConfig>(url => modifiedUrlConfigs.Add(url)), patch.UrlConfig); patch.Apply(file, progress, logger); Assert.Equal(1, file.configs.Count); Assert.NotSame(urlConfig, file.configs[0]); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "000" }, { "aaa", "16" }, { "bbb", "002" }, { "bbb", "002" }, { "bbb", "002" }, { "bbb", "002" }, }, file.configs[0].config); Assert.Same(urlConfig, modifiedUrlConfigs[0]); Assert.NotSame(urlConfig, modifiedUrlConfigs[1]); Assert.NotSame(urlConfig, modifiedUrlConfigs[2]); Assert.NotSame(urlConfig, modifiedUrlConfigs[3]); Received.InOrder(delegate { logger.Log(LogType.Log, "Looping on ghi/jkl/@NODE to abc/def/NODE"); progress.ApplyingUpdate(urlConfig, patch.UrlConfig); progress.ApplyingUpdate(modifiedUrlConfigs[1], patch.UrlConfig); progress.ApplyingUpdate(modifiedUrlConfigs[2], patch.UrlConfig); progress.ApplyingUpdate(modifiedUrlConfigs[3], patch.UrlConfig); }); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
public CopyPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier) { UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher)); PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); }
public HasParentSelector(INodeMatcher childSelector) { this._parentSelector = childSelector; }
public void TestApply() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); ConfigNode config1 = new TestConfigNode("NODE") { { "foo", "bar" }, }; ConfigNode config2 = new TestConfigNode("NODE") { { "foo", "bar" }, }; ConfigNode config3 = new ConfigNode("NODE"); ConfigNode config4 = new ConfigNode("NODE"); INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(config1).Returns(false); nodeMatcher.IsMatch(config2).Returns(true); nodeMatcher.IsMatch(config3).Returns(false); nodeMatcher.IsMatch(config4).Returns(true); EditPatch patch = new EditPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("@NODE") { { "@foo", "baz" }, { "pqr", "stw" }, }), nodeMatcher, Substitute.For <IPassSpecifier>()); IProtoUrlConfig urlConfig1 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig2 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig3 = Substitute.For <IProtoUrlConfig>(); IProtoUrlConfig urlConfig4 = Substitute.For <IProtoUrlConfig>(); urlConfig1.Node.Returns(config1); urlConfig2.Node.Returns(config2); urlConfig3.Node.Returns(config3); urlConfig4.Node.Returns(config4); urlConfig1.UrlFile.Returns(file); urlConfig2.UrlFile.Returns(file); urlConfig3.UrlFile.Returns(file); urlConfig4.UrlFile.Returns(file); LinkedList <IProtoUrlConfig> configs = new LinkedList <IProtoUrlConfig>(); configs.AddLast(urlConfig1); configs.AddLast(urlConfig2); configs.AddLast(urlConfig3); configs.AddLast(urlConfig4); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(configs, progress, logger); IProtoUrlConfig[] newConfigs = configs.ToArray(); Assert.Equal(4, newConfigs.Length); Assert.Same(urlConfig1, newConfigs[0]); AssertNodesEqual(new TestConfigNode("NODE") { { "foo", "bar" }, }, newConfigs[0].Node); AssertNodesEqual(new TestConfigNode("NODE") { { "foo", "baz" }, { "pqr", "stw" }, }, newConfigs[1].Node); Assert.Same(file, newConfigs[1].UrlFile); Assert.Same(urlConfig3, newConfigs[2]); AssertNodesEqual(new ConfigNode("NODE"), newConfigs[2].Node); AssertNodesEqual(new TestConfigNode("NODE") { { "pqr", "stw" }, }, newConfigs[3].Node); Assert.Same(file, newConfigs[3].UrlFile); Received.InOrder(delegate { progress.ApplyingUpdate(urlConfig2, patch.UrlConfig); progress.ApplyingUpdate(urlConfig4, patch.UrlConfig); }); progress.DidNotReceiveWithAnyArgs().ApplyingCopy(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }
public BaseStepSelector(INodeMatcher selector) { this.Selector = selector; }
public HasChildSelector(INodeMatcher childSelector) { this._childSelector = childSelector; }
// consumes steps and whitespace from token stream until EOS or ',' is reached CssPathSelector ConsumePath() { List <StepSelector> steps = new List <StepSelector>(); bool bContinue = true; bool isImmediateChild = false; string siblingOp = null; StepSelector last = null; while (HasNext && bContinue) { var t = Peek; switch (t.Type) { case CssTokenType.Comma: bContinue = false; // TODO?? change the ',' to a terminator and consume it here instead of in the caller. break; case CssTokenType.Whitespace: ++_index; break; case CssTokenType.Relationship: if (last == null) { throw new Exception("Error starting selector path with relationship ( '>' '+' '~' ) operator."); } if (t.Text == ">") { isImmediateChild = true; // set flag so next selector added, adds its self to parent as immediate } else { siblingOp = t.Text; // set flag so when next node gets added, it can strip off the previous node and use as a sibling instead } ++_index; break; default: // add new step INodeMatcher ns = ConsumeStep(); var cur = new StepSelector(ns); steps.Add(cur); // check last/cur have a relationship if (isImmediateChild) { INodeMatcher pureParentSelector = last.Selector; // grab this before it gets soiled. last.AddChild(ns); // this part helps pair down the tree faster cur.AddParent(pureParentSelector); // this part is required to make it work isImmediateChild = false; } if (siblingOp != null) { steps.Remove(last); // remove last because it is not a parent cur.AddSibling(last.Selector, siblingOp == "~"); // it is a sibling siblingOp = null; } last = cur; break; } } return(new CssPathSelector(steps)); }
/// <summary> /// Use the given <see cref="INodeMatcher"/> when comparing. /// </summary> /// <param name="nodeMatcher">INodeMatcher to use</param> public CompareConstraint WithNodeMatcher(INodeMatcher nodeMatcher) { diffBuilder.WithNodeMatcher(nodeMatcher); return(this); }
public void TestApply__NameChanged() { UrlDir.UrlFile file = UrlBuilder.CreateFile("abc/def.cfg"); ConfigNode config = new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }; INodeMatcher nodeMatcher = Substitute.For <INodeMatcher>(); nodeMatcher.IsMatch(config).Returns(true); CopyPatch patch = new CopyPatch(UrlBuilder.CreateConfig("ghi/jkl", new TestConfigNode("@NODE") { { "@name", "001" }, { "@foo", "baz" }, { "pqr", "stw" }, }), nodeMatcher, Substitute.For <IPassSpecifier>()); IProtoUrlConfig protoConfig = Substitute.For <IProtoUrlConfig>(); protoConfig.Node.Returns(config); protoConfig.UrlFile.Returns(file); LinkedList <IProtoUrlConfig> configs = new LinkedList <IProtoUrlConfig>(); configs.AddLast(protoConfig); IPatchProgress progress = Substitute.For <IPatchProgress>(); IBasicLogger logger = Substitute.For <IBasicLogger>(); patch.Apply(configs, progress, logger); IProtoUrlConfig[] newConfigs = configs.ToArray(); Assert.Equal(2, newConfigs.Length); Assert.Same(protoConfig, newConfigs[0]); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "000" }, { "foo", "bar" }, }, newConfigs[0].Node); AssertNodesEqual(new TestConfigNode("NODE") { { "name", "001" }, { "foo", "baz" }, { "pqr", "stw" }, }, newConfigs[1].Node); Assert.Same(file, newConfigs[1].UrlFile); progress.Received().ApplyingCopy(protoConfig, patch.UrlConfig); progress.DidNotReceiveWithAnyArgs().ApplyingUpdate(null, null); progress.DidNotReceiveWithAnyArgs().ApplyingDelete(null, null); progress.DidNotReceiveWithAnyArgs().Error(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null); progress.DidNotReceiveWithAnyArgs().Exception(null, null, null); }