diff options
20 files changed, 534 insertions, 19 deletions
@@ -27,7 +27,7 @@ config_setting( # Android builds do not need to link in a separate pthread library. LINK_OPTS = select({ ":android": [], - "//conditions:default": ["-lpthread"], + "//conditions:default": ["-lpthread", "-lm"], }) load( diff --git a/Makefile.am b/Makefile.am index 1d1fb045..6b520b39 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,6 +83,7 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs \ csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs \ csharp/src/Google.Protobuf.Test/Compatibility/PropertyInfoExtensionsTest.cs \ + csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs \ csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs \ csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs \ csharp/src/Google.Protobuf.Test/EqualityTester.cs \ @@ -125,6 +126,7 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf/Collections/ReadOnlyDictionary.cs \ csharp/src/Google.Protobuf/Collections/RepeatedField.cs \ csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs \ + csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs \ csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs \ csharp/src/Google.Protobuf/FieldCodec.cs \ csharp/src/Google.Protobuf/FrameworkPortability.cs \ @@ -74,4 +74,4 @@ Usage The complete documentation for Protocol Buffers is available via the web at: - https://developers.google.com/protocol-buffers/ +https://developers.google.com/protocol-buffers/ diff --git a/csharp/README.md b/csharp/README.md index ed5c7be7..65d2311f 100644 --- a/csharp/README.md +++ b/csharp/README.md @@ -32,8 +32,7 @@ Building ======== Open the `src/Google.Protobuf.sln` solution in Visual Studio 2015 or -later. You should be able to run the NUnit test from Test Explorer -(you might need to install NUnit Visual Studio add-in). +later. Although *users* of this project are only expected to have Visual Studio 2012 or later, *developers* of the library are required to @@ -42,6 +41,57 @@ in its implementation. These features have no impact when using the compiled code - they're only relevant when building the `Google.Protobuf` assembly. +Testing +======= + +The unit tests use [NUnit 3](https://github.com/nunit/nunit). Vanilla NUnit doesn't +support .NET Core, so to run the tests you'll need to use +[dotnet-test-nunit](https://github.com/nunit/dotnet-test-nunit). +`dotnet-test-nunit` can also run tests for .NET 4.5+, so to run the tests +for both .NET Core and .NET 4.5, you can simply open the +`Package Manager Console` in Visual Studio and execute: +``` +dotnet test Google.Protobuf.Test +``` + +.NET 3.5 +======== + +We don't officially support .NET 3.5. However, there has been some effort +to make enabling .NET 3.5 support relatively painless in case you require it. +There's no guarantee that this will continue in the future, so rely on .NET +3.5 support at your peril. + +To enable .NET 3.5 support: + +1. Modify [src/Google.Protobuf/project.json](src/Google.Protobuf/project.json) to add `"net35": {}` to `"frameworks"`. +2. Modify [src/Google.Protobuf.Test/project.json](src/Google.Protobuf/project.json): + 1. Add `"net35": {}` to `"frameworks"`. + 2. `dotnet-test-nunit` doesn't support .NET 3.5, so remove it from + the project-wide `"dependencies"` and add it to the framework-specific + dependencies under `"net451"` and `"netcoreapp1.0"`. + +Note that `dotnet-test-nunit` doesn't support .NET 3.5. You can instead run the +tests with [NUnit 3 console](https://github.com/nunit/nunit-console) +by running something like: +``` +nunit3-console.exe "Google.Protobuf.Test\bin\Debug\net35\win7-x64\Google.Protobuf.Test.dll" --inprocess +``` + +The exact path may differ depending on your environment (e.g., the `win7-x64` +directory may be called something else). The `--inprocess` flag seems to be a +necessary workaround for a bug in NUnit; otherwise, you'll receive +an error "Exception has been thrown by the target of an invocation" +([possibly related issue](https://github.com/nunit/nunit/issues/1480)). + +If you still want to run the .NET 4.5 and .NET Core tests, you can do so by +specifying the framework when using `dotnet-test-nunit`, i.e. from +`Package Manager Console` in Visual Studio: +``` +dotnet test Google.Protobuf.Test --framework netcoreapp1.0 +dotnet test Google.Protobuf.Test --framework net451 +``` + History of C# protobufs ======================= diff --git a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs index 21aeb310..afdd491f 100644..100755 --- a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs +++ b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs @@ -34,7 +34,7 @@ using System; using System.Text;
using NUnit.Framework;
using System.IO;
-#if !DOTNET35
+#if !NET35
using System.Threading.Tasks;
#endif
@@ -196,7 +196,7 @@ namespace Google.Protobuf Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
}
-#if !DOTNET35
+#if !NET35
[Test]
public async Task FromStreamAsync_Seekable()
{
diff --git a/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs b/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs new file mode 100755 index 00000000..48c0725f --- /dev/null +++ b/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs @@ -0,0 +1,67 @@ +#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 + +#if NET35 +using System; +using System.IO; +using NUnit.Framework; +using Google.Protobuf.Compatibility; + +namespace Google.Protobuf.Test.Compatibility +{ + public class StreamExtensionsTest + { + [Test] + public void CopyToNullArgument() + { + var memoryStream = new MemoryStream(); + Assert.Throws<ArgumentNullException>(() => memoryStream.CopyTo(null)); + } + + [Test] + public void CopyToTest() + { + byte[] bytesToStream = new byte[] { 0x31, 0x08, 0xFF, 0x00 }; + Stream source = new MemoryStream(bytesToStream); + Stream destination = new MemoryStream((int)source.Length); + source.CopyTo(destination); + destination.Seek(0, SeekOrigin.Begin); + + Assert.AreEqual(0x31, destination.ReadByte()); + Assert.AreEqual(0x08, destination.ReadByte()); + Assert.AreEqual(0xFF, destination.ReadByte()); + Assert.AreEqual(0x00, destination.ReadByte()); + Assert.AreEqual(-1, destination.ReadByte()); + } + } +} +#endif diff --git a/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs b/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs index f430b06b..abbe3c95 100644..100755 --- a/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs +++ b/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs @@ -34,7 +34,7 @@ using System; using System.Collections.Generic; using System.Reflection; -#if !DOTNET35 +#if !NET35 namespace Google.Protobuf.Compatibility { public class TypeExtensionsTest diff --git a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs index 0e2bad59..77641163 100644..100755 --- a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs +++ b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs @@ -158,7 +158,9 @@ namespace Google.Protobuf { // WriteTagAndValue ignores default values var stream = new MemoryStream(); - var codedOutput = new CodedOutputStream(stream); + CodedOutputStream codedOutput;
+#if !NET35 + codedOutput = new CodedOutputStream(stream); codec.WriteTagAndValue(codedOutput, codec.DefaultValue); codedOutput.Flush(); Assert.AreEqual(0, stream.Position); @@ -167,6 +169,7 @@ namespace Google.Protobuf { Assert.AreEqual(default(T), codec.DefaultValue); } +#endif // The plain ValueWriter/ValueReader delegates don't. if (codec.DefaultValue != null) // This part isn't appropriate for message types. diff --git a/csharp/src/Google.Protobuf.Test/project.json b/csharp/src/Google.Protobuf.Test/project.json index 9f739f90..eaa7f79d 100644 --- a/csharp/src/Google.Protobuf.Test/project.json +++ b/csharp/src/Google.Protobuf.Test/project.json @@ -20,8 +20,8 @@ "dependencies": { "Google.Protobuf": { "target": "project" }, - "NUnit": "3.4.0", - "dotnet-test-nunit": "3.4.0-alpha-2" + "dotnet-test-nunit": "3.4.0-beta-3", + "NUnit": "3.6.0" }, "testRunner": "nunit", diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs index 9973d211..4abdb718 100644..100755 --- a/csharp/src/Google.Protobuf/ByteString.cs +++ b/csharp/src/Google.Protobuf/ByteString.cs @@ -35,10 +35,13 @@ using System.Collections; using System.Collections.Generic;
using System.IO;
using System.Text;
-#if !DOTNET35
+#if !NET35
using System.Threading;
using System.Threading.Tasks;
#endif
+#if NET35
+using Google.Protobuf.Compatibility;
+#endif
namespace Google.Protobuf
{
@@ -167,7 +170,7 @@ namespace Google.Protobuf return AttachBytes(bytes);
}
-#if !DOTNET35
+#if !NET35
/// <summary>
/// Constructs a <see cref="ByteString"/> from data in the given stream, asynchronously.
/// </summary>
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index 9504d7ef..6063ff61 100644..100755 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -47,7 +47,7 @@ namespace Google.Protobuf.Collections /// </remarks> /// <typeparam name="T">The element type of the repeated field.</typeparam> public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>> -#if !DOTNET35 +#if !NET35 , IReadOnlyList<T> #endif { diff --git a/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs index e3914dd3..95a02c72 100644..100755 --- a/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs +++ b/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs @@ -47,7 +47,7 @@ namespace Google.Protobuf.Compatibility /// </summary> internal static MethodInfo GetGetMethod(this PropertyInfo target) { -#if DOTNET35 +#if NET35 var method = target.GetGetMethod(); #else var method = target.GetMethod; @@ -61,7 +61,7 @@ namespace Google.Protobuf.Compatibility /// </summary> internal static MethodInfo GetSetMethod(this PropertyInfo target) { -#if DOTNET35 +#if NET35 var method = target.GetSetMethod(); #else var method = target.SetMethod; diff --git a/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs new file mode 100755 index 00000000..bf4bf220 --- /dev/null +++ b/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs @@ -0,0 +1,66 @@ +#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 + +#if NET35 +using System; +using System.IO; + +namespace Google.Protobuf.Compatibility +{ + /// <summary> + /// Extension methods for <see cref="Stream"/> in order to provide + /// backwards compatibility with .NET 3.5 + /// </summary> + public static class StreamExtensions + { + // 81920 seems to be the default buffer size used in .NET 4.5.1 + private const int BUFFER_SIZE = 81920; + + /// <summary> + /// Write the contents of the current stream to the destination stream + /// </summary> + public static void CopyTo(this Stream source, Stream destination) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + byte[] buffer = new byte[BUFFER_SIZE]; + int numBytesRead; + while ((numBytesRead = source.Read(buffer, 0, buffer.Length)) > 0) { + destination.Write(buffer, 0, numBytesRead); + } + } + } +} +#endif diff --git a/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs index 2d93183b..2f237138 100644..100755 --- a/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs +++ b/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs @@ -33,7 +33,7 @@ using System; using System.Reflection; -#if !DOTNET35 +#if !NET35 namespace Google.Protobuf.Compatibility { /// <summary> diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index bb1a361e..05282775 100644..100755 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -831,7 +831,7 @@ namespace Google.Protobuf return originalName; } -#if DOTNET35 +#if NET35 // TODO: Consider adding functionality to TypeExtensions to avoid this difference. private static Dictionary<object, string> GetNameMapping(System.Type enumType) => enumType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index 4a0922e8..86942acc 100644..100755 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -34,7 +34,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -#if DOTNET35 +#if NET35 // Needed for ReadOnlyDictionary, which does not exist in .NET 3.5 using Google.Protobuf.Collections; #endif diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs index 0685c21a..4b0670f6 100644..100755 --- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs @@ -59,7 +59,7 @@ namespace Google.Protobuf.WellKnownTypes if (firstInvalid == null) { var writer = new StringWriter(); -#if DOTNET35 +#if NET35 var query = paths.Select(JsonFormatter.ToJsonName); JsonFormatter.WriteString(writer, string.Join(",", query.ToArray())); #else diff --git a/objectivec/GPBUtilities.h b/objectivec/GPBUtilities.h index 52e7d2e0..5464dfb3 100644 --- a/objectivec/GPBUtilities.h +++ b/objectivec/GPBUtilities.h @@ -392,6 +392,11 @@ void GPBSetMessageMapField(GPBMessage *self, **/ NSData *GPBEmptyNSData(void) __attribute__((pure)); +/** + * Drops the `unknownFields` from the given message and from all sub message. + **/ +void GPBMessageDropUnknownFieldsRecursively(GPBMessage *message); + NS_ASSUME_NONNULL_END CF_EXTERN_C_END diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m index 68aadb77..1843478c 100644 --- a/objectivec/GPBUtilities.m +++ b/objectivec/GPBUtilities.m @@ -58,6 +58,125 @@ NSData *GPBEmptyNSData(void) { return defaultNSData; } +void GPBMessageDropUnknownFieldsRecursively(GPBMessage *initialMessage) { + if (!initialMessage) { + return; + } + + // Use an array as a list to process to avoid recursion. + NSMutableArray *todo = [NSMutableArray arrayWithObject:initialMessage]; + + while (todo.count) { + GPBMessage *msg = todo.lastObject; + [todo removeLastObject]; + + // Clear unknowns. + msg.unknownFields = nil; + + // Handle the message fields. + GPBDescriptor *descriptor = [[msg class] descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (!GPBFieldDataTypeIsMessage(field)) { + continue; + } + switch (field.fieldType) { + case GPBFieldTypeSingle: + if (GPBGetHasIvarField(msg, field)) { + GPBMessage *fieldMessage = GPBGetObjectIvarWithFieldNoAutocreate(msg, field); + [todo addObject:fieldMessage]; + } + break; + + case GPBFieldTypeRepeated: { + NSArray *fieldMessages = GPBGetObjectIvarWithFieldNoAutocreate(msg, field); + if (fieldMessages.count) { + [todo addObjectsFromArray:fieldMessages]; + } + break; + } + + case GPBFieldTypeMap: { + id rawFieldMap = GPBGetObjectIvarWithFieldNoAutocreate(msg, field); + switch (field.mapKeyDataType) { + case GPBDataTypeBool: + [(GPBBoolObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + BOOL key, id _Nonnull object, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:object]; + }]; + break; + case GPBDataTypeFixed32: + case GPBDataTypeUInt32: + [(GPBUInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + uint32_t key, id _Nonnull object, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:object]; + }]; + break; + case GPBDataTypeInt32: + case GPBDataTypeSFixed32: + case GPBDataTypeSInt32: + [(GPBInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + int32_t key, id _Nonnull object, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:object]; + }]; + break; + case GPBDataTypeFixed64: + case GPBDataTypeUInt64: + [(GPBUInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + uint64_t key, id _Nonnull object, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:object]; + }]; + break; + case GPBDataTypeInt64: + case GPBDataTypeSFixed64: + case GPBDataTypeSInt64: + [(GPBInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + int64_t key, id _Nonnull object, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:object]; + }]; + break; + case GPBDataTypeString: + [(NSDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^( + NSString * _Nonnull key, GPBMessage * _Nonnull obj, BOOL * _Nonnull stop) { + #pragma unused(key, stop) + [todo addObject:obj]; + }]; + break; + case GPBDataTypeFloat: + case GPBDataTypeDouble: + case GPBDataTypeEnum: + case GPBDataTypeBytes: + case GPBDataTypeGroup: + case GPBDataTypeMessage: + NSCAssert(NO, @"Aren't valid key types."); + } + break; + } // switch(field.mapKeyDataType) + } // switch(field.fieldType) + } // for(fields) + + // Handle any extensions holding messages. + for (GPBExtensionDescriptor *extension in [msg extensionsCurrentlySet]) { + if (!GPBDataTypeIsMessage(extension.dataType)) { + continue; + } + if (extension.isRepeated) { + NSArray *extMessages = [msg getExtension:extension]; + [todo addObjectsFromArray:extMessages]; + } else { + GPBMessage *extMessage = [msg getExtension:extension]; + [todo addObject:extMessage]; + } + } // for(extensionsCurrentlySet) + + } // while(todo.count) +} + + // -- About Version Checks -- // There's actually 3 places these checks all come into play: // 1. When the generated source is compile into .o files, the header check diff --git a/objectivec/Tests/GPBUtilitiesTests.m b/objectivec/Tests/GPBUtilitiesTests.m index dfaca660..2e206a54 100644 --- a/objectivec/Tests/GPBUtilitiesTests.m +++ b/objectivec/Tests/GPBUtilitiesTests.m @@ -39,6 +39,7 @@ #import "GPBDescriptor.h" #import "GPBDescriptor_PackagePrivate.h" #import "GPBMessage.h" +#import "GPBUnknownField_PackagePrivate.h" #import "google/protobuf/MapUnittest.pbobjc.h" #import "google/protobuf/Unittest.pbobjc.h" @@ -197,4 +198,203 @@ } } +// Helper to make an unknown field set with something in it. +static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) { + GPBUnknownFieldSet *result = + [[[GPBUnknownFieldSet alloc] init] autorelease]; + + GPBUnknownField *field = + [[[GPBUnknownField alloc] initWithNumber:num] autorelease]; + [field addVarint:num]; + [result addField:field]; + + return result; +} + +- (void)testDropMessageUnknownFieldsRecursively { + TestAllExtensions *message = [TestAllExtensions message]; + + // Give it unknownFields. + message.unknownFields = UnknownFieldsSetHelper(777); + + // Given it extensions that include a message with unknown fields of its own. + { + // Int + [message setExtension:[UnittestRoot optionalInt32Extension] value:@5]; + + // Group + OptionalGroup_extension *optionalGroup = [OptionalGroup_extension message]; + optionalGroup.a = 123; + optionalGroup.unknownFields = UnknownFieldsSetHelper(779); + [message setExtension:[UnittestRoot optionalGroupExtension] + value:optionalGroup]; + + // Message + TestAllTypes_NestedMessage *nestedMessage = + [TestAllTypes_NestedMessage message]; + nestedMessage.bb = 456; + nestedMessage.unknownFields = UnknownFieldsSetHelper(778); + [message setExtension:[UnittestRoot optionalNestedMessageExtension] + value:nestedMessage]; + + // Repeated Group + RepeatedGroup_extension *repeatedGroup = + [[RepeatedGroup_extension alloc] init]; + repeatedGroup.a = 567; + repeatedGroup.unknownFields = UnknownFieldsSetHelper(780); + [message addExtension:[UnittestRoot repeatedGroupExtension] + value:repeatedGroup]; + [repeatedGroup release]; + + // Repeated Message + nestedMessage = [[TestAllTypes_NestedMessage alloc] init]; + nestedMessage.bb = 678; + nestedMessage.unknownFields = UnknownFieldsSetHelper(781); + [message addExtension:[UnittestRoot repeatedNestedMessageExtension] + value:nestedMessage]; + [nestedMessage release]; + } + + // Confirm everything is there. + + XCTAssertNotNil(message); + XCTAssertNotNil(message.unknownFields); + XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]); + + { + XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]); + OptionalGroup_extension *optionalGroup = + [message getExtension:[UnittestRoot optionalGroupExtension]]; + XCTAssertNotNil(optionalGroup); + XCTAssertEqual(optionalGroup.a, 123); + XCTAssertNotNil(optionalGroup.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]); + TestAllTypes_NestedMessage *nestedMessage = + [message getExtension:[UnittestRoot optionalNestedMessageExtension]]; + XCTAssertNotNil(nestedMessage); + XCTAssertEqual(nestedMessage.bb, 456); + XCTAssertNotNil(nestedMessage.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]); + NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]]; + XCTAssertEqual(repeatedGroups.count, (NSUInteger)1); + RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject; + XCTAssertNotNil(repeatedGroup); + XCTAssertEqual(repeatedGroup.a, 567); + XCTAssertNotNil(repeatedGroup.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]); + NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]]; + XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1); + TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject; + XCTAssertNotNil(repeatedNestedMessage); + XCTAssertEqual(repeatedNestedMessage.bb, 678); + XCTAssertNotNil(repeatedNestedMessage.unknownFields); + } + + // Drop them. + GPBMessageDropUnknownFieldsRecursively(message); + + // Confirm unknowns are gone from within the messages. + + XCTAssertNotNil(message); + XCTAssertNil(message.unknownFields); + XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]); + + { + XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]); + OptionalGroup_extension *optionalGroup = + [message getExtension:[UnittestRoot optionalGroupExtension]]; + XCTAssertNotNil(optionalGroup); + XCTAssertEqual(optionalGroup.a, 123); + XCTAssertNil(optionalGroup.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]); + TestAllTypes_NestedMessage *nestedMessage = + [message getExtension:[UnittestRoot optionalNestedMessageExtension]]; + XCTAssertNotNil(nestedMessage); + XCTAssertEqual(nestedMessage.bb, 456); + XCTAssertNil(nestedMessage.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]); + NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]]; + XCTAssertEqual(repeatedGroups.count, (NSUInteger)1); + RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject; + XCTAssertNotNil(repeatedGroup); + XCTAssertEqual(repeatedGroup.a, 567); + XCTAssertNil(repeatedGroup.unknownFields); + } + + { + XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]); + NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]]; + XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1); + TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject; + XCTAssertNotNil(repeatedNestedMessage); + XCTAssertEqual(repeatedNestedMessage.bb, 678); + XCTAssertNil(repeatedNestedMessage.unknownFields); + } + +} + +- (void)testDropMessageUnknownFieldsRecursively_Maps { + TestMap *message = [TestMap message]; + + { + ForeignMessage *foreignMessage = [ForeignMessage message]; + foreignMessage.unknownFields = UnknownFieldsSetHelper(100); + [message.mapInt32ForeignMessage setObject:foreignMessage forKey:100]; + + foreignMessage = [ForeignMessage message]; + foreignMessage.unknownFields = UnknownFieldsSetHelper(101); + [message.mapStringForeignMessage setObject:foreignMessage forKey:@"101"]; + } + + // Confirm everything is there. + + XCTAssertNotNil(message); + + { + ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100]; + XCTAssertNotNil(foreignMessage); + XCTAssertNotNil(foreignMessage.unknownFields); + } + + { + ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"]; + XCTAssertNotNil(foreignMessage); + XCTAssertNotNil(foreignMessage.unknownFields); + } + + GPBMessageDropUnknownFieldsRecursively(message); + + // Confirm unknowns are gone from within the messages. + + XCTAssertNotNil(message); + + { + ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100]; + XCTAssertNotNil(foreignMessage); + XCTAssertNil(foreignMessage.unknownFields); + } + + { + ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"]; + XCTAssertNotNil(foreignMessage); + XCTAssertNil(foreignMessage.unknownFields); + } + +} + @end |