123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- #region Copyright notice and license
- // Protocol Buffers - Google's data interchange format
- // Copyright 2015 Google Inc. All rights reserved.
- // https://developers.google.com/protocol-buffers/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #endregion
- using System.Collections.Generic;
- using Google.Protobuf.Collections;
- using Google.Protobuf.TestProtos;
- using NUnit.Framework;
- using Google.Protobuf.WellKnownTypes;
- namespace Google.Protobuf
- {
- public class FieldMaskTreeTest
- {
- [Test]
- public void AddFieldPath()
- {
- FieldMaskTree tree = new FieldMaskTree();
- RepeatedField<string> paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(0, paths.Count);
- tree.AddFieldPath("");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(1, paths.Count);
- Assert.Contains("", paths);
- // New branch.
- tree.AddFieldPath("foo");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(2, paths.Count);
- Assert.Contains("foo", paths);
- // Redundant path.
- tree.AddFieldPath("foo");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(2, paths.Count);
- // New branch.
- tree.AddFieldPath("bar.baz");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(3, paths.Count);
- Assert.Contains("bar.baz", paths);
- // Redundant sub-path.
- tree.AddFieldPath("foo.bar");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(3, paths.Count);
- // New branch from a non-root node.
- tree.AddFieldPath("bar.quz");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(4, paths.Count);
- Assert.Contains("bar.quz", paths);
- // A path that matches several existing sub-paths.
- tree.AddFieldPath("bar");
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(3, paths.Count);
- Assert.Contains("foo", paths);
- Assert.Contains("bar", paths);
- }
- [Test]
- public void MergeFromFieldMask()
- {
- FieldMaskTree tree = new FieldMaskTree();
- tree.MergeFromFieldMask(new FieldMask
- {
- Paths = {"foo", "bar.baz", "bar.quz"}
- });
- RepeatedField<string> paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(3, paths.Count);
- Assert.Contains("foo", paths);
- Assert.Contains("bar.baz", paths);
- Assert.Contains("bar.quz", paths);
- tree.MergeFromFieldMask(new FieldMask
- {
- Paths = {"foo.bar", "bar"}
- });
- paths = tree.ToFieldMask().Paths;
- Assert.AreEqual(2, paths.Count);
- Assert.Contains("foo", paths);
- Assert.Contains("bar", paths);
- }
- [Test]
- public void IntersectFieldPath()
- {
- FieldMaskTree tree = new FieldMaskTree();
- FieldMaskTree result = new FieldMaskTree();
- tree.MergeFromFieldMask(new FieldMask
- {
- Paths = {"foo", "bar.baz", "bar.quz"}
- });
- // Empty path.
- tree.IntersectFieldPath("", result);
- RepeatedField<string> paths = result.ToFieldMask().Paths;
- Assert.AreEqual(0, paths.Count);
- // Non-exist path.
- tree.IntersectFieldPath("quz", result);
- paths = result.ToFieldMask().Paths;
- Assert.AreEqual(0, paths.Count);
- // Sub-path of an existing leaf.
- tree.IntersectFieldPath("foo.bar", result);
- paths = result.ToFieldMask().Paths;
- Assert.AreEqual(1, paths.Count);
- Assert.Contains("foo.bar", paths);
- // Match an existing leaf node.
- tree.IntersectFieldPath("foo", result);
- paths = result.ToFieldMask().Paths;
- Assert.AreEqual(1, paths.Count);
- Assert.Contains("foo", paths);
- // Non-exist path.
- tree.IntersectFieldPath("bar.foo", result);
- paths = result.ToFieldMask().Paths;
- Assert.AreEqual(1, paths.Count);
- Assert.Contains("foo", paths);
- // Match a non-leaf node.
- tree.IntersectFieldPath("bar", result);
- paths = result.ToFieldMask().Paths;
- Assert.AreEqual(3, paths.Count);
- Assert.Contains("foo", paths);
- Assert.Contains("bar.baz", paths);
- Assert.Contains("bar.quz", paths);
- }
- private void Merge(FieldMaskTree tree, IMessage source, IMessage destination, FieldMask.MergeOptions options, bool useDynamicMessage)
- {
- if (useDynamicMessage)
- {
- var newSource = source.Descriptor.Parser.CreateTemplate();
- newSource.MergeFrom(source.ToByteString());
- var newDestination = source.Descriptor.Parser.CreateTemplate();
- newDestination.MergeFrom(destination.ToByteString());
- tree.Merge(newSource, newDestination, options);
- // Clear before merging:
- foreach (var fieldDescriptor in destination.Descriptor.Fields.InFieldNumberOrder())
- {
- fieldDescriptor.Accessor.Clear(destination);
- }
- destination.MergeFrom(newDestination.ToByteString());
- }
- else
- {
- tree.Merge(source, destination, options);
- }
- }
- [Test]
- [TestCase(true)]
- [TestCase(false)]
- public void Merge(bool useDynamicMessage)
- {
- TestAllTypes value = new TestAllTypes
- {
- SingleInt32 = 1234,
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage {Bb = 5678},
- RepeatedInt32 = {4321},
- RepeatedNestedMessage = {new TestAllTypes.Types.NestedMessage {Bb = 8765}}
- };
- NestedTestAllTypes source = new NestedTestAllTypes
- {
- Payload = value,
- Child = new NestedTestAllTypes {Payload = value}
- };
- // Now we have a message source with the following structure:
- // [root] -+- payload -+- single_int32
- // | +- single_nested_message
- // | +- repeated_int32
- // | +- repeated_nested_message
- // |
- // +- child --- payload -+- single_int32
- // +- single_nested_message
- // +- repeated_int32
- // +- repeated_nested_message
- FieldMask.MergeOptions options = new FieldMask.MergeOptions();
- // Test merging each individual field.
- NestedTestAllTypes destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
- source, destination, options, useDynamicMessage);
- NestedTestAllTypes expected = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleInt32 = 1234
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload.single_nested_message"),
- source, destination, options, useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage {Bb = 5678}
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
- source, destination, options, useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- RepeatedInt32 = {4321}
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload.repeated_nested_message"),
- source, destination, options, useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- RepeatedNestedMessage = {new TestAllTypes.Types.NestedMessage {Bb = 8765}}
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(
- new FieldMaskTree().AddFieldPath("child.payload.single_int32"),
- source,
- destination,
- options,
- useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Child = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleInt32 = 1234
- }
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(
- new FieldMaskTree().AddFieldPath("child.payload.single_nested_message"),
- source,
- destination,
- options,
- useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Child = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage {Bb = 5678}
- }
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("child.payload.repeated_int32"),
- source, destination, options, useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Child = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- RepeatedInt32 = {4321}
- }
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("child.payload.repeated_nested_message"),
- source, destination, options, useDynamicMessage);
- expected = new NestedTestAllTypes
- {
- Child = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- RepeatedNestedMessage = {new TestAllTypes.Types.NestedMessage {Bb = 8765}}
- }
- }
- };
- Assert.AreEqual(expected, destination);
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("child").AddFieldPath("payload"),
- source, destination, options, useDynamicMessage);
- Assert.AreEqual(source, destination);
- // Test repeated options.
- destination = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- RepeatedInt32 = { 1000 }
- }
- };
- Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
- source, destination, options, useDynamicMessage);
- // Default behavior is to append repeated fields.
- Assert.AreEqual(2, destination.Payload.RepeatedInt32.Count);
- Assert.AreEqual(1000, destination.Payload.RepeatedInt32[0]);
- Assert.AreEqual(4321, destination.Payload.RepeatedInt32[1]);
- // Change to replace repeated fields.
- options.ReplaceRepeatedFields = true;
- Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
- source, destination, options, useDynamicMessage);
- Assert.AreEqual(1, destination.Payload.RepeatedInt32.Count);
- Assert.AreEqual(4321, destination.Payload.RepeatedInt32[0]);
- // Test message options.
- destination = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleInt32 = 1000,
- SingleUint32 = 2000
- }
- };
- Merge(new FieldMaskTree().AddFieldPath("payload"),
- source, destination, options, useDynamicMessage);
- // Default behavior is to merge message fields.
- Assert.AreEqual(1234, destination.Payload.SingleInt32);
- Assert.AreEqual(2000, destination.Payload.SingleUint32);
- // Test merging unset message fields.
- NestedTestAllTypes clearedSource = source.Clone();
- clearedSource.Payload = null;
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload"),
- clearedSource, destination, options, useDynamicMessage);
- Assert.IsNull(destination.Payload);
- // Skip a message field if they are unset in both source and target.
- destination = new NestedTestAllTypes();
- Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
- clearedSource, destination, options, useDynamicMessage);
- Assert.IsNull(destination.Payload);
- // Change to replace message fields.
- options.ReplaceMessageFields = true;
- destination = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleInt32 = 1000,
- SingleUint32 = 2000
- }
- };
- Merge(new FieldMaskTree().AddFieldPath("payload"),
- source, destination, options, useDynamicMessage);
- Assert.AreEqual(1234, destination.Payload.SingleInt32);
- Assert.AreEqual(0, destination.Payload.SingleUint32);
- // Test merging unset message fields.
- destination = new NestedTestAllTypes
- {
- Payload = new TestAllTypes
- {
- SingleInt32 = 1000,
- SingleUint32 = 2000
- }
- };
- Merge(new FieldMaskTree().AddFieldPath("payload"),
- clearedSource, destination, options, useDynamicMessage);
- Assert.IsNull(destination.Payload);
- // Test merging unset primitive fields.
- destination = source.Clone();
- destination.Payload.SingleInt32 = 0;
- NestedTestAllTypes sourceWithPayloadInt32Unset = destination;
- destination = source.Clone();
- Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
- sourceWithPayloadInt32Unset, destination, options, useDynamicMessage);
- Assert.AreEqual(0, destination.Payload.SingleInt32);
- // Change to clear unset primitive fields.
- options.ReplacePrimitiveFields = true;
- destination = source.Clone();
- Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
- sourceWithPayloadInt32Unset, destination, options, useDynamicMessage);
- Assert.IsNotNull(destination.Payload);
- }
- }
- }
|