Commit 80e530da authored by Benjamin Krämer's avatar Benjamin Krämer Committed by Jie Luo

Ported FieldMaskUtil from Java to C# (#5045)

* Ported FieldMaskUtil from Java to C#

* Merged FieldMaskUtil into FieldMaskPartial

- Removed FieldMaskUtil
- Moved FieldMaskTree to root
- Updated tests

* Improved tests

- Removed internal method FieldMaskTree.GetFieldPaths
- Proof FieldMask.Paths only contains expected values

* Added FieldMaskTreeTest to Makefile

* Added FieldMaskTree to Makefile
parent 6a51c038
......@@ -93,6 +93,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs \
csharp/src/Google.Protobuf.Test/EqualityTester.cs \
csharp/src/Google.Protobuf.Test/FieldCodecTest.cs \
csharp/src/Google.Protobuf.Test/FieldMaskTreeTest.cs \
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs \
csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj \
csharp/src/Google.Protobuf.Test/IssuesTest.cs \
......@@ -140,6 +141,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs \
csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs \
csharp/src/Google.Protobuf/FieldCodec.cs \
csharp/src/Google.Protobuf/FieldMaskTree.cs \
csharp/src/Google.Protobuf/FrameworkPortability.cs \
csharp/src/Google.Protobuf/Google.Protobuf.csproj \
csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs \
......
This diff is collapsed.
......@@ -30,7 +30,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
namespace Google.Protobuf.WellKnownTypes
......@@ -58,5 +59,187 @@ namespace Google.Protobuf.WellKnownTypes
"{ \"@warning\": \"Invalid FieldMask\", \"paths\": [ \"x\", \"foo__bar\", \"x\\\\y\" ] }",
mask.ToString());
}
[Test]
public void IsValid()
{
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload"));
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("nonexist"));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_int32"));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_int32"));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_nested_message"));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_nested_message"));
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.nonexist"));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("payload")));
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("nonexist")));
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("payload,nonexist")));
Assert.IsTrue(FieldMask.IsValid(NestedTestAllTypes.Descriptor, "payload"));
Assert.IsFalse(FieldMask.IsValid(NestedTestAllTypes.Descriptor, "nonexist"));
Assert.IsTrue(FieldMask.IsValid(NestedTestAllTypes.Descriptor, FieldMask.FromString("payload")));
Assert.IsFalse(FieldMask.IsValid(NestedTestAllTypes.Descriptor, FieldMask.FromString("nonexist")));
Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_nested_message.bb"));
// Repeated fields cannot have sub-paths.
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_nested_message.bb"));
// Non-message fields cannot have sub-paths.
Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.single_int32.bb"));
}
[Test]
[TestCase(new string[] { }, "\"\"")]
[TestCase(new string[] { "foo" }, "\"foo\"")]
[TestCase(new string[] { "foo", "bar" }, "\"foo,bar\"")]
[TestCase(new string[] { "", "foo", "", "bar", "" }, "\",foo,,bar,\"")]
public void ToString(string[] input, string expectedOutput)
{
FieldMask mask = new FieldMask();
mask.Paths.AddRange(input);
Assert.AreEqual(expectedOutput, mask.ToString());
}
[Test]
[TestCase("", new string[] { })]
[TestCase("foo", new string[] { "foo" })]
[TestCase("foo,bar.baz", new string[] { "foo", "bar.baz" })]
[TestCase(",foo,,bar,", new string[] { "foo", "bar" })]
public void FromString(string input, string[] expectedOutput)
{
FieldMask mask = FieldMask.FromString(input);
Assert.AreEqual(expectedOutput.Length, mask.Paths.Count);
for (int i = 0; i < expectedOutput.Length; i++)
{
Assert.AreEqual(expectedOutput[i], mask.Paths[i]);
}
}
[Test]
public void FromString_Validated()
{
// Check whether the field paths are valid if a class parameter is provided.
Assert.DoesNotThrow(() => FieldMask.FromString<NestedTestAllTypes>(",payload"));
Assert.Throws<InvalidProtocolBufferException>(() => FieldMask.FromString<NestedTestAllTypes>("payload,nonexist"));
}
[Test]
[TestCase(new int[] { }, new string[] { })]
[TestCase(new int[] { TestAllTypes.SingleInt32FieldNumber }, new string[] { "single_int32" })]
[TestCase(new int[] { TestAllTypes.SingleInt32FieldNumber, TestAllTypes.SingleInt64FieldNumber }, new string[] { "single_int32", "single_int64" })]
public void FromFieldNumbers(int[] input, string[] expectedOutput)
{
FieldMask mask = FieldMask.FromFieldNumbers<TestAllTypes>(input);
Assert.AreEqual(expectedOutput.Length, mask.Paths.Count);
for (int i = 0; i < expectedOutput.Length; i++)
{
Assert.AreEqual(expectedOutput[i], mask.Paths[i]);
}
}
[Test]
public void FromFieldNumbers_Invalid()
{
Assert.Throws<ArgumentNullException>(() =>
{
int invalidFieldNumber = 1000;
FieldMask.FromFieldNumbers<TestAllTypes>(invalidFieldNumber);
});
}
[Test]
[TestCase(new string[] { }, "\"\"")]
[TestCase(new string[] { "foo" }, "\"foo\"")]
[TestCase(new string[] { "foo", "bar" }, "\"foo,bar\"")]
[TestCase(new string[] { "", "foo", "", "bar", "" }, "\",foo,bar\"")]
public void Normalize(string[] input, string expectedOutput)
{
FieldMask mask = new FieldMask();
mask.Paths.AddRange(input);
FieldMask result = mask.Normalize();
Assert.AreEqual(expectedOutput, result.ToString());
}
[Test]
public void Union()
{
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#AddFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMask.FromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMask.FromString("foo.bar,bar");
FieldMask result = mask1.Union(mask2);
Assert.AreEqual(2, result.Paths.Count);
Assert.Contains("bar", result.Paths);
Assert.Contains("foo", result.Paths);
Assert.That(result.Paths, Has.No.Member("bar.baz"));
Assert.That(result.Paths, Has.No.Member("bar.quz"));
Assert.That(result.Paths, Has.No.Member("foo.bar"));
}
[Test]
public void Union_UsingVarArgs()
{
FieldMask mask1 = FieldMask.FromString("foo");
FieldMask mask2 = FieldMask.FromString("foo.bar,bar.quz");
FieldMask mask3 = FieldMask.FromString("bar.quz");
FieldMask mask4 = FieldMask.FromString("bar");
FieldMask result = mask1.Union(mask2, mask3, mask4);
Assert.AreEqual(2, result.Paths.Count);
Assert.Contains("bar", result.Paths);
Assert.Contains("foo", result.Paths);
Assert.That(result.Paths, Has.No.Member("foo.bar"));
Assert.That(result.Paths, Has.No.Member("bar.quz"));
}
[Test]
public void Intersection()
{
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#IntersectFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMask.FromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMask.FromString("foo.bar,bar");
FieldMask result = mask1.Intersection(mask2);
Assert.AreEqual(3, result.Paths.Count);
Assert.Contains("foo.bar", result.Paths);
Assert.Contains("bar.baz", result.Paths);
Assert.Contains("bar.quz", result.Paths);
Assert.That(result.Paths, Has.No.Member("foo"));
Assert.That(result.Paths, Has.No.Member("bar"));
}
[Test]
public void Merge()
{
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#Merge} to cover all scenarios.
FieldMask fieldMask = FieldMask.FromString("payload");
NestedTestAllTypes source = new NestedTestAllTypes
{
Payload = new TestAllTypes
{
SingleInt32 = 1234,
SingleFixed64 = 4321
}
};
NestedTestAllTypes destination = new NestedTestAllTypes();
fieldMask.Merge(source, destination);
Assert.AreEqual(1234, destination.Payload.SingleInt32);
Assert.AreEqual(4321, destination.Payload.SingleFixed64);
destination = new NestedTestAllTypes
{
Payload = new TestAllTypes
{
SingleInt32 = 4321,
SingleInt64 = 5678
}
};
fieldMask.Merge(source, destination);
Assert.AreEqual(1234, destination.Payload.SingleInt32);
Assert.AreEqual(5678, destination.Payload.SingleInt64);
Assert.AreEqual(4321, destination.Payload.SingleFixed64);
}
}
}
This diff is collapsed.
......@@ -271,7 +271,25 @@ namespace Google.Protobuf
}
return result.ToString();
}
internal static string FromJsonName(string name)
{
StringBuilder result = new StringBuilder(name.Length);
foreach (char ch in name)
{
if (char.IsUpper(ch))
{
result.Append('_');
result.Append(char.ToLowerInvariant(ch));
}
else
{
result.Append(ch);
}
}
return result.ToString();
}
private static void WriteNull(TextWriter writer)
{
writer.Write("null");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment