diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index 497e811c8b..41dc715db2 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -13,4 +13,4 @@ jobs:
with:
check_filenames: true
skip: ./.git,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml
- ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,sur"
+ ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur"
diff --git a/.gitignore b/.gitignore
index 4fe3edde37..8b764964d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,7 +46,7 @@ src/.libs
any_test.pb.*
map*unittest.pb.*
unittest*.pb.*
-cpp_test*.pb.*
+src/google/protobuf/compiler/cpp/test*.pb.*
src/google/protobuf/util/**/*.pb.cc
src/google/protobuf/util/**/*.pb.h
@@ -89,6 +89,10 @@ java/**/*.iml
# Windows native output.
cmake/build
build_msvc
+# Directories suggested by cmake/README.md
+/debug/
+/solution/
+/release/
# NuGet packages: we want the repository configuration, but not the
# packages themselves.
diff --git a/BUILD b/BUILD
index 887e5e3845..5de4aa3f2f 100644
--- a/BUILD
+++ b/BUILD
@@ -159,6 +159,7 @@ cc_library(
"src/google/protobuf/any_lite.cc",
"src/google/protobuf/arena.cc",
"src/google/protobuf/arenastring.cc",
+ "src/google/protobuf/arenaz_sampler.cc",
"src/google/protobuf/extension_set.cc",
"src/google/protobuf/generated_enum_util.cc",
"src/google/protobuf/generated_message_tctable_lite.cc",
@@ -419,21 +420,21 @@ cc_library(
# AUTOGEN(protoc_lib_srcs)
"src/google/protobuf/compiler/code_generator.cc",
"src/google/protobuf/compiler/command_line_interface.cc",
- "src/google/protobuf/compiler/cpp/cpp_enum.cc",
- "src/google/protobuf/compiler/cpp/cpp_enum_field.cc",
- "src/google/protobuf/compiler/cpp/cpp_extension.cc",
- "src/google/protobuf/compiler/cpp/cpp_field.cc",
- "src/google/protobuf/compiler/cpp/cpp_file.cc",
- "src/google/protobuf/compiler/cpp/cpp_generator.cc",
- "src/google/protobuf/compiler/cpp/cpp_helpers.cc",
- "src/google/protobuf/compiler/cpp/cpp_map_field.cc",
- "src/google/protobuf/compiler/cpp/cpp_message.cc",
- "src/google/protobuf/compiler/cpp/cpp_message_field.cc",
- "src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
- "src/google/protobuf/compiler/cpp/cpp_parse_function_generator.cc",
- "src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
- "src/google/protobuf/compiler/cpp/cpp_service.cc",
- "src/google/protobuf/compiler/cpp/cpp_string_field.cc",
+ "src/google/protobuf/compiler/cpp/enum.cc",
+ "src/google/protobuf/compiler/cpp/enum_field.cc",
+ "src/google/protobuf/compiler/cpp/extension.cc",
+ "src/google/protobuf/compiler/cpp/field.cc",
+ "src/google/protobuf/compiler/cpp/file.cc",
+ "src/google/protobuf/compiler/cpp/generator.cc",
+ "src/google/protobuf/compiler/cpp/helpers.cc",
+ "src/google/protobuf/compiler/cpp/map_field.cc",
+ "src/google/protobuf/compiler/cpp/message.cc",
+ "src/google/protobuf/compiler/cpp/message_field.cc",
+ "src/google/protobuf/compiler/cpp/padding_optimizer.cc",
+ "src/google/protobuf/compiler/cpp/parse_function_generator.cc",
+ "src/google/protobuf/compiler/cpp/primitive_field.cc",
+ "src/google/protobuf/compiler/cpp/service.cc",
+ "src/google/protobuf/compiler/cpp/string_field.cc",
"src/google/protobuf/compiler/csharp/csharp_doc_comment.cc",
"src/google/protobuf/compiler/csharp/csharp_enum.cc",
"src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
@@ -450,35 +451,35 @@ cc_library(
"src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
"src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
"src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
- "src/google/protobuf/compiler/java/java_context.cc",
- "src/google/protobuf/compiler/java/java_doc_comment.cc",
- "src/google/protobuf/compiler/java/java_enum.cc",
- "src/google/protobuf/compiler/java/java_enum_field.cc",
- "src/google/protobuf/compiler/java/java_enum_field_lite.cc",
- "src/google/protobuf/compiler/java/java_enum_lite.cc",
- "src/google/protobuf/compiler/java/java_extension.cc",
- "src/google/protobuf/compiler/java/java_extension_lite.cc",
- "src/google/protobuf/compiler/java/java_field.cc",
- "src/google/protobuf/compiler/java/java_file.cc",
- "src/google/protobuf/compiler/java/java_generator.cc",
- "src/google/protobuf/compiler/java/java_generator_factory.cc",
- "src/google/protobuf/compiler/java/java_helpers.cc",
- "src/google/protobuf/compiler/java/java_kotlin_generator.cc",
- "src/google/protobuf/compiler/java/java_map_field.cc",
- "src/google/protobuf/compiler/java/java_map_field_lite.cc",
- "src/google/protobuf/compiler/java/java_message.cc",
- "src/google/protobuf/compiler/java/java_message_builder.cc",
- "src/google/protobuf/compiler/java/java_message_builder_lite.cc",
- "src/google/protobuf/compiler/java/java_message_field.cc",
- "src/google/protobuf/compiler/java/java_message_field_lite.cc",
- "src/google/protobuf/compiler/java/java_message_lite.cc",
- "src/google/protobuf/compiler/java/java_name_resolver.cc",
- "src/google/protobuf/compiler/java/java_primitive_field.cc",
- "src/google/protobuf/compiler/java/java_primitive_field_lite.cc",
- "src/google/protobuf/compiler/java/java_service.cc",
- "src/google/protobuf/compiler/java/java_shared_code_generator.cc",
- "src/google/protobuf/compiler/java/java_string_field.cc",
- "src/google/protobuf/compiler/java/java_string_field_lite.cc",
+ "src/google/protobuf/compiler/java/context.cc",
+ "src/google/protobuf/compiler/java/doc_comment.cc",
+ "src/google/protobuf/compiler/java/enum.cc",
+ "src/google/protobuf/compiler/java/enum_field.cc",
+ "src/google/protobuf/compiler/java/enum_field_lite.cc",
+ "src/google/protobuf/compiler/java/enum_lite.cc",
+ "src/google/protobuf/compiler/java/extension.cc",
+ "src/google/protobuf/compiler/java/extension_lite.cc",
+ "src/google/protobuf/compiler/java/field.cc",
+ "src/google/protobuf/compiler/java/file.cc",
+ "src/google/protobuf/compiler/java/generator.cc",
+ "src/google/protobuf/compiler/java/generator_factory.cc",
+ "src/google/protobuf/compiler/java/helpers.cc",
+ "src/google/protobuf/compiler/java/kotlin_generator.cc",
+ "src/google/protobuf/compiler/java/map_field.cc",
+ "src/google/protobuf/compiler/java/map_field_lite.cc",
+ "src/google/protobuf/compiler/java/message.cc",
+ "src/google/protobuf/compiler/java/message_builder.cc",
+ "src/google/protobuf/compiler/java/message_builder_lite.cc",
+ "src/google/protobuf/compiler/java/message_field.cc",
+ "src/google/protobuf/compiler/java/message_field_lite.cc",
+ "src/google/protobuf/compiler/java/message_lite.cc",
+ "src/google/protobuf/compiler/java/name_resolver.cc",
+ "src/google/protobuf/compiler/java/primitive_field.cc",
+ "src/google/protobuf/compiler/java/primitive_field_lite.cc",
+ "src/google/protobuf/compiler/java/service.cc",
+ "src/google/protobuf/compiler/java/shared_code_generator.cc",
+ "src/google/protobuf/compiler/java/string_field.cc",
+ "src/google/protobuf/compiler/java/string_field_lite.cc",
"src/google/protobuf/compiler/js/js_generator.cc",
"src/google/protobuf/compiler/js/well_known_types_embed.cc",
"src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
@@ -496,9 +497,9 @@ cc_library(
"src/google/protobuf/compiler/php/php_generator.cc",
"src/google/protobuf/compiler/plugin.cc",
"src/google/protobuf/compiler/plugin.pb.cc",
- "src/google/protobuf/compiler/python/python_generator.cc",
- "src/google/protobuf/compiler/python/python_helpers.cc",
- "src/google/protobuf/compiler/python/python_pyi_generator.cc",
+ "src/google/protobuf/compiler/python/generator.cc",
+ "src/google/protobuf/compiler/python/helpers.cc",
+ "src/google/protobuf/compiler/python/pyi_generator.cc",
"src/google/protobuf/compiler/ruby/ruby_generator.cc",
"src/google/protobuf/compiler/subprocess.cc",
"src/google/protobuf/compiler/zip_writer.cc",
@@ -605,8 +606,8 @@ LITE_TEST_PROTOS = ["src/" + s for s in RELATIVE_LITE_TEST_PROTOS]
RELATIVE_TEST_PROTOS = [
# AUTOGEN(test_protos)
"google/protobuf/any_test.proto",
- "google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto",
- "google/protobuf/compiler/cpp/cpp_test_large_enum_value.proto",
+ "google/protobuf/compiler/cpp/test_bad_identifiers.proto",
+ "google/protobuf/compiler/cpp/test_large_enum_value.proto",
"google/protobuf/map_proto2_unittest.proto",
"google/protobuf/map_unittest.proto",
"google/protobuf/unittest.proto",
@@ -765,23 +766,24 @@ cc_test(
"src/google/protobuf/any_test.cc",
"src/google/protobuf/arena_unittest.cc",
"src/google/protobuf/arenastring_unittest.cc",
+ "src/google/protobuf/arenaz_sampler_test.cc",
"src/google/protobuf/compiler/annotation_test_util.cc",
"src/google/protobuf/compiler/command_line_interface_unittest.cc",
- "src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc",
- "src/google/protobuf/compiler/cpp/cpp_move_unittest.cc",
- "src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc",
- "src/google/protobuf/compiler/cpp/cpp_unittest.cc",
- "src/google/protobuf/compiler/cpp/cpp_unittest.inc",
+ "src/google/protobuf/compiler/cpp/bootstrap_unittest.cc",
"src/google/protobuf/compiler/cpp/metadata_test.cc",
+ "src/google/protobuf/compiler/cpp/move_unittest.cc",
+ "src/google/protobuf/compiler/cpp/plugin_unittest.cc",
+ "src/google/protobuf/compiler/cpp/unittest.cc",
+ "src/google/protobuf/compiler/cpp/unittest.inc",
"src/google/protobuf/compiler/csharp/csharp_bootstrap_unittest.cc",
"src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc",
"src/google/protobuf/compiler/importer_unittest.cc",
- "src/google/protobuf/compiler/java/java_doc_comment_unittest.cc",
- "src/google/protobuf/compiler/java/java_plugin_unittest.cc",
+ "src/google/protobuf/compiler/java/doc_comment_unittest.cc",
+ "src/google/protobuf/compiler/java/plugin_unittest.cc",
"src/google/protobuf/compiler/mock_code_generator.cc",
"src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc",
"src/google/protobuf/compiler/parser_unittest.cc",
- "src/google/protobuf/compiler/python/python_plugin_unittest.cc",
+ "src/google/protobuf/compiler/python/plugin_unittest.cc",
"src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc",
"src/google/protobuf/descriptor_database_unittest.cc",
"src/google/protobuf/descriptor_unittest.cc",
@@ -789,6 +791,7 @@ cc_test(
"src/google/protobuf/dynamic_message_unittest.cc",
"src/google/protobuf/extension_set_unittest.cc",
"src/google/protobuf/generated_message_reflection_unittest.cc",
+ "src/google/protobuf/generated_message_tctable_lite_test.cc",
"src/google/protobuf/inlined_string_field_unittest.cc",
"src/google/protobuf/io/coded_stream_unittest.cc",
"src/google/protobuf/io/io_win32_unittest.cc",
diff --git a/CHANGES.txt b/CHANGES.txt
index 71b4927b1e..873c0dce6f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,8 +17,11 @@ Python
Ruby
* Dropped Ruby 2.3 and 2.4 support for CI and releases. (#9311)
- * Message.decode/encode: Add max_recursion_depth option (#9218)
- * Rename max_recursion_depth to recursion_limit (#9486)
+ * Added Ruby 3.1 support for CI and releases (#9566).
+ * Message.decode/encode: Add recursion_limit option (#9218/#9486)
+ * Allocate with xrealloc()/xfree() so message allocation is visible to the
+ Ruby GC. In certain tests this leads to much lower memory usage due to more
+ frequent GC runs (#9586).
* Fix conversion of singleton classes in Ruby (#9342)
* Suppress warning for intentional circular require (#9556)
* JSON will now output shorter strings for double and float fields when possible
diff --git a/Protobuf.podspec b/Protobuf.podspec
index 3bd7147eaf..92c53c9159 100644
--- a/Protobuf.podspec
+++ b/Protobuf.podspec
@@ -5,7 +5,7 @@
# dependent projects use the :git notation to refer to the library.
Pod::Spec.new do |s|
s.name = 'Protobuf'
- s.version = '3.20.0-rc1'
+ s.version = '3.20.0'
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
s.homepage = 'https://github.com/protocolbuffers/protobuf'
s.license = 'BSD-3-Clause'
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index a24b153ce5..9c60705bb4 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -145,6 +145,7 @@ set(tests_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/annotation_test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
+ ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_size_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/metadata_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/move_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/plugin_unittest.cc
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index b12978fa65..9b1548df55 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -2226,11 +2226,11 @@ void BinaryAndJsonConformanceSuite::RunJsonTestsForNonRepeatedTypes() {
"optional_aliased_enum: ALIAS_BAZ");
RunValidJsonTest(
"EnumFieldWithAliasUseAlias", REQUIRED,
- R"({"optionalAliasedEnum": "QUX"})",
+ R"({"optionalAliasedEnum": "MOO"})",
"optional_aliased_enum: ALIAS_BAZ");
RunValidJsonTest(
"EnumFieldWithAliasLowerCase", REQUIRED,
- R"({"optionalAliasedEnum": "qux"})",
+ R"({"optionalAliasedEnum": "moo"})",
"optional_aliased_enum: ALIAS_BAZ");
RunValidJsonTest(
"EnumFieldWithAliasDifferentCase", REQUIRED,
diff --git a/csharp/.editorconfig b/csharp/.editorconfig
new file mode 100644
index 0000000000..5e2afd0bee
--- /dev/null
+++ b/csharp/.editorconfig
@@ -0,0 +1,17 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = lf
+insert_final_newline = false
+trim_trailing_whitespace = true
\ No newline at end of file
diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec
index 86f12c9211..21b6a624dd 100644
--- a/csharp/Google.Protobuf.Tools.nuspec
+++ b/csharp/Google.Protobuf.Tools.nuspec
@@ -5,7 +5,7 @@
Google Protocol Buffers tools
Tools for Protocol Buffers - Google's data interchange format.
See project site for more info.
- 3.20.0-rc1
+ 3.20.0
Google Inc.
protobuf-packages
https://github.com/protocolbuffers/protobuf/blob/master/LICENSE
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
index 685e130a74..8935b7829d 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/ByteStringTest.cs
@@ -1,171 +1,171 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.Text;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class ByteStringTest
- {
- [Test]
- public void Equality()
- {
- ByteString b1 = ByteString.CopyFrom(1, 2, 3);
- ByteString b2 = ByteString.CopyFrom(1, 2, 3);
- ByteString b3 = ByteString.CopyFrom(1, 2, 4);
- ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
- EqualityTester.AssertEquality(b1, b1);
- EqualityTester.AssertEquality(b1, b2);
- EqualityTester.AssertInequality(b1, b3);
- EqualityTester.AssertInequality(b1, b4);
- EqualityTester.AssertInequality(b1, null);
-#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
- Assert.IsTrue(b1 == b1);
- Assert.IsTrue(b1 == b2);
- Assert.IsFalse(b1 == b3);
- Assert.IsFalse(b1 == b4);
- Assert.IsFalse(b1 == null);
- Assert.IsTrue((ByteString) null == null);
- Assert.IsFalse(b1 != b1);
- Assert.IsFalse(b1 != b2);
-#pragma warning disable 1718
- Assert.IsTrue(b1 != b3);
- Assert.IsTrue(b1 != b4);
- Assert.IsTrue(b1 != null);
- Assert.IsFalse((ByteString) null != null);
- }
-
- [Test]
- public void EmptyByteStringHasZeroSize()
- {
- Assert.AreEqual(0, ByteString.Empty.Length);
- }
-
- [Test]
- public void CopyFromStringWithExplicitEncoding()
- {
- ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
- Assert.AreEqual(4, bs.Length);
- Assert.AreEqual(65, bs[0]);
- Assert.AreEqual(0, bs[1]);
- Assert.AreEqual(66, bs[2]);
- Assert.AreEqual(0, bs[3]);
- }
-
- [Test]
- public void IsEmptyWhenEmpty()
- {
- Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
- }
-
- [Test]
- public void IsEmptyWhenNotEmpty()
- {
- Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
- }
-
- [Test]
- public void CopyFromByteArrayCopiesContents()
- {
- byte[] data = new byte[1];
- data[0] = 10;
- ByteString bs = ByteString.CopyFrom(data);
- Assert.AreEqual(10, bs[0]);
- data[0] = 5;
- Assert.AreEqual(10, bs[0]);
- }
-
- [Test]
- public void ToByteArrayCopiesContents()
- {
- ByteString bs = ByteString.CopyFromUtf8("Hello");
- byte[] data = bs.ToByteArray();
- Assert.AreEqual((byte)'H', data[0]);
- Assert.AreEqual((byte)'H', bs[0]);
- data[0] = 0;
- Assert.AreEqual(0, data[0]);
- Assert.AreEqual((byte)'H', bs[0]);
- }
-
- [Test]
- public void CopyFromUtf8UsesUtf8()
- {
- ByteString bs = ByteString.CopyFromUtf8("\u20ac");
- Assert.AreEqual(3, bs.Length);
- Assert.AreEqual(0xe2, bs[0]);
- Assert.AreEqual(0x82, bs[1]);
- Assert.AreEqual(0xac, bs[2]);
- }
-
- [Test]
- public void CopyFromPortion()
- {
- byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
- ByteString bs = ByteString.CopyFrom(data, 2, 3);
- Assert.AreEqual(3, bs.Length);
- Assert.AreEqual(2, bs[0]);
- Assert.AreEqual(3, bs[1]);
- }
-
- [Test]
- public void ToStringUtf8()
- {
- ByteString bs = ByteString.CopyFromUtf8("\u20ac");
- Assert.AreEqual("\u20ac", bs.ToStringUtf8());
- }
-
- [Test]
- public void ToStringWithExplicitEncoding()
- {
- ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
- Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
- }
-
- [Test]
- public void FromBase64_WithText()
- {
- byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
- string base64 = Convert.ToBase64String(data);
- ByteString bs = ByteString.FromBase64(base64);
- Assert.AreEqual(data, bs.ToByteArray());
- }
-
- [Test]
- public void FromBase64_Empty()
- {
- // Optimization which also fixes issue 61.
- Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.Text;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class ByteStringTest
+ {
+ [Test]
+ public void Equality()
+ {
+ ByteString b1 = ByteString.CopyFrom(1, 2, 3);
+ ByteString b2 = ByteString.CopyFrom(1, 2, 3);
+ ByteString b3 = ByteString.CopyFrom(1, 2, 4);
+ ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
+ EqualityTester.AssertEquality(b1, b1);
+ EqualityTester.AssertEquality(b1, b2);
+ EqualityTester.AssertInequality(b1, b3);
+ EqualityTester.AssertInequality(b1, b4);
+ EqualityTester.AssertInequality(b1, null);
+#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
+ Assert.IsTrue(b1 == b1);
+ Assert.IsTrue(b1 == b2);
+ Assert.IsFalse(b1 == b3);
+ Assert.IsFalse(b1 == b4);
+ Assert.IsFalse(b1 == null);
+ Assert.IsTrue((ByteString) null == null);
+ Assert.IsFalse(b1 != b1);
+ Assert.IsFalse(b1 != b2);
+#pragma warning disable 1718
+ Assert.IsTrue(b1 != b3);
+ Assert.IsTrue(b1 != b4);
+ Assert.IsTrue(b1 != null);
+ Assert.IsFalse((ByteString) null != null);
+ }
+
+ [Test]
+ public void EmptyByteStringHasZeroSize()
+ {
+ Assert.AreEqual(0, ByteString.Empty.Length);
+ }
+
+ [Test]
+ public void CopyFromStringWithExplicitEncoding()
+ {
+ ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
+ Assert.AreEqual(4, bs.Length);
+ Assert.AreEqual(65, bs[0]);
+ Assert.AreEqual(0, bs[1]);
+ Assert.AreEqual(66, bs[2]);
+ Assert.AreEqual(0, bs[3]);
+ }
+
+ [Test]
+ public void IsEmptyWhenEmpty()
+ {
+ Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
+ }
+
+ [Test]
+ public void IsEmptyWhenNotEmpty()
+ {
+ Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
+ }
+
+ [Test]
+ public void CopyFromByteArrayCopiesContents()
+ {
+ byte[] data = new byte[1];
+ data[0] = 10;
+ ByteString bs = ByteString.CopyFrom(data);
+ Assert.AreEqual(10, bs[0]);
+ data[0] = 5;
+ Assert.AreEqual(10, bs[0]);
+ }
+
+ [Test]
+ public void ToByteArrayCopiesContents()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("Hello");
+ byte[] data = bs.ToByteArray();
+ Assert.AreEqual((byte)'H', data[0]);
+ Assert.AreEqual((byte)'H', bs[0]);
+ data[0] = 0;
+ Assert.AreEqual(0, data[0]);
+ Assert.AreEqual((byte)'H', bs[0]);
+ }
+
+ [Test]
+ public void CopyFromUtf8UsesUtf8()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+ Assert.AreEqual(3, bs.Length);
+ Assert.AreEqual(0xe2, bs[0]);
+ Assert.AreEqual(0x82, bs[1]);
+ Assert.AreEqual(0xac, bs[2]);
+ }
+
+ [Test]
+ public void CopyFromPortion()
+ {
+ byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+ ByteString bs = ByteString.CopyFrom(data, 2, 3);
+ Assert.AreEqual(3, bs.Length);
+ Assert.AreEqual(2, bs[0]);
+ Assert.AreEqual(3, bs[1]);
+ }
+
+ [Test]
+ public void ToStringUtf8()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+ Assert.AreEqual("\u20ac", bs.ToStringUtf8());
+ }
+
+ [Test]
+ public void ToStringWithExplicitEncoding()
+ {
+ ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
+ Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
+ }
+
+ [Test]
+ public void FromBase64_WithText()
+ {
+ byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+ string base64 = Convert.ToBase64String(data);
+ ByteString bs = ByteString.FromBase64(base64);
+ Assert.AreEqual(data, bs.ToByteArray());
+ }
+
+ [Test]
+ public void FromBase64_Empty()
+ {
+ // Optimization which also fixes issue 61.
+ Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 11d06f1d7b..5ac8c71a82 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedInputStreamTest.cs
@@ -1,598 +1,598 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class CodedInputStreamTest
- {
- ///
- /// Helper to construct a byte array from a bunch of bytes. The inputs are
- /// actually ints so that I can use hex notation and not get stupid errors
- /// about precision.
- ///
- private static byte[] Bytes(params int[] bytesAsInts)
- {
- byte[] bytes = new byte[bytesAsInts.Length];
- for (int i = 0; i < bytesAsInts.Length; i++)
- {
- bytes[i] = (byte) bytesAsInts[i];
- }
- return bytes;
- }
-
- ///
- /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
- ///
- private static void AssertReadVarint(byte[] data, ulong value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual((uint) value, input.ReadRawVarint32());
-
- input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawVarint64());
- Assert.IsTrue(input.IsAtEnd);
-
- // Try different block sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
- Assert.AreEqual((uint) value, input.ReadRawVarint32());
-
- input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
- Assert.AreEqual(value, input.ReadRawVarint64());
- Assert.IsTrue(input.IsAtEnd);
- }
-
- // Try reading directly from a MemoryStream. We want to verify that it
- // doesn't read past the end of the input, so write an extra byte - this
- // lets us test the position at the end.
- MemoryStream memoryStream = new MemoryStream();
- memoryStream.Write(data, 0, data.Length);
- memoryStream.WriteByte(0);
- memoryStream.Position = 0;
- Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
- Assert.AreEqual(data.Length, memoryStream.Position);
- }
-
- ///
- /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
- /// expects them to fail with an InvalidProtocolBufferException whose
- /// description matches the given one.
- ///
- private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
- {
- CodedInputStream input = new CodedInputStream(data);
- var exception = Assert.Throws(() => input.ReadRawVarint32());
- Assert.AreEqual(expected.Message, exception.Message);
-
- input = new CodedInputStream(data);
- exception = Assert.Throws(() => input.ReadRawVarint64());
- Assert.AreEqual(expected.Message, exception.Message);
-
- // Make sure we get the same error when reading directly from a Stream.
- exception = Assert.Throws(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
- Assert.AreEqual(expected.Message, exception.Message);
- }
-
- [Test]
- public void ReadVarint()
- {
- AssertReadVarint(Bytes(0x00), 0);
- AssertReadVarint(Bytes(0x01), 1);
- AssertReadVarint(Bytes(0x7f), 127);
- // 14882
- AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
- // 2961488830
- AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
-
- // 64-bit
- // 7256456126
- AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
- // 41256202580718336
- AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
- // 11964378330978735131
- AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
- (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
- (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
-
- // Failures
- AssertReadVarintFailure(
- InvalidProtocolBufferException.MalformedVarint(),
- Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x00));
- AssertReadVarintFailure(
- InvalidProtocolBufferException.TruncatedMessage(),
- Bytes(0x80));
- }
-
- ///
- /// Parses the given bytes using ReadRawLittleEndian32() and checks
- /// that the result matches the given value.
- ///
- private static void AssertReadLittleEndian32(byte[] data, uint value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawLittleEndian32());
- Assert.IsTrue(input.IsAtEnd);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- input = new CodedInputStream(
- new SmallBlockInputStream(data, blockSize));
- Assert.AreEqual(value, input.ReadRawLittleEndian32());
- Assert.IsTrue(input.IsAtEnd);
- }
- }
-
- ///
- /// Parses the given bytes using ReadRawLittleEndian64() and checks
- /// that the result matches the given value.
- ///
- private static void AssertReadLittleEndian64(byte[] data, ulong value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawLittleEndian64());
- Assert.IsTrue(input.IsAtEnd);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- input = new CodedInputStream(
- new SmallBlockInputStream(data, blockSize));
- Assert.AreEqual(value, input.ReadRawLittleEndian64());
- Assert.IsTrue(input.IsAtEnd);
- }
- }
-
- [Test]
- public void ReadLittleEndian()
- {
- AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
- AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
-
- AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
- 0x123456789abcdef0L);
- AssertReadLittleEndian64(
- Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
- }
-
- [Test]
- public void DecodeZigZag32()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
- Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
- Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
- Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
- Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
- Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
- }
-
- [Test]
- public void DecodeZigZag64()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
- Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
- Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
- Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
- Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
- Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
- Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
- Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
- }
-
- [Test]
- public void ReadWholeMessage_VaryingBlockSizes()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- byte[] rawBytes = message.ToByteArray();
- Assert.AreEqual(rawBytes.Length, message.CalculateSize());
- TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
- Assert.AreEqual(message, message2);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2)
- {
- message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
- Assert.AreEqual(message, message2);
- }
- }
-
- [Test]
- public void ReadHugeBlob()
- {
- // Allocate and initialize a 1MB blob.
- byte[] blob = new byte[1 << 20];
- for (int i = 0; i < blob.Length; i++)
- {
- blob[i] = (byte) i;
- }
-
- // Make a message containing it.
- var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
-
- // Serialize and parse it. Make sure to parse from an InputStream, not
- // directly from a ByteString, so that CodedInputStream uses buffered
- // reading.
- TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
-
- Assert.AreEqual(message, message2);
- }
-
- [Test]
- public void ReadMaliciouslyLargeBlob()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(0x7FFFFFFF);
- output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
- Assert.AreEqual(tag, input.ReadTag());
-
- Assert.Throws(() => input.ReadBytes());
- }
-
- internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
- {
- if (depth == 0)
- {
- return new TestRecursiveMessage { I = 5 };
- }
- else
- {
- return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
- }
- }
-
- internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
- {
- if (depth == 0)
- {
- Assert.IsNull(message.A);
- Assert.AreEqual(5, message.I);
- }
- else
- {
- Assert.IsNotNull(message.A);
- AssertMessageDepth(message.A, depth - 1);
- }
- }
-
- [Test]
- public void MaliciousRecursion()
- {
- ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
- ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
-
- AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
-
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
-
- CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(input));
- }
-
- [Test]
- public void SizeLimit()
- {
- // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
- // apply to the latter case.
- MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
- CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
- Assert.Throws(() => TestAllTypes.Parser.ParseFrom(input));
- }
-
- ///
- /// Tests that if we read an string that contains invalid UTF-8, no exception
- /// is thrown. Instead, the invalid bytes are replaced with the Unicode
- /// "replacement character" U+FFFD.
- ///
- [Test]
- public void ReadInvalidUtf8()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(1);
- output.WriteRawBytes(new byte[] {0x80});
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
-
- Assert.AreEqual(tag, input.ReadTag());
- string text = input.ReadString();
- Assert.AreEqual('\ufffd', text[0]);
- }
-
- ///
- /// A stream which limits the number of bytes it reads at a time.
- /// We use this to make sure that CodedInputStream doesn't screw up when
- /// reading in small blocks.
- ///
- private sealed class SmallBlockInputStream : MemoryStream
- {
- private readonly int blockSize;
-
- public SmallBlockInputStream(byte[] data, int blockSize)
- : base(data)
- {
- this.blockSize = blockSize;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return base.Read(buffer, offset, Math.Min(count, blockSize));
- }
- }
-
- [Test]
- public void TestNegativeEnum()
- {
- byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
- CodedInputStream input = new CodedInputStream(bytes);
- Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
- Assert.IsTrue(input.IsAtEnd);
- }
-
- //Issue 71: CodedInputStream.ReadBytes go to slow path unnecessarily
- [Test]
- public void TestSlowPathAvoidance()
- {
- using (var ms = new MemoryStream())
- {
- CodedOutputStream output = new CodedOutputStream(ms);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteBytes(ByteString.CopyFrom(new byte[100]));
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteBytes(ByteString.CopyFrom(new byte[100]));
- output.Flush();
-
- ms.Position = 0;
- CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
-
- uint tag = input.ReadTag();
- Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
- Assert.AreEqual(100, input.ReadBytes().Length);
-
- tag = input.ReadTag();
- Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
- Assert.AreEqual(100, input.ReadBytes().Length);
- }
- }
-
- [Test]
- public void Tag0Throws()
- {
- var input = new CodedInputStream(new byte[] { 0 });
- Assert.Throws(() => input.ReadTag());
- }
-
- [Test]
- public void SkipGroup()
- {
- // Create an output stream with a group in:
- // Field 1: string "field 1"
- // Field 2: group containing:
- // Field 1: fixed int32 value 100
- // Field 2: string "ignore me"
- // Field 3: nested group containing
- // Field 1: fixed int64 value 1000
- // Field 3: string "field 3"
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 1");
-
- // The outer group...
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(1, WireFormat.WireType.Fixed32);
- output.WriteFixed32(100);
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteString("ignore me");
- // The nested group...
- output.WriteTag(3, WireFormat.WireType.StartGroup);
- output.WriteTag(1, WireFormat.WireType.Fixed64);
- output.WriteFixed64(1000);
- // Note: Not sure the field number is relevant for end group...
- output.WriteTag(3, WireFormat.WireType.EndGroup);
-
- // End the outer group
- output.WriteTag(2, WireFormat.WireType.EndGroup);
-
- output.WriteTag(3, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 3");
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 1", input.ReadString());
- Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
- input.SkipLastField(); // Should consume the whole group, including the nested one.
- Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 3", input.ReadString());
- }
-
- [Test]
- public void SkipGroup_WrongEndGroupTag()
- {
- // Create an output stream with:
- // Field 1: string "field 1"
- // Start group 2
- // Field 3: fixed int32
- // End group 4 (should give an error)
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 1");
-
- // The outer group...
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(3, WireFormat.WireType.Fixed32);
- output.WriteFixed32(100);
- output.WriteTag(4, WireFormat.WireType.EndGroup);
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 1", input.ReadString());
- Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void RogueEndGroupTag()
- {
- // If we have an end-group tag without a leading start-group tag, generated
- // code will just call SkipLastField... so that should fail.
-
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.EndGroup);
- output.Flush();
- stream.Position = 0;
-
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void EndOfStreamReachedWhileSkippingGroup()
- {
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.StartGroup);
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(2, WireFormat.WireType.EndGroup);
-
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- input.ReadTag();
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void RecursionLimitAppliedWhileSkippingGroup()
- {
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
- {
- output.WriteTag(1, WireFormat.WireType.StartGroup);
- }
- for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
- {
- output.WriteTag(1, WireFormat.WireType.EndGroup);
- }
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void Construction_Invalid()
- {
- Assert.Throws(() => new CodedInputStream((byte[]) null));
- Assert.Throws(() => new CodedInputStream(null, 0, 0));
- Assert.Throws(() => new CodedInputStream((Stream) null));
- Assert.Throws(() => new CodedInputStream(new byte[10], 100, 0));
- Assert.Throws(() => new CodedInputStream(new byte[10], 5, 10));
- }
-
- [Test]
- public void CreateWithLimits_InvalidLimits()
- {
- var stream = new MemoryStream();
- Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
- Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
- }
-
- [Test]
- public void Dispose_DisposesUnderlyingStream()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanRead);
- using (var cis = new CodedInputStream(memoryStream))
- {
- }
- Assert.IsFalse(memoryStream.CanRead); // Disposed
- }
-
- [Test]
- public void Dispose_WithLeaveOpen()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanRead);
- using (var cis = new CodedInputStream(memoryStream, true))
- {
- }
- Assert.IsTrue(memoryStream.CanRead); // We left the stream open
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class CodedInputStreamTest
+ {
+ ///
+ /// Helper to construct a byte array from a bunch of bytes. The inputs are
+ /// actually ints so that I can use hex notation and not get stupid errors
+ /// about precision.
+ ///
+ private static byte[] Bytes(params int[] bytesAsInts)
+ {
+ byte[] bytes = new byte[bytesAsInts.Length];
+ for (int i = 0; i < bytesAsInts.Length; i++)
+ {
+ bytes[i] = (byte) bytesAsInts[i];
+ }
+ return bytes;
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
+ ///
+ private static void AssertReadVarint(byte[] data, ulong value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+ input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawVarint64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ // Try different block sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+ Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+ input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+ Assert.AreEqual(value, input.ReadRawVarint64());
+ Assert.IsTrue(input.IsAtEnd);
+ }
+
+ // Try reading directly from a MemoryStream. We want to verify that it
+ // doesn't read past the end of the input, so write an extra byte - this
+ // lets us test the position at the end.
+ MemoryStream memoryStream = new MemoryStream();
+ memoryStream.Write(data, 0, data.Length);
+ memoryStream.WriteByte(0);
+ memoryStream.Position = 0;
+ Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
+ Assert.AreEqual(data.Length, memoryStream.Position);
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
+ /// expects them to fail with an InvalidProtocolBufferException whose
+ /// description matches the given one.
+ ///
+ private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ var exception = Assert.Throws(() => input.ReadRawVarint32());
+ Assert.AreEqual(expected.Message, exception.Message);
+
+ input = new CodedInputStream(data);
+ exception = Assert.Throws(() => input.ReadRawVarint64());
+ Assert.AreEqual(expected.Message, exception.Message);
+
+ // Make sure we get the same error when reading directly from a Stream.
+ exception = Assert.Throws(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
+ Assert.AreEqual(expected.Message, exception.Message);
+ }
+
+ [Test]
+ public void ReadVarint()
+ {
+ AssertReadVarint(Bytes(0x00), 0);
+ AssertReadVarint(Bytes(0x01), 1);
+ AssertReadVarint(Bytes(0x7f), 127);
+ // 14882
+ AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
+ // 2961488830
+ AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x0bL << 28));
+
+ // 64-bit
+ // 7256456126
+ AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x1bL << 28));
+ // 41256202580718336
+ AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+ (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+ (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+ // 11964378330978735131
+ AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+ (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+ (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
+ (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
+
+ // Failures
+ AssertReadVarintFailure(
+ InvalidProtocolBufferException.MalformedVarint(),
+ Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x00));
+ AssertReadVarintFailure(
+ InvalidProtocolBufferException.TruncatedMessage(),
+ Bytes(0x80));
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawLittleEndian32() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertReadLittleEndian32(byte[] data, uint value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawLittleEndian32());
+ Assert.IsTrue(input.IsAtEnd);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ input = new CodedInputStream(
+ new SmallBlockInputStream(data, blockSize));
+ Assert.AreEqual(value, input.ReadRawLittleEndian32());
+ Assert.IsTrue(input.IsAtEnd);
+ }
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawLittleEndian64() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertReadLittleEndian64(byte[] data, ulong value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawLittleEndian64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ input = new CodedInputStream(
+ new SmallBlockInputStream(data, blockSize));
+ Assert.AreEqual(value, input.ReadRawLittleEndian64());
+ Assert.IsTrue(input.IsAtEnd);
+ }
+ }
+
+ [Test]
+ public void ReadLittleEndian()
+ {
+ AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
+ AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
+
+ AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
+ 0x123456789abcdef0L);
+ AssertReadLittleEndian64(
+ Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
+ }
+
+ [Test]
+ public void DecodeZigZag32()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+ Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+ Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+ Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+ Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+ Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
+ }
+
+ [Test]
+ public void DecodeZigZag64()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+ Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+ Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+ Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+ Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+ }
+
+ [Test]
+ public void ReadWholeMessage_VaryingBlockSizes()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ byte[] rawBytes = message.ToByteArray();
+ Assert.AreEqual(rawBytes.Length, message.CalculateSize());
+ TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
+ Assert.AreEqual(message, message2);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+ {
+ message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
+ Assert.AreEqual(message, message2);
+ }
+ }
+
+ [Test]
+ public void ReadHugeBlob()
+ {
+ // Allocate and initialize a 1MB blob.
+ byte[] blob = new byte[1 << 20];
+ for (int i = 0; i < blob.Length; i++)
+ {
+ blob[i] = (byte) i;
+ }
+
+ // Make a message containing it.
+ var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
+
+ // Serialize and parse it. Make sure to parse from an InputStream, not
+ // directly from a ByteString, so that CodedInputStream uses buffered
+ // reading.
+ TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
+
+ Assert.AreEqual(message, message2);
+ }
+
+ [Test]
+ public void ReadMaliciouslyLargeBlob()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(0x7FFFFFFF);
+ output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+ Assert.AreEqual(tag, input.ReadTag());
+
+ Assert.Throws(() => input.ReadBytes());
+ }
+
+ internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
+ {
+ if (depth == 0)
+ {
+ return new TestRecursiveMessage { I = 5 };
+ }
+ else
+ {
+ return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
+ }
+ }
+
+ internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
+ {
+ if (depth == 0)
+ {
+ Assert.IsNull(message.A);
+ Assert.AreEqual(5, message.I);
+ }
+ else
+ {
+ Assert.IsNotNull(message.A);
+ AssertMessageDepth(message.A, depth - 1);
+ }
+ }
+
+ [Test]
+ public void MaliciousRecursion()
+ {
+ ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
+ ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
+
+ AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
+
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
+
+ CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(input));
+ }
+
+ [Test]
+ public void SizeLimit()
+ {
+ // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
+ // apply to the latter case.
+ MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
+ CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
+ Assert.Throws(() => TestAllTypes.Parser.ParseFrom(input));
+ }
+
+ ///
+ /// Tests that if we read an string that contains invalid UTF-8, no exception
+ /// is thrown. Instead, the invalid bytes are replaced with the Unicode
+ /// "replacement character" U+FFFD.
+ ///
+ [Test]
+ public void ReadInvalidUtf8()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(1);
+ output.WriteRawBytes(new byte[] {0x80});
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+
+ Assert.AreEqual(tag, input.ReadTag());
+ string text = input.ReadString();
+ Assert.AreEqual('\ufffd', text[0]);
+ }
+
+ ///
+ /// A stream which limits the number of bytes it reads at a time.
+ /// We use this to make sure that CodedInputStream doesn't screw up when
+ /// reading in small blocks.
+ ///
+ private sealed class SmallBlockInputStream : MemoryStream
+ {
+ private readonly int blockSize;
+
+ public SmallBlockInputStream(byte[] data, int blockSize)
+ : base(data)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return base.Read(buffer, offset, Math.Min(count, blockSize));
+ }
+ }
+
+ [Test]
+ public void TestNegativeEnum()
+ {
+ byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
+ CodedInputStream input = new CodedInputStream(bytes);
+ Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
+ Assert.IsTrue(input.IsAtEnd);
+ }
+
+ //Issue 71: CodedInputStream.ReadBytes go to slow path unnecessarily
+ [Test]
+ public void TestSlowPathAvoidance()
+ {
+ using (var ms = new MemoryStream())
+ {
+ CodedOutputStream output = new CodedOutputStream(ms);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+ output.Flush();
+
+ ms.Position = 0;
+ CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
+
+ uint tag = input.ReadTag();
+ Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
+ Assert.AreEqual(100, input.ReadBytes().Length);
+
+ tag = input.ReadTag();
+ Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
+ Assert.AreEqual(100, input.ReadBytes().Length);
+ }
+ }
+
+ [Test]
+ public void Tag0Throws()
+ {
+ var input = new CodedInputStream(new byte[] { 0 });
+ Assert.Throws(() => input.ReadTag());
+ }
+
+ [Test]
+ public void SkipGroup()
+ {
+ // Create an output stream with a group in:
+ // Field 1: string "field 1"
+ // Field 2: group containing:
+ // Field 1: fixed int32 value 100
+ // Field 2: string "ignore me"
+ // Field 3: nested group containing
+ // Field 1: fixed int64 value 1000
+ // Field 3: string "field 3"
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 1");
+
+ // The outer group...
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(1, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(100);
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteString("ignore me");
+ // The nested group...
+ output.WriteTag(3, WireFormat.WireType.StartGroup);
+ output.WriteTag(1, WireFormat.WireType.Fixed64);
+ output.WriteFixed64(1000);
+ // Note: Not sure the field number is relevant for end group...
+ output.WriteTag(3, WireFormat.WireType.EndGroup);
+
+ // End the outer group
+ output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+ output.WriteTag(3, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 3");
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 1", input.ReadString());
+ Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+ input.SkipLastField(); // Should consume the whole group, including the nested one.
+ Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 3", input.ReadString());
+ }
+
+ [Test]
+ public void SkipGroup_WrongEndGroupTag()
+ {
+ // Create an output stream with:
+ // Field 1: string "field 1"
+ // Start group 2
+ // Field 3: fixed int32
+ // End group 4 (should give an error)
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 1");
+
+ // The outer group...
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(3, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(100);
+ output.WriteTag(4, WireFormat.WireType.EndGroup);
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 1", input.ReadString());
+ Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void RogueEndGroupTag()
+ {
+ // If we have an end-group tag without a leading start-group tag, generated
+ // code will just call SkipLastField... so that should fail.
+
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.EndGroup);
+ output.Flush();
+ stream.Position = 0;
+
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void EndOfStreamReachedWhileSkippingGroup()
+ {
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.StartGroup);
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ input.ReadTag();
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void RecursionLimitAppliedWhileSkippingGroup()
+ {
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+ {
+ output.WriteTag(1, WireFormat.WireType.StartGroup);
+ }
+ for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+ {
+ output.WriteTag(1, WireFormat.WireType.EndGroup);
+ }
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void Construction_Invalid()
+ {
+ Assert.Throws(() => new CodedInputStream((byte[]) null));
+ Assert.Throws(() => new CodedInputStream(null, 0, 0));
+ Assert.Throws(() => new CodedInputStream((Stream) null));
+ Assert.Throws(() => new CodedInputStream(new byte[10], 100, 0));
+ Assert.Throws(() => new CodedInputStream(new byte[10], 5, 10));
+ }
+
+ [Test]
+ public void CreateWithLimits_InvalidLimits()
+ {
+ var stream = new MemoryStream();
+ Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
+ Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
+ }
+
+ [Test]
+ public void Dispose_DisposesUnderlyingStream()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanRead);
+ using (var cis = new CodedInputStream(memoryStream))
+ {
+ }
+ Assert.IsFalse(memoryStream.CanRead); // Disposed
+ }
+
+ [Test]
+ public void Dispose_WithLeaveOpen()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanRead);
+ using (var cis = new CodedInputStream(memoryStream, true))
+ {
+ }
+ Assert.IsTrue(memoryStream.CanRead); // We left the stream open
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
index e9b4ea8cf0..4890417517 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
@@ -1,419 +1,419 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class CodedOutputStreamTest
- {
- ///
- /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
- /// checks that the result matches the given bytes
- ///
- private static void AssertWriteVarint(byte[] data, ulong value)
- {
- // Only do 32-bit write if the value fits in 32 bits.
- if ((value >> 32) == 0)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawVarint32((uint) value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
- // Also try computing size.
- Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
- }
-
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawVarint64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- // Also try computing size.
- Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
- }
-
- // Try different buffer sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- // Only do 32-bit write if the value fits in 32 bits.
- if ((value >> 32) == 0)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output =
- new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawVarint32((uint) value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
- }
-
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawVarint64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
- }
- }
- }
-
- ///
- /// Tests WriteRawVarint32() and WriteRawVarint64()
- ///
- [Test]
- public void WriteVarint()
- {
- AssertWriteVarint(new byte[] {0x00}, 0);
- AssertWriteVarint(new byte[] {0x01}, 1);
- AssertWriteVarint(new byte[] {0x7f}, 127);
- // 14882
- AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
- // 2961488830
- AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
-
- // 64-bit
- // 7256456126
- AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
- // 41256202580718336
- AssertWriteVarint(
- new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
- // 11964378330978735131
- AssertWriteVarint(
- new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
- unchecked((ulong)
- ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
- (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
- }
-
- ///
- /// Parses the given bytes using WriteRawLittleEndian32() and checks
- /// that the result matches the given value.
- ///
- private static void AssertWriteLittleEndian32(byte[] data, uint value)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawLittleEndian32(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- // Try different buffer sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- rawOutput = new MemoryStream();
- output = new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawLittleEndian32(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
- }
- }
-
- ///
- /// Parses the given bytes using WriteRawLittleEndian64() and checks
- /// that the result matches the given value.
- ///
- private static void AssertWriteLittleEndian64(byte[] data, ulong value)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawLittleEndian64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- rawOutput = new MemoryStream();
- output = new CodedOutputStream(rawOutput, blockSize);
- output.WriteRawLittleEndian64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
- }
- }
-
- ///
- /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
- ///
- [Test]
- public void WriteLittleEndian()
- {
- AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
- AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
-
- AssertWriteLittleEndian64(
- new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
- 0x123456789abcdef0L);
- AssertWriteLittleEndian64(
- new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
- 0x9abcdef012345678UL);
- }
-
- [Test]
- public void WriteWholeMessage_VaryingBlockSizes()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- byte[] rawBytes = message.ToByteArray();
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
- message.WriteTo(output);
- output.Flush();
- Assert.AreEqual(rawBytes, rawOutput.ToArray());
- }
- }
-
- [Test]
- public void EncodeZigZag32()
- {
- Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
- Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
- Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
- Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
- Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
- Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
- Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
- Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
- }
-
- [Test]
- public void EncodeZigZag64()
- {
- Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
- Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
- Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
- Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
- Assert.AreEqual(0x000000007FFFFFFEuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
- Assert.AreEqual(0x000000007FFFFFFFuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
- Assert.AreEqual(0x00000000FFFFFFFEuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
- Assert.AreEqual(0x00000000FFFFFFFFuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
- Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
- Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
- }
-
- [Test]
- public void RoundTripZigZag32()
- {
- // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
- // were chosen semi-randomly via keyboard bashing.
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
- Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
- Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
- }
-
- [Test]
- public void RoundTripZigZag64()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
- Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
- Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
-
- Assert.AreEqual(856912304801416L,
- ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
- Assert.AreEqual(-75123905439571256L,
- ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
- }
-
- [Test]
- public void TestNegativeEnumNoTag()
- {
- Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
- Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
-
- byte[] bytes = new byte[10];
- CodedOutputStream output = new CodedOutputStream(bytes);
- output.WriteEnum((int) SampleEnum.NegativeValue);
-
- Assert.AreEqual(0, output.SpaceLeft);
- Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
- }
-
- [Test]
- public void TestCodedInputOutputPosition()
- {
- byte[] content = new byte[110];
- for (int i = 0; i < content.Length; i++)
- content[i] = (byte)i;
-
- byte[] child = new byte[120];
- {
- MemoryStream ms = new MemoryStream(child);
- CodedOutputStream cout = new CodedOutputStream(ms, 20);
- // Field 11: numeric value: 500
- cout.WriteTag(11, WireFormat.WireType.Varint);
- Assert.AreEqual(1, cout.Position);
- cout.WriteInt32(500);
- Assert.AreEqual(3, cout.Position);
- //Field 12: length delimited 120 bytes
- cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
- Assert.AreEqual(4, cout.Position);
- cout.WriteBytes(ByteString.CopyFrom(content));
- Assert.AreEqual(115, cout.Position);
- // Field 13: fixed numeric value: 501
- cout.WriteTag(13, WireFormat.WireType.Fixed32);
- Assert.AreEqual(116, cout.Position);
- cout.WriteSFixed32(501);
- Assert.AreEqual(120, cout.Position);
- cout.Flush();
- }
-
- byte[] bytes = new byte[130];
- {
- CodedOutputStream cout = new CodedOutputStream(bytes);
- // Field 1: numeric value: 500
- cout.WriteTag(1, WireFormat.WireType.Varint);
- Assert.AreEqual(1, cout.Position);
- cout.WriteInt32(500);
- Assert.AreEqual(3, cout.Position);
- //Field 2: length delimited 120 bytes
- cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
- Assert.AreEqual(4, cout.Position);
- cout.WriteBytes(ByteString.CopyFrom(child));
- Assert.AreEqual(125, cout.Position);
- // Field 3: fixed numeric value: 500
- cout.WriteTag(3, WireFormat.WireType.Fixed32);
- Assert.AreEqual(126, cout.Position);
- cout.WriteSFixed32(501);
- Assert.AreEqual(130, cout.Position);
- cout.Flush();
- }
- // Now test Input stream:
- {
- CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
- Assert.AreEqual(0, cin.Position);
- // Field 1:
- uint tag = cin.ReadTag();
- Assert.AreEqual(1, tag >> 3);
- Assert.AreEqual(1, cin.Position);
- Assert.AreEqual(500, cin.ReadInt32());
- Assert.AreEqual(3, cin.Position);
- //Field 2:
- tag = cin.ReadTag();
- Assert.AreEqual(2, tag >> 3);
- Assert.AreEqual(4, cin.Position);
- int childlen = cin.ReadLength();
- Assert.AreEqual(120, childlen);
- Assert.AreEqual(5, cin.Position);
- int oldlimit = cin.PushLimit((int)childlen);
- Assert.AreEqual(5, cin.Position);
- // Now we are reading child message
- {
- // Field 11: numeric value: 500
- tag = cin.ReadTag();
- Assert.AreEqual(11, tag >> 3);
- Assert.AreEqual(6, cin.Position);
- Assert.AreEqual(500, cin.ReadInt32());
- Assert.AreEqual(8, cin.Position);
- //Field 12: length delimited 120 bytes
- tag = cin.ReadTag();
- Assert.AreEqual(12, tag >> 3);
- Assert.AreEqual(9, cin.Position);
- ByteString bstr = cin.ReadBytes();
- Assert.AreEqual(110, bstr.Length);
- Assert.AreEqual((byte) 109, bstr[109]);
- Assert.AreEqual(120, cin.Position);
- // Field 13: fixed numeric value: 501
- tag = cin.ReadTag();
- Assert.AreEqual(13, tag >> 3);
- // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
- Assert.AreEqual(121, cin.Position);
- Assert.AreEqual(501, cin.ReadSFixed32());
- Assert.AreEqual(125, cin.Position);
- Assert.IsTrue(cin.IsAtEnd);
- }
- cin.PopLimit(oldlimit);
- Assert.AreEqual(125, cin.Position);
- // Field 3: fixed numeric value: 501
- tag = cin.ReadTag();
- Assert.AreEqual(3, tag >> 3);
- Assert.AreEqual(126, cin.Position);
- Assert.AreEqual(501, cin.ReadSFixed32());
- Assert.AreEqual(130, cin.Position);
- Assert.IsTrue(cin.IsAtEnd);
- }
- }
-
- [Test]
- public void Dispose_DisposesUnderlyingStream()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanWrite);
- using (var cos = new CodedOutputStream(memoryStream))
- {
- cos.WriteRawBytes(new byte[] {0});
- Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
- }
- Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
- Assert.IsFalse(memoryStream.CanWrite); // Disposed
- }
-
- [Test]
- public void Dispose_WithLeaveOpen()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanWrite);
- using (var cos = new CodedOutputStream(memoryStream, true))
- {
- cos.WriteRawBytes(new byte[] {0});
- Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
- }
- Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
- Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class CodedOutputStreamTest
+ {
+ ///
+ /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
+ /// checks that the result matches the given bytes
+ ///
+ private static void AssertWriteVarint(byte[] data, ulong value)
+ {
+ // Only do 32-bit write if the value fits in 32 bits.
+ if ((value >> 32) == 0)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawVarint32((uint) value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+ // Also try computing size.
+ Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
+ }
+
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawVarint64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ // Also try computing size.
+ Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
+ }
+
+ // Try different buffer sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ // Only do 32-bit write if the value fits in 32 bits.
+ if ((value >> 32) == 0)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output =
+ new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawVarint32((uint) value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+ }
+
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawVarint64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+ }
+ }
+ }
+
+ ///
+ /// Tests WriteRawVarint32() and WriteRawVarint64()
+ ///
+ [Test]
+ public void WriteVarint()
+ {
+ AssertWriteVarint(new byte[] {0x00}, 0);
+ AssertWriteVarint(new byte[] {0x01}, 1);
+ AssertWriteVarint(new byte[] {0x7f}, 127);
+ // 14882
+ AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
+ // 2961488830
+ AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x0bL << 28));
+
+ // 64-bit
+ // 7256456126
+ AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x1bL << 28));
+ // 41256202580718336
+ AssertWriteVarint(
+ new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
+ (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+ (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
+ // 11964378330978735131
+ AssertWriteVarint(
+ new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
+ unchecked((ulong)
+ ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+ (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
+ (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
+ }
+
+ ///
+ /// Parses the given bytes using WriteRawLittleEndian32() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertWriteLittleEndian32(byte[] data, uint value)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawLittleEndian32(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ // Try different buffer sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ rawOutput = new MemoryStream();
+ output = new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawLittleEndian32(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+ }
+ }
+
+ ///
+ /// Parses the given bytes using WriteRawLittleEndian64() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertWriteLittleEndian64(byte[] data, ulong value)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawLittleEndian64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ rawOutput = new MemoryStream();
+ output = new CodedOutputStream(rawOutput, blockSize);
+ output.WriteRawLittleEndian64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+ }
+ }
+
+ ///
+ /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
+ ///
+ [Test]
+ public void WriteLittleEndian()
+ {
+ AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
+ AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
+
+ AssertWriteLittleEndian64(
+ new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
+ 0x123456789abcdef0L);
+ AssertWriteLittleEndian64(
+ new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
+ 0x9abcdef012345678UL);
+ }
+
+ [Test]
+ public void WriteWholeMessage_VaryingBlockSizes()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ byte[] rawBytes = message.ToByteArray();
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
+ message.WriteTo(output);
+ output.Flush();
+ Assert.AreEqual(rawBytes, rawOutput.ToArray());
+ }
+ }
+
+ [Test]
+ public void EncodeZigZag32()
+ {
+ Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+ Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+ Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+ Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+ Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+ Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+ Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+ Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
+ }
+
+ [Test]
+ public void EncodeZigZag64()
+ {
+ Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+ Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+ Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+ Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
+ Assert.AreEqual(0x000000007FFFFFFEuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+ Assert.AreEqual(0x000000007FFFFFFFuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+ Assert.AreEqual(0x00000000FFFFFFFEuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+ Assert.AreEqual(0x00000000FFFFFFFFuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+ Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+ Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+ }
+
+ [Test]
+ public void RoundTripZigZag32()
+ {
+ // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
+ // were chosen semi-randomly via keyboard bashing.
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+ Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+ Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
+ }
+
+ [Test]
+ public void RoundTripZigZag64()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+ Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+ Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
+
+ Assert.AreEqual(856912304801416L,
+ ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
+ Assert.AreEqual(-75123905439571256L,
+ ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
+ }
+
+ [Test]
+ public void TestNegativeEnumNoTag()
+ {
+ Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
+ Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
+
+ byte[] bytes = new byte[10];
+ CodedOutputStream output = new CodedOutputStream(bytes);
+ output.WriteEnum((int) SampleEnum.NegativeValue);
+
+ Assert.AreEqual(0, output.SpaceLeft);
+ Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
+ }
+
+ [Test]
+ public void TestCodedInputOutputPosition()
+ {
+ byte[] content = new byte[110];
+ for (int i = 0; i < content.Length; i++)
+ content[i] = (byte)i;
+
+ byte[] child = new byte[120];
+ {
+ MemoryStream ms = new MemoryStream(child);
+ CodedOutputStream cout = new CodedOutputStream(ms, 20);
+ // Field 11: numeric value: 500
+ cout.WriteTag(11, WireFormat.WireType.Varint);
+ Assert.AreEqual(1, cout.Position);
+ cout.WriteInt32(500);
+ Assert.AreEqual(3, cout.Position);
+ //Field 12: length delimited 120 bytes
+ cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
+ Assert.AreEqual(4, cout.Position);
+ cout.WriteBytes(ByteString.CopyFrom(content));
+ Assert.AreEqual(115, cout.Position);
+ // Field 13: fixed numeric value: 501
+ cout.WriteTag(13, WireFormat.WireType.Fixed32);
+ Assert.AreEqual(116, cout.Position);
+ cout.WriteSFixed32(501);
+ Assert.AreEqual(120, cout.Position);
+ cout.Flush();
+ }
+
+ byte[] bytes = new byte[130];
+ {
+ CodedOutputStream cout = new CodedOutputStream(bytes);
+ // Field 1: numeric value: 500
+ cout.WriteTag(1, WireFormat.WireType.Varint);
+ Assert.AreEqual(1, cout.Position);
+ cout.WriteInt32(500);
+ Assert.AreEqual(3, cout.Position);
+ //Field 2: length delimited 120 bytes
+ cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ Assert.AreEqual(4, cout.Position);
+ cout.WriteBytes(ByteString.CopyFrom(child));
+ Assert.AreEqual(125, cout.Position);
+ // Field 3: fixed numeric value: 500
+ cout.WriteTag(3, WireFormat.WireType.Fixed32);
+ Assert.AreEqual(126, cout.Position);
+ cout.WriteSFixed32(501);
+ Assert.AreEqual(130, cout.Position);
+ cout.Flush();
+ }
+ // Now test Input stream:
+ {
+ CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
+ Assert.AreEqual(0, cin.Position);
+ // Field 1:
+ uint tag = cin.ReadTag();
+ Assert.AreEqual(1, tag >> 3);
+ Assert.AreEqual(1, cin.Position);
+ Assert.AreEqual(500, cin.ReadInt32());
+ Assert.AreEqual(3, cin.Position);
+ //Field 2:
+ tag = cin.ReadTag();
+ Assert.AreEqual(2, tag >> 3);
+ Assert.AreEqual(4, cin.Position);
+ int childlen = cin.ReadLength();
+ Assert.AreEqual(120, childlen);
+ Assert.AreEqual(5, cin.Position);
+ int oldlimit = cin.PushLimit((int)childlen);
+ Assert.AreEqual(5, cin.Position);
+ // Now we are reading child message
+ {
+ // Field 11: numeric value: 500
+ tag = cin.ReadTag();
+ Assert.AreEqual(11, tag >> 3);
+ Assert.AreEqual(6, cin.Position);
+ Assert.AreEqual(500, cin.ReadInt32());
+ Assert.AreEqual(8, cin.Position);
+ //Field 12: length delimited 120 bytes
+ tag = cin.ReadTag();
+ Assert.AreEqual(12, tag >> 3);
+ Assert.AreEqual(9, cin.Position);
+ ByteString bstr = cin.ReadBytes();
+ Assert.AreEqual(110, bstr.Length);
+ Assert.AreEqual((byte) 109, bstr[109]);
+ Assert.AreEqual(120, cin.Position);
+ // Field 13: fixed numeric value: 501
+ tag = cin.ReadTag();
+ Assert.AreEqual(13, tag >> 3);
+ // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
+ Assert.AreEqual(121, cin.Position);
+ Assert.AreEqual(501, cin.ReadSFixed32());
+ Assert.AreEqual(125, cin.Position);
+ Assert.IsTrue(cin.IsAtEnd);
+ }
+ cin.PopLimit(oldlimit);
+ Assert.AreEqual(125, cin.Position);
+ // Field 3: fixed numeric value: 501
+ tag = cin.ReadTag();
+ Assert.AreEqual(3, tag >> 3);
+ Assert.AreEqual(126, cin.Position);
+ Assert.AreEqual(501, cin.ReadSFixed32());
+ Assert.AreEqual(130, cin.Position);
+ Assert.IsTrue(cin.IsAtEnd);
+ }
+ }
+
+ [Test]
+ public void Dispose_DisposesUnderlyingStream()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanWrite);
+ using (var cos = new CodedOutputStream(memoryStream))
+ {
+ cos.WriteRawBytes(new byte[] {0});
+ Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+ }
+ Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
+ Assert.IsFalse(memoryStream.CanWrite); // Disposed
+ }
+
+ [Test]
+ public void Dispose_WithLeaveOpen()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanWrite);
+ using (var cos = new CodedOutputStream(memoryStream, true))
+ {
+ cos.WriteRawBytes(new byte[] {0});
+ Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+ }
+ Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
+ Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
index 34d5b9f98c..fd041e0171 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
@@ -1,55 +1,55 @@
-#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;
-using System.Reflection;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class DeprecatedMemberTest
- {
- private static void AssertIsDeprecated(MemberInfo member)
- {
- Assert.NotNull(member);
- Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
- }
-
- [Test]
- public void TestDepreatedPrimitiveValue()
- {
- AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
- }
-
- }
-}
+#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;
+using System.Reflection;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class DeprecatedMemberTest
+ {
+ private static void AssertIsDeprecated(MemberInfo member)
+ {
+ Assert.NotNull(member);
+ Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
+ }
+
+ [Test]
+ public void TestDepreatedPrimitiveValue()
+ {
+ AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
+ }
+
+ }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 429c51ff91..61f3abb46a 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -1,725 +1,725 @@
-#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;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using Google.Protobuf.WellKnownTypes;
-
-namespace Google.Protobuf
-{
- ///
- /// Tests around the generated TestAllTypes message.
- ///
- public class GeneratedMessageTest
- {
- [Test]
- public void EmptyMessageFieldDistinctFromMissingMessageField()
- {
- // This demonstrates what we're really interested in...
- var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
- var message2 = new TestAllTypes(); // SingleForeignMessage is null
- EqualityTester.AssertInequality(message1, message2);
- }
-
- [Test]
- public void DefaultValues()
- {
- // Single fields
- var message = new TestAllTypes();
- Assert.AreEqual(false, message.SingleBool);
- Assert.AreEqual(ByteString.Empty, message.SingleBytes);
- Assert.AreEqual(0.0, message.SingleDouble);
- Assert.AreEqual(0, message.SingleFixed32);
- Assert.AreEqual(0L, message.SingleFixed64);
- Assert.AreEqual(0.0f, message.SingleFloat);
- Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
- Assert.IsNull(message.SingleForeignMessage);
- Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
- Assert.IsNull(message.SingleImportMessage);
- Assert.AreEqual(0, message.SingleInt32);
- Assert.AreEqual(0L, message.SingleInt64);
- Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
- Assert.IsNull(message.SingleNestedMessage);
- Assert.IsNull(message.SinglePublicImportMessage);
- Assert.AreEqual(0, message.SingleSfixed32);
- Assert.AreEqual(0L, message.SingleSfixed64);
- Assert.AreEqual(0, message.SingleSint32);
- Assert.AreEqual(0L, message.SingleSint64);
- Assert.AreEqual("", message.SingleString);
- Assert.AreEqual(0U, message.SingleUint32);
- Assert.AreEqual(0UL, message.SingleUint64);
-
- // Repeated fields
- Assert.AreEqual(0, message.RepeatedBool.Count);
- Assert.AreEqual(0, message.RepeatedBytes.Count);
- Assert.AreEqual(0, message.RepeatedDouble.Count);
- Assert.AreEqual(0, message.RepeatedFixed32.Count);
- Assert.AreEqual(0, message.RepeatedFixed64.Count);
- Assert.AreEqual(0, message.RepeatedFloat.Count);
- Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
- Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
- Assert.AreEqual(0, message.RepeatedImportEnum.Count);
- Assert.AreEqual(0, message.RepeatedImportMessage.Count);
- Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
- Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
- Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
- Assert.AreEqual(0, message.RepeatedSfixed32.Count);
- Assert.AreEqual(0, message.RepeatedSfixed64.Count);
- Assert.AreEqual(0, message.RepeatedSint32.Count);
- Assert.AreEqual(0, message.RepeatedSint64.Count);
- Assert.AreEqual(0, message.RepeatedString.Count);
- Assert.AreEqual(0, message.RepeatedUint32.Count);
- Assert.AreEqual(0, message.RepeatedUint64.Count);
-
- // Oneof fields
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- }
-
- [Test]
- public void NullStringAndBytesRejected()
- {
- var message = new TestAllTypes();
- Assert.Throws(() => message.SingleString = null);
- Assert.Throws(() => message.OneofString = null);
- Assert.Throws(() => message.SingleBytes = null);
- Assert.Throws(() => message.OneofBytes = null);
- }
-
- [Test]
- public void RoundTrip_Empty()
- {
- var message = new TestAllTypes();
- // Without setting any values, there's nothing to write.
- byte[] bytes = message.ToByteArray();
- Assert.AreEqual(0, bytes.Length);
- TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, parsed);
- }
-
- [Test]
- public void RoundTrip_SingleValues()
- {
- var message = new TestAllTypes
- {
- SingleBool = true,
- SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
- SingleDouble = 23.5,
- SingleFixed32 = 23,
- SingleFixed64 = 1234567890123,
- SingleFloat = 12.25f,
- SingleForeignEnum = ForeignEnum.ForeignBar,
- SingleForeignMessage = new ForeignMessage { C = 10 },
- SingleImportEnum = ImportEnum.ImportBaz,
- SingleImportMessage = new ImportMessage { D = 20 },
- SingleInt32 = 100,
- SingleInt64 = 3210987654321,
- SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
- SinglePublicImportMessage = new PublicImportMessage { E = 54 },
- SingleSfixed32 = -123,
- SingleSfixed64 = -12345678901234,
- SingleSint32 = -456,
- SingleSint64 = -12345678901235,
- SingleString = "test",
- SingleUint32 = uint.MaxValue,
- SingleUint64 = ulong.MaxValue
- };
-
- byte[] bytes = message.ToByteArray();
- TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, parsed);
- }
-
- [Test]
- public void RoundTrip_RepeatedValues()
- {
- var message = new TestAllTypes
- {
- RepeatedBool = { true, false },
- RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
- RepeatedDouble = { -12.25, 23.5 },
- RepeatedFixed32 = { uint.MaxValue, 23 },
- RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
- RepeatedFloat = { 100f, 12.25f },
- RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
- RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
- RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified },
- RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
- RepeatedInt32 = { 100, 200 },
- RepeatedInt64 = { 3210987654321, long.MaxValue },
- RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
- RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
- RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
- RepeatedSfixed32 = { -123, 123 },
- RepeatedSfixed64 = { -12345678901234, 12345678901234 },
- RepeatedSint32 = { -456, 100 },
- RepeatedSint64 = { -12345678901235, 123 },
- RepeatedString = { "foo", "bar" },
- RepeatedUint32 = { uint.MaxValue, uint.MinValue },
- RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
- };
-
- byte[] bytes = message.ToByteArray();
- TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, parsed);
- }
-
- // Note that not every map within map_unittest_proto3 is used. They all go through very
- // similar code paths. The fact that all maps are present is validation that we have codecs
- // for every type.
- [Test]
- public void RoundTrip_Maps()
- {
- var message = new TestMap
- {
- MapBoolBool = {
- { false, true },
- { true, false }
- },
- MapInt32Bytes = {
- { 5, ByteString.CopyFrom(6, 7, 8) },
- { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
- { 10, ByteString.Empty }
- },
- MapInt32ForeignMessage = {
- { 0, new ForeignMessage { C = 10 } },
- { 5, new ForeignMessage() },
- },
- MapInt32Enum = {
- { 1, MapEnum.Bar },
- { 2000, MapEnum.Foo }
- }
- };
-
- byte[] bytes = message.ToByteArray();
- TestMap parsed = TestMap.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, parsed);
- }
-
- [Test]
- public void MapWithEmptyEntry()
- {
- var message = new TestMap
- {
- MapInt32Bytes = { { 0, ByteString.Empty } }
- };
-
- byte[] bytes = message.ToByteArray();
- Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
-
- var parsed = TestMap.Parser.ParseFrom(bytes);
- Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
- Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
- }
-
- [Test]
- public void MapWithOnlyValue()
- {
- // Hand-craft the stream to contain a single entry with just a value.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
- var nestedMessage = new ForeignMessage { C = 20 };
- // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
- output.WriteLength(2 + nestedMessage.CalculateSize());
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteMessage(nestedMessage);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
- }
-
- [Test]
- public void MapWithOnlyKey_PrimitiveValue()
- {
- // Hand-craft the stream to contain a single entry with just a key.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
- int key = 10;
- output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
- }
-
- [Test]
- public void MapWithOnlyKey_MessageValue()
- {
- // Hand-craft the stream to contain a single entry with just a key.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
- int key = 10;
- output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
- }
-
- [Test]
- public void MapIgnoresExtraFieldsWithinEntryMessages()
- {
- // Hand-craft the stream to contain a single entry with three fields
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
-
- var key = 10; // Field 1
- var value = 20; // Field 2
- var extra = 30; // Field 3
-
- // Each field can be represented in a single byte, with a single byte tag.
- // Total message size: 6 bytes.
- output.WriteLength(6);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value);
- output.WriteTag(3, WireFormat.WireType.Varint);
- output.WriteInt32(extra);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(value, parsed.MapInt32Int32[key]);
- }
-
- [Test]
- public void MapFieldOrderIsIrrelevant()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
-
- var key = 10;
- var value = 20;
-
- // Each field can be represented in a single byte, with a single byte tag.
- // Total message size: 4 bytes.
- output.WriteLength(4);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(value, parsed.MapInt32Int32[key]);
- }
-
- [Test]
- public void MapNonContiguousEntries()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- // Message structure:
- // Entry for MapInt32Int32
- // Entry for MapStringString
- // Entry for MapInt32Int32
-
- // First entry
- var key1 = 10;
- var value1 = 20;
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key1);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value1);
-
- // Second entry
- var key2 = "a";
- var value2 = "b";
- output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(6); // 3 bytes per entry: tag, size, character
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString(key2);
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteString(value2);
-
- // Third entry
- var key3 = 15;
- var value3 = 25;
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key3);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value3);
-
- output.Flush();
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- var expected = new TestMap
- {
- MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
- MapStringString = { { key2, value2 } }
- };
- Assert.AreEqual(expected, parsed);
- }
-
- [Test]
- public void DuplicateKeys_LastEntryWins()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- var key = 10;
- var value1 = 20;
- var value2 = 30;
-
- // First entry
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value1);
-
- // Second entry - same key, different value
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value2);
- output.Flush();
-
- var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
- Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
- }
-
- [Test]
- public void CloneSingleNonMessageValues()
- {
- var original = new TestAllTypes
- {
- SingleBool = true,
- SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
- SingleDouble = 23.5,
- SingleFixed32 = 23,
- SingleFixed64 = 1234567890123,
- SingleFloat = 12.25f,
- SingleInt32 = 100,
- SingleInt64 = 3210987654321,
- SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
- SingleSfixed32 = -123,
- SingleSfixed64 = -12345678901234,
- SingleSint32 = -456,
- SingleSint64 = -12345678901235,
- SingleString = "test",
- SingleUint32 = uint.MaxValue,
- SingleUint64 = ulong.MaxValue
- };
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
- // Just as a single example
- clone.SingleInt32 = 150;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneRepeatedNonMessageValues()
- {
- var original = new TestAllTypes
- {
- RepeatedBool = { true, false },
- RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
- RepeatedDouble = { -12.25, 23.5 },
- RepeatedFixed32 = { uint.MaxValue, 23 },
- RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
- RepeatedFloat = { 100f, 12.25f },
- RepeatedInt32 = { 100, 200 },
- RepeatedInt64 = { 3210987654321, long.MaxValue },
- RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
- RepeatedSfixed32 = { -123, 123 },
- RepeatedSfixed64 = { -12345678901234, 12345678901234 },
- RepeatedSint32 = { -456, 100 },
- RepeatedSint64 = { -12345678901235, 123 },
- RepeatedString = { "foo", "bar" },
- RepeatedUint32 = { uint.MaxValue, uint.MinValue },
- RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
- // Just as a single example
- clone.RepeatedDouble.Add(25.5);
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneSingleMessageField()
- {
- var original = new TestAllTypes
- {
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
- Assert.AreEqual(original, clone);
-
- clone.SingleNestedMessage.Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneRepeatedMessageField()
- {
- var original = new TestAllTypes
- {
- RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
- Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
- Assert.AreEqual(original, clone);
-
- clone.RepeatedNestedMessage[0].Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneOneofField()
- {
- var original = new TestAllTypes
- {
- OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
-
- // We should have cloned the message
- original.OneofNestedMessage.Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void OneofProperties()
- {
- // Switch the oneof case between each of the different options, and check everything behaves
- // as expected in each case.
- var message = new TestAllTypes();
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
-
- message.OneofString = "sample";
- Assert.AreEqual("sample", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
-
- var bytes = ByteString.CopyFrom(1, 2, 3);
- message.OneofBytes = bytes;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(bytes, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
-
- message.OneofUint32 = 20;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(20, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
-
- var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
- message.OneofNestedMessage = nestedMessage;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
-
- message.ClearOneofField();
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
- }
-
- [Test]
- public void Oneof_DefaultValuesNotEqual()
- {
- var message1 = new TestAllTypes { OneofString = "" };
- var message2 = new TestAllTypes { OneofUint32 = 0 };
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
- Assert.AreNotEqual(message1, message2);
- }
-
- [Test]
- public void OneofSerialization_NonDefaultValue()
- {
- var message = new TestAllTypes();
- message.OneofString = "this would take a bit of space";
- message.OneofUint32 = 10;
- var bytes = message.ToByteArray();
- Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
-
- var message2 = TestAllTypes.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, message2);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
- }
-
- [Test]
- public void OneofSerialization_DefaultValue()
- {
- var message = new TestAllTypes();
- message.OneofString = "this would take a bit of space";
- message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
- var bytes = message.ToByteArray();
- Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
-
- var message2 = TestAllTypes.Parser.ParseFrom(bytes);
- Assert.AreEqual(message, message2);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
- }
-
- [Test]
- public void DiscardUnknownFields_RealDataStillRead()
- {
- var message = SampleMessages.CreateFullTestAllTypes();
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- var unusedFieldNumber = 23456;
- Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
- output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteString("ignore me");
- message.WriteTo(output);
- output.Flush();
-
- stream.Position = 0;
- var parsed = TestAllTypes.Parser.ParseFrom(stream);
- // TODO(jieluo): Add test back after DiscardUnknownFields is supported
- // Assert.AreEqual(message, parsed);
- }
-
- [Test]
- public void DiscardUnknownFields_AllTypes()
- {
- // Simple way of ensuring we can skip all kinds of fields.
- var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
- var empty = Empty.Parser.ParseFrom(data);
- // TODO(jieluo): Add test back after DiscardUnknownField is supported.
- // Assert.AreEqual(new Empty(), empty);
- }
-
- // This was originally seen as a conformance test failure.
- [Test]
- public void TruncatedMessageFieldThrows()
- {
- // 130, 3 is the message tag
- // 1 is the data length - but there's no data.
- var data = new byte[] { 130, 3, 1 };
- Assert.Throws(() => TestAllTypes.Parser.ParseFrom(data));
- }
-
- ///
- /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
- /// for details; we may want to change this.
- ///
- [Test]
- public void ExtraEndGroupThrows()
- {
- var message = SampleMessages.CreateFullTestAllTypes();
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
-
- output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
- output.WriteFixed32(123);
- output.WriteTag(100, WireFormat.WireType.EndGroup);
-
- output.Flush();
-
- stream.Position = 0;
- Assert.Throws(() => TestAllTypes.Parser.ParseFrom(stream));
- }
-
- [Test]
- public void CustomDiagnosticMessage_DirectToStringCall()
- {
- var message = new ForeignMessage { C = 31 };
- Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
- Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void CustomDiagnosticMessage_Nested()
- {
- var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
- Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
- Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void CustomDiagnosticMessage_DirectToTextWriterCall()
- {
- var message = new ForeignMessage { C = 31 };
- var writer = new StringWriter();
- JsonFormatter.Default.Format(message, writer);
- Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
- }
- }
-}
+#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;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Google.Protobuf.WellKnownTypes;
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests around the generated TestAllTypes message.
+ ///
+ public class GeneratedMessageTest
+ {
+ [Test]
+ public void EmptyMessageFieldDistinctFromMissingMessageField()
+ {
+ // This demonstrates what we're really interested in...
+ var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
+ var message2 = new TestAllTypes(); // SingleForeignMessage is null
+ EqualityTester.AssertInequality(message1, message2);
+ }
+
+ [Test]
+ public void DefaultValues()
+ {
+ // Single fields
+ var message = new TestAllTypes();
+ Assert.AreEqual(false, message.SingleBool);
+ Assert.AreEqual(ByteString.Empty, message.SingleBytes);
+ Assert.AreEqual(0.0, message.SingleDouble);
+ Assert.AreEqual(0, message.SingleFixed32);
+ Assert.AreEqual(0L, message.SingleFixed64);
+ Assert.AreEqual(0.0f, message.SingleFloat);
+ Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
+ Assert.IsNull(message.SingleForeignMessage);
+ Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
+ Assert.IsNull(message.SingleImportMessage);
+ Assert.AreEqual(0, message.SingleInt32);
+ Assert.AreEqual(0L, message.SingleInt64);
+ Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
+ Assert.IsNull(message.SingleNestedMessage);
+ Assert.IsNull(message.SinglePublicImportMessage);
+ Assert.AreEqual(0, message.SingleSfixed32);
+ Assert.AreEqual(0L, message.SingleSfixed64);
+ Assert.AreEqual(0, message.SingleSint32);
+ Assert.AreEqual(0L, message.SingleSint64);
+ Assert.AreEqual("", message.SingleString);
+ Assert.AreEqual(0U, message.SingleUint32);
+ Assert.AreEqual(0UL, message.SingleUint64);
+
+ // Repeated fields
+ Assert.AreEqual(0, message.RepeatedBool.Count);
+ Assert.AreEqual(0, message.RepeatedBytes.Count);
+ Assert.AreEqual(0, message.RepeatedDouble.Count);
+ Assert.AreEqual(0, message.RepeatedFixed32.Count);
+ Assert.AreEqual(0, message.RepeatedFixed64.Count);
+ Assert.AreEqual(0, message.RepeatedFloat.Count);
+ Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+ Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+ Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+ Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+ Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+ Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+ Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+ Assert.AreEqual(0, message.RepeatedSint32.Count);
+ Assert.AreEqual(0, message.RepeatedSint64.Count);
+ Assert.AreEqual(0, message.RepeatedString.Count);
+ Assert.AreEqual(0, message.RepeatedUint32.Count);
+ Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+ // Oneof fields
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ }
+
+ [Test]
+ public void NullStringAndBytesRejected()
+ {
+ var message = new TestAllTypes();
+ Assert.Throws(() => message.SingleString = null);
+ Assert.Throws(() => message.OneofString = null);
+ Assert.Throws(() => message.SingleBytes = null);
+ Assert.Throws(() => message.OneofBytes = null);
+ }
+
+ [Test]
+ public void RoundTrip_Empty()
+ {
+ var message = new TestAllTypes();
+ // Without setting any values, there's nothing to write.
+ byte[] bytes = message.ToByteArray();
+ Assert.AreEqual(0, bytes.Length);
+ TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, parsed);
+ }
+
+ [Test]
+ public void RoundTrip_SingleValues()
+ {
+ var message = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+ SingleDouble = 23.5,
+ SingleFixed32 = 23,
+ SingleFixed64 = 1234567890123,
+ SingleFloat = 12.25f,
+ SingleForeignEnum = ForeignEnum.ForeignBar,
+ SingleForeignMessage = new ForeignMessage { C = 10 },
+ SingleImportEnum = ImportEnum.ImportBaz,
+ SingleImportMessage = new ImportMessage { D = 20 },
+ SingleInt32 = 100,
+ SingleInt64 = 3210987654321,
+ SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+ SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
+ SinglePublicImportMessage = new PublicImportMessage { E = 54 },
+ SingleSfixed32 = -123,
+ SingleSfixed64 = -12345678901234,
+ SingleSint32 = -456,
+ SingleSint64 = -12345678901235,
+ SingleString = "test",
+ SingleUint32 = uint.MaxValue,
+ SingleUint64 = ulong.MaxValue
+ };
+
+ byte[] bytes = message.ToByteArray();
+ TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, parsed);
+ }
+
+ [Test]
+ public void RoundTrip_RepeatedValues()
+ {
+ var message = new TestAllTypes
+ {
+ RepeatedBool = { true, false },
+ RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+ RepeatedDouble = { -12.25, 23.5 },
+ RepeatedFixed32 = { uint.MaxValue, 23 },
+ RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+ RepeatedFloat = { 100f, 12.25f },
+ RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
+ RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
+ RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified },
+ RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
+ RepeatedInt32 = { 100, 200 },
+ RepeatedInt64 = { 3210987654321, long.MaxValue },
+ RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+ RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
+ RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
+ RepeatedSfixed32 = { -123, 123 },
+ RepeatedSfixed64 = { -12345678901234, 12345678901234 },
+ RepeatedSint32 = { -456, 100 },
+ RepeatedSint64 = { -12345678901235, 123 },
+ RepeatedString = { "foo", "bar" },
+ RepeatedUint32 = { uint.MaxValue, uint.MinValue },
+ RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
+ };
+
+ byte[] bytes = message.ToByteArray();
+ TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, parsed);
+ }
+
+ // Note that not every map within map_unittest_proto3 is used. They all go through very
+ // similar code paths. The fact that all maps are present is validation that we have codecs
+ // for every type.
+ [Test]
+ public void RoundTrip_Maps()
+ {
+ var message = new TestMap
+ {
+ MapBoolBool = {
+ { false, true },
+ { true, false }
+ },
+ MapInt32Bytes = {
+ { 5, ByteString.CopyFrom(6, 7, 8) },
+ { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
+ { 10, ByteString.Empty }
+ },
+ MapInt32ForeignMessage = {
+ { 0, new ForeignMessage { C = 10 } },
+ { 5, new ForeignMessage() },
+ },
+ MapInt32Enum = {
+ { 1, MapEnum.Bar },
+ { 2000, MapEnum.Foo }
+ }
+ };
+
+ byte[] bytes = message.ToByteArray();
+ TestMap parsed = TestMap.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, parsed);
+ }
+
+ [Test]
+ public void MapWithEmptyEntry()
+ {
+ var message = new TestMap
+ {
+ MapInt32Bytes = { { 0, ByteString.Empty } }
+ };
+
+ byte[] bytes = message.ToByteArray();
+ Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
+
+ var parsed = TestMap.Parser.ParseFrom(bytes);
+ Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+ Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+ }
+
+ [Test]
+ public void MapWithOnlyValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a value.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+ var nestedMessage = new ForeignMessage { C = 20 };
+ // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
+ output.WriteLength(2 + nestedMessage.CalculateSize());
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteMessage(nestedMessage);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+ }
+
+ [Test]
+ public void MapWithOnlyKey_PrimitiveValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+ }
+
+ [Test]
+ public void MapWithOnlyKey_MessageValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+ }
+
+ [Test]
+ public void MapIgnoresExtraFieldsWithinEntryMessages()
+ {
+ // Hand-craft the stream to contain a single entry with three fields
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+ var key = 10; // Field 1
+ var value = 20; // Field 2
+ var extra = 30; // Field 3
+
+ // Each field can be represented in a single byte, with a single byte tag.
+ // Total message size: 6 bytes.
+ output.WriteLength(6);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value);
+ output.WriteTag(3, WireFormat.WireType.Varint);
+ output.WriteInt32(extra);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+ }
+
+ [Test]
+ public void MapFieldOrderIsIrrelevant()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+ var key = 10;
+ var value = 20;
+
+ // Each field can be represented in a single byte, with a single byte tag.
+ // Total message size: 4 bytes.
+ output.WriteLength(4);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+ }
+
+ [Test]
+ public void MapNonContiguousEntries()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ // Message structure:
+ // Entry for MapInt32Int32
+ // Entry for MapStringString
+ // Entry for MapInt32Int32
+
+ // First entry
+ var key1 = 10;
+ var value1 = 20;
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key1);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value1);
+
+ // Second entry
+ var key2 = "a";
+ var value2 = "b";
+ output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(6); // 3 bytes per entry: tag, size, character
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString(key2);
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteString(value2);
+
+ // Third entry
+ var key3 = 15;
+ var value3 = 25;
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key3);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value3);
+
+ output.Flush();
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ var expected = new TestMap
+ {
+ MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+ MapStringString = { { key2, value2 } }
+ };
+ Assert.AreEqual(expected, parsed);
+ }
+
+ [Test]
+ public void DuplicateKeys_LastEntryWins()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ var key = 10;
+ var value1 = 20;
+ var value2 = 30;
+
+ // First entry
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value1);
+
+ // Second entry - same key, different value
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value2);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+ }
+
+ [Test]
+ public void CloneSingleNonMessageValues()
+ {
+ var original = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+ SingleDouble = 23.5,
+ SingleFixed32 = 23,
+ SingleFixed64 = 1234567890123,
+ SingleFloat = 12.25f,
+ SingleInt32 = 100,
+ SingleInt64 = 3210987654321,
+ SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+ SingleSfixed32 = -123,
+ SingleSfixed64 = -12345678901234,
+ SingleSint32 = -456,
+ SingleSint64 = -12345678901235,
+ SingleString = "test",
+ SingleUint32 = uint.MaxValue,
+ SingleUint64 = ulong.MaxValue
+ };
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+ // Just as a single example
+ clone.SingleInt32 = 150;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneRepeatedNonMessageValues()
+ {
+ var original = new TestAllTypes
+ {
+ RepeatedBool = { true, false },
+ RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+ RepeatedDouble = { -12.25, 23.5 },
+ RepeatedFixed32 = { uint.MaxValue, 23 },
+ RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+ RepeatedFloat = { 100f, 12.25f },
+ RepeatedInt32 = { 100, 200 },
+ RepeatedInt64 = { 3210987654321, long.MaxValue },
+ RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+ RepeatedSfixed32 = { -123, 123 },
+ RepeatedSfixed64 = { -12345678901234, 12345678901234 },
+ RepeatedSint32 = { -456, 100 },
+ RepeatedSint64 = { -12345678901235, 123 },
+ RepeatedString = { "foo", "bar" },
+ RepeatedUint32 = { uint.MaxValue, uint.MinValue },
+ RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+ // Just as a single example
+ clone.RepeatedDouble.Add(25.5);
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneSingleMessageField()
+ {
+ var original = new TestAllTypes
+ {
+ SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
+ Assert.AreEqual(original, clone);
+
+ clone.SingleNestedMessage.Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneRepeatedMessageField()
+ {
+ var original = new TestAllTypes
+ {
+ RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
+ Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
+ Assert.AreEqual(original, clone);
+
+ clone.RepeatedNestedMessage[0].Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneOneofField()
+ {
+ var original = new TestAllTypes
+ {
+ OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+
+ // We should have cloned the message
+ original.OneofNestedMessage.Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void OneofProperties()
+ {
+ // Switch the oneof case between each of the different options, and check everything behaves
+ // as expected in each case.
+ var message = new TestAllTypes();
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+
+ message.OneofString = "sample";
+ Assert.AreEqual("sample", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
+
+ var bytes = ByteString.CopyFrom(1, 2, 3);
+ message.OneofBytes = bytes;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(bytes, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
+
+ message.OneofUint32 = 20;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(20, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
+
+ var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
+ message.OneofNestedMessage = nestedMessage;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
+
+ message.ClearOneofField();
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+ }
+
+ [Test]
+ public void Oneof_DefaultValuesNotEqual()
+ {
+ var message1 = new TestAllTypes { OneofString = "" };
+ var message2 = new TestAllTypes { OneofUint32 = 0 };
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+ Assert.AreNotEqual(message1, message2);
+ }
+
+ [Test]
+ public void OneofSerialization_NonDefaultValue()
+ {
+ var message = new TestAllTypes();
+ message.OneofString = "this would take a bit of space";
+ message.OneofUint32 = 10;
+ var bytes = message.ToByteArray();
+ Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
+
+ var message2 = TestAllTypes.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, message2);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+ }
+
+ [Test]
+ public void OneofSerialization_DefaultValue()
+ {
+ var message = new TestAllTypes();
+ message.OneofString = "this would take a bit of space";
+ message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
+ var bytes = message.ToByteArray();
+ Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
+
+ var message2 = TestAllTypes.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, message2);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+ }
+
+ [Test]
+ public void DiscardUnknownFields_RealDataStillRead()
+ {
+ var message = SampleMessages.CreateFullTestAllTypes();
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ var unusedFieldNumber = 23456;
+ Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
+ output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteString("ignore me");
+ message.WriteTo(output);
+ output.Flush();
+
+ stream.Position = 0;
+ var parsed = TestAllTypes.Parser.ParseFrom(stream);
+ // TODO(jieluo): Add test back after DiscardUnknownFields is supported
+ // Assert.AreEqual(message, parsed);
+ }
+
+ [Test]
+ public void DiscardUnknownFields_AllTypes()
+ {
+ // Simple way of ensuring we can skip all kinds of fields.
+ var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
+ var empty = Empty.Parser.ParseFrom(data);
+ // TODO(jieluo): Add test back after DiscardUnknownField is supported.
+ // Assert.AreEqual(new Empty(), empty);
+ }
+
+ // This was originally seen as a conformance test failure.
+ [Test]
+ public void TruncatedMessageFieldThrows()
+ {
+ // 130, 3 is the message tag
+ // 1 is the data length - but there's no data.
+ var data = new byte[] { 130, 3, 1 };
+ Assert.Throws(() => TestAllTypes.Parser.ParseFrom(data));
+ }
+
+ ///
+ /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
+ /// for details; we may want to change this.
+ ///
+ [Test]
+ public void ExtraEndGroupThrows()
+ {
+ var message = SampleMessages.CreateFullTestAllTypes();
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+
+ output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(123);
+ output.WriteTag(100, WireFormat.WireType.EndGroup);
+
+ output.Flush();
+
+ stream.Position = 0;
+ Assert.Throws(() => TestAllTypes.Parser.ParseFrom(stream));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_DirectToStringCall()
+ {
+ var message = new ForeignMessage { C = 31 };
+ Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
+ Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_Nested()
+ {
+ var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_DirectToTextWriterCall()
+ {
+ var message = new ForeignMessage { C = 31 };
+ var writer = new StringWriter();
+ JsonFormatter.Default.Format(message, writer);
+ Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
+ }
+ }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
index a38d6b08b5..b4022a21cf 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/IssuesTest.cs
@@ -1,82 +1,82 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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 Google.Protobuf.Reflection;
-using UnitTest.Issues.TestProtos;
-using NUnit.Framework;
-
-
-namespace Google.Protobuf
-{
- ///
- /// Tests for issues which aren't easily compartmentalized into other unit tests.
- ///
- public class IssuesTest
- {
- // Issue 45
- [Test]
- public void FieldCalledItem()
- {
- ItemField message = new ItemField { Item = 3 };
- FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
- Assert.NotNull(field);
- Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
- }
-
- [Test]
- public void ReservedNames()
- {
- var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
- // Underscores aren't reflected in the JSON.
- Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
- }
-
- [Test]
- public void JsonNameParseTest()
- {
- var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
- var parser = new JsonParser(settings);
-
- // It is safe to use either original field name or explicitly specified json_name
- Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
- parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
- }
-
- [Test]
- public void JsonNameFormatTest()
- {
- var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
- Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
- JsonFormatter.Default.Format(message));
- }
- }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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 Google.Protobuf.Reflection;
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests for issues which aren't easily compartmentalized into other unit tests.
+ ///
+ public class IssuesTest
+ {
+ // Issue 45
+ [Test]
+ public void FieldCalledItem()
+ {
+ ItemField message = new ItemField { Item = 3 };
+ FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
+ Assert.NotNull(field);
+ Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
+ }
+
+ [Test]
+ public void ReservedNames()
+ {
+ var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
+ // Underscores aren't reflected in the JSON.
+ Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
+ }
+
+ [Test]
+ public void JsonNameParseTest()
+ {
+ var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
+ var parser = new JsonParser(settings);
+
+ // It is safe to use either original field name or explicitly specified json_name
+ Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
+ parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
+ }
+
+ [Test]
+ public void JsonNameFormatTest()
+ {
+ var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
+ Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
+ JsonFormatter.Default.Format(message));
+ }
+ }
+}
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
index 248f5fa913..859b49c2d6 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/TestCornerCases.cs
@@ -1,62 +1,62 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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 UnitTest.Issues.TestProtos;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class TestCornerCases
- {
- [Test]
- public void TestRoundTripNegativeEnums()
- {
- NegativeEnumMessage msg = new NegativeEnumMessage
- {
- Value = NegativeEnum.MinusOne,
- Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },
- PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }
- };
-
- Assert.AreEqual(58, msg.CalculateSize());
-
- byte[] bytes = new byte[58];
- CodedOutputStream output = new CodedOutputStream(bytes);
-
- msg.WriteTo(output);
- Assert.AreEqual(0, output.SpaceLeft);
-
- NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);
- Assert.AreEqual(msg, copy);
- }
- }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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 UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class TestCornerCases
+ {
+ [Test]
+ public void TestRoundTripNegativeEnums()
+ {
+ NegativeEnumMessage msg = new NegativeEnumMessage
+ {
+ Value = NegativeEnum.MinusOne,
+ Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow },
+ PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }
+ };
+
+ Assert.AreEqual(58, msg.CalculateSize());
+
+ byte[] bytes = new byte[58];
+ CodedOutputStream output = new CodedOutputStream(bytes);
+
+ msg.WriteTo(output);
+ Assert.AreEqual(0, output.SpaceLeft);
+
+ NegativeEnumMessage copy = NegativeEnumMessage.Parser.ParseFrom(bytes);
+ Assert.AreEqual(msg, copy);
+ }
+ }
+}
diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh
index b663138d10..b021de2aca 100755
--- a/csharp/generate_protos.sh
+++ b/csharp/generate_protos.sh
@@ -13,7 +13,9 @@ pushd $(dirname $0)/..
# Windows and Unix.
if [ -z "$PROTOC" ]; then
# TODO(jonskeet): Use an array and a for loop instead?
- if [ -x cmake/build/Debug/protoc.exe ]; then
+ if [ -x solution/Debug/protoc.exe ]; then
+ PROTOC=solution/Debug/protoc.exe
+ elif [ -x cmake/build/Debug/protoc.exe ]; then
PROTOC=cmake/build/Debug/protoc.exe
elif [ -x cmake/build/Release/protoc.exe ]; then
PROTOC=cmake/build/Release/protoc.exe
diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto
index 388998f0a0..f46c20e4da 100644
--- a/csharp/protos/unittest_issues.proto
+++ b/csharp/protos/unittest_issues.proto
@@ -156,3 +156,17 @@ message MixedRegularAndOptional {
string regular_field = 1;
optional string optional_field = 2;
}
+
+message OneofWithNoneField {
+ oneof test {
+ string x = 1;
+ string none = 2;
+ }
+}
+
+message OneofWithNoneName {
+ oneof none {
+ string x = 1;
+ string y = 2;
+ }
+}
\ No newline at end of file
diff --git a/csharp/src/AddressBook/AddPerson.cs b/csharp/src/AddressBook/AddPerson.cs
index 62d1788d55..889d1d060c 100644
--- a/csharp/src/AddressBook/AddPerson.cs
+++ b/csharp/src/AddressBook/AddPerson.cs
@@ -1,132 +1,132 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-
-namespace Google.Protobuf.Examples.AddressBook
-{
- internal class AddPerson
- {
- ///
- /// Builds a person based on user input
- ///
- private static Person PromptForAddress(TextReader input, TextWriter output)
- {
- Person person = new Person();
-
- output.Write("Enter person ID: ");
- person.Id = int.Parse(input.ReadLine());
-
- output.Write("Enter name: ");
- person.Name = input.ReadLine();
-
- output.Write("Enter email address (blank for none): ");
- string email = input.ReadLine();
- if (email.Length > 0)
- {
- person.Email = email;
- }
-
- while (true)
- {
- output.Write("Enter a phone number (or leave blank to finish): ");
- string number = input.ReadLine();
- if (number.Length == 0)
- {
- break;
- }
-
- Person.Types.PhoneNumber phoneNumber = new Person.Types.PhoneNumber { Number = number };
-
- output.Write("Is this a mobile, home, or work phone? ");
- String type = input.ReadLine();
- switch (type)
- {
- case "mobile":
- phoneNumber.Type = Person.Types.PhoneType.Mobile;
- break;
- case "home":
- phoneNumber.Type = Person.Types.PhoneType.Home;
- break;
- case "work":
- phoneNumber.Type = Person.Types.PhoneType.Work;
- break;
- default:
- output.Write("Unknown phone type. Using default.");
- break;
- }
-
- person.Phones.Add(phoneNumber);
- }
- return person;
- }
-
- ///
- /// Entry point - loads an existing addressbook or creates a new one,
- /// then writes it back to the file.
- ///
- public static int Main(string[] args)
- {
- if (args.Length != 1)
- {
- Console.Error.WriteLine("Usage: AddPerson ADDRESS_BOOK_FILE");
- return -1;
- }
-
- AddressBook addressBook;
-
- if (File.Exists(args[0]))
- {
- using (Stream file = File.OpenRead(args[0]))
- {
- addressBook = AddressBook.Parser.ParseFrom(file);
- }
- }
- else
- {
- Console.WriteLine("{0}: File not found. Creating a new file.", args[0]);
- addressBook = new AddressBook();
- }
-
- // Add an address.
- addressBook.People.Add(PromptForAddress(Console.In, Console.Out));
-
- // Write the new address book back to disk.
- using (Stream output = File.OpenWrite(args[0]))
- {
- addressBook.WriteTo(output);
- }
- return 0;
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+ internal class AddPerson
+ {
+ ///
+ /// Builds a person based on user input
+ ///
+ private static Person PromptForAddress(TextReader input, TextWriter output)
+ {
+ Person person = new Person();
+
+ output.Write("Enter person ID: ");
+ person.Id = int.Parse(input.ReadLine());
+
+ output.Write("Enter name: ");
+ person.Name = input.ReadLine();
+
+ output.Write("Enter email address (blank for none): ");
+ string email = input.ReadLine();
+ if (email.Length > 0)
+ {
+ person.Email = email;
+ }
+
+ while (true)
+ {
+ output.Write("Enter a phone number (or leave blank to finish): ");
+ string number = input.ReadLine();
+ if (number.Length == 0)
+ {
+ break;
+ }
+
+ Person.Types.PhoneNumber phoneNumber = new Person.Types.PhoneNumber { Number = number };
+
+ output.Write("Is this a mobile, home, or work phone? ");
+ String type = input.ReadLine();
+ switch (type)
+ {
+ case "mobile":
+ phoneNumber.Type = Person.Types.PhoneType.Mobile;
+ break;
+ case "home":
+ phoneNumber.Type = Person.Types.PhoneType.Home;
+ break;
+ case "work":
+ phoneNumber.Type = Person.Types.PhoneType.Work;
+ break;
+ default:
+ output.Write("Unknown phone type. Using default.");
+ break;
+ }
+
+ person.Phones.Add(phoneNumber);
+ }
+ return person;
+ }
+
+ ///
+ /// Entry point - loads an existing addressbook or creates a new one,
+ /// then writes it back to the file.
+ ///
+ public static int Main(string[] args)
+ {
+ if (args.Length != 1)
+ {
+ Console.Error.WriteLine("Usage: AddPerson ADDRESS_BOOK_FILE");
+ return -1;
+ }
+
+ AddressBook addressBook;
+
+ if (File.Exists(args[0]))
+ {
+ using (Stream file = File.OpenRead(args[0]))
+ {
+ addressBook = AddressBook.Parser.ParseFrom(file);
+ }
+ }
+ else
+ {
+ Console.WriteLine("{0}: File not found. Creating a new file.", args[0]);
+ addressBook = new AddressBook();
+ }
+
+ // Add an address.
+ addressBook.People.Add(PromptForAddress(Console.In, Console.Out));
+
+ // Write the new address book back to disk.
+ using (Stream output = File.OpenWrite(args[0]))
+ {
+ addressBook.WriteTo(output);
+ }
+ return 0;
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/AddressBook/ListPeople.cs b/csharp/src/AddressBook/ListPeople.cs
index 3758c1bce5..fdcd64d267 100644
--- a/csharp/src/AddressBook/ListPeople.cs
+++ b/csharp/src/AddressBook/ListPeople.cs
@@ -1,99 +1,99 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-
-namespace Google.Protobuf.Examples.AddressBook
-{
- internal class ListPeople
- {
- ///
- /// Iterates though all people in the AddressBook and prints info about them.
- ///
- private static void Print(AddressBook addressBook)
- {
- foreach (Person person in addressBook.People)
- {
- Console.WriteLine("Person ID: {0}", person.Id);
- Console.WriteLine(" Name: {0}", person.Name);
- if (person.Email != "")
- {
- Console.WriteLine(" E-mail address: {0}", person.Email);
- }
-
- foreach (Person.Types.PhoneNumber phoneNumber in person.Phones)
- {
- switch (phoneNumber.Type)
- {
- case Person.Types.PhoneType.Mobile:
- Console.Write(" Mobile phone #: ");
- break;
- case Person.Types.PhoneType.Home:
- Console.Write(" Home phone #: ");
- break;
- case Person.Types.PhoneType.Work:
- Console.Write(" Work phone #: ");
- break;
- }
- Console.WriteLine(phoneNumber.Number);
- }
- }
- }
-
- ///
- /// Entry point - loads the addressbook and then displays it.
- ///
- public static int Main(string[] args)
- {
- if (args.Length != 1)
- {
- Console.Error.WriteLine("Usage: ListPeople ADDRESS_BOOK_FILE");
- return 1;
- }
-
- if (!File.Exists(args[0]))
- {
- Console.WriteLine("{0} doesn't exist. Add a person to create the file first.", args[0]);
- return 0;
- }
-
- // Read the existing address book.
- using (Stream stream = File.OpenRead(args[0]))
- {
- AddressBook addressBook = AddressBook.Parser.ParseFrom(stream);
- Print(addressBook);
- }
- return 0;
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+ internal class ListPeople
+ {
+ ///
+ /// Iterates though all people in the AddressBook and prints info about them.
+ ///
+ private static void Print(AddressBook addressBook)
+ {
+ foreach (Person person in addressBook.People)
+ {
+ Console.WriteLine("Person ID: {0}", person.Id);
+ Console.WriteLine(" Name: {0}", person.Name);
+ if (person.Email != "")
+ {
+ Console.WriteLine(" E-mail address: {0}", person.Email);
+ }
+
+ foreach (Person.Types.PhoneNumber phoneNumber in person.Phones)
+ {
+ switch (phoneNumber.Type)
+ {
+ case Person.Types.PhoneType.Mobile:
+ Console.Write(" Mobile phone #: ");
+ break;
+ case Person.Types.PhoneType.Home:
+ Console.Write(" Home phone #: ");
+ break;
+ case Person.Types.PhoneType.Work:
+ Console.Write(" Work phone #: ");
+ break;
+ }
+ Console.WriteLine(phoneNumber.Number);
+ }
+ }
+ }
+
+ ///
+ /// Entry point - loads the addressbook and then displays it.
+ ///
+ public static int Main(string[] args)
+ {
+ if (args.Length != 1)
+ {
+ Console.Error.WriteLine("Usage: ListPeople ADDRESS_BOOK_FILE");
+ return 1;
+ }
+
+ if (!File.Exists(args[0]))
+ {
+ Console.WriteLine("{0} doesn't exist. Add a person to create the file first.", args[0]);
+ return 0;
+ }
+
+ // Read the existing address book.
+ using (Stream stream = File.OpenRead(args[0]))
+ {
+ AddressBook addressBook = AddressBook.Parser.ParseFrom(stream);
+ Print(addressBook);
+ }
+ return 0;
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/AddressBook/Program.cs b/csharp/src/AddressBook/Program.cs
index de4867a0ce..5bbe84cd52 100644
--- a/csharp/src/AddressBook/Program.cs
+++ b/csharp/src/AddressBook/Program.cs
@@ -1,95 +1,95 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-
-namespace Google.Protobuf.Examples.AddressBook
-{
- ///
- /// Entry point. Repeatedly prompts user for an action to take, delegating actual behaviour
- /// to individual actions. Each action has its own Main method, so that it can be used as an
- /// individual complete program.
- ///
- internal class Program
- {
- private static int Main(string[] args)
- {
- if (args.Length > 1)
- {
- Console.Error.WriteLine("Usage: AddressBook [file]");
- Console.Error.WriteLine("If the filename isn't specified, \"addressbook.data\" is used instead.");
- return 1;
- }
- string addressBookFile = args.Length > 0 ? args[0] : "addressbook.data";
-
- bool stopping = false;
- while (!stopping)
- {
- Console.WriteLine("Options:");
- Console.WriteLine(" L: List contents");
- Console.WriteLine(" A: Add new person");
- Console.WriteLine(" Q: Quit");
- Console.Write("Action? ");
- Console.Out.Flush();
- char choice = Console.ReadKey().KeyChar;
- Console.WriteLine();
- try
- {
- switch (choice)
- {
- case 'A':
- case 'a':
- AddPerson.Main(new string[] {addressBookFile});
- break;
- case 'L':
- case 'l':
- ListPeople.Main(new string[] {addressBookFile});
- break;
- case 'Q':
- case 'q':
- stopping = true;
- break;
- default:
- Console.WriteLine("Unknown option: {0}", choice);
- break;
- }
- }
- catch (Exception e)
- {
- Console.WriteLine("Exception executing action: {0}", e);
- }
- Console.WriteLine();
- }
- return 0;
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+ ///
+ /// Entry point. Repeatedly prompts user for an action to take, delegating actual behaviour
+ /// to individual actions. Each action has its own Main method, so that it can be used as an
+ /// individual complete program.
+ ///
+ internal class Program
+ {
+ private static int Main(string[] args)
+ {
+ if (args.Length > 1)
+ {
+ Console.Error.WriteLine("Usage: AddressBook [file]");
+ Console.Error.WriteLine("If the filename isn't specified, \"addressbook.data\" is used instead.");
+ return 1;
+ }
+ string addressBookFile = args.Length > 0 ? args[0] : "addressbook.data";
+
+ bool stopping = false;
+ while (!stopping)
+ {
+ Console.WriteLine("Options:");
+ Console.WriteLine(" L: List contents");
+ Console.WriteLine(" A: Add new person");
+ Console.WriteLine(" Q: Quit");
+ Console.Write("Action? ");
+ Console.Out.Flush();
+ char choice = Console.ReadKey().KeyChar;
+ Console.WriteLine();
+ try
+ {
+ switch (choice)
+ {
+ case 'A':
+ case 'a':
+ AddPerson.Main(new string[] {addressBookFile});
+ break;
+ case 'L':
+ case 'l':
+ ListPeople.Main(new string[] {addressBookFile});
+ break;
+ case 'Q':
+ case 'q':
+ stopping = true;
+ break;
+ default:
+ Console.WriteLine("Unknown option: {0}", choice);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Exception executing action: {0}", e);
+ }
+ Console.WriteLine();
+ }
+ return 0;
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/AddressBook/SampleUsage.cs b/csharp/src/AddressBook/SampleUsage.cs
index 941d865aa4..8f08f0f148 100644
--- a/csharp/src/AddressBook/SampleUsage.cs
+++ b/csharp/src/AddressBook/SampleUsage.cs
@@ -1,73 +1,73 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-
-namespace Google.Protobuf.Examples.AddressBook
-{
- internal class SampleUsage
- {
- private static void Main()
- {
- byte[] bytes;
- // Create a new person
- Person person = new Person
- {
- Id = 1,
- Name = "Foo",
- Email = "foo@bar",
- Phones = { new Person.Types.PhoneNumber { Number = "555-1212" } }
- };
- using (MemoryStream stream = new MemoryStream())
- {
- // Save the person to a stream
- person.WriteTo(stream);
- bytes = stream.ToArray();
- }
- Person copy = Person.Parser.ParseFrom(bytes);
-
- AddressBook book = new AddressBook
- {
- People = { copy }
- };
- bytes = book.ToByteArray();
- // And read the address book back again
- AddressBook restored = AddressBook.Parser.ParseFrom(bytes);
- // The message performs a deep-comparison on equality:
- if (restored.People.Count != 1 || !person.Equals(restored.People[0]))
- {
- throw new Exception("There is a bad person in here!");
- }
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+
+namespace Google.Protobuf.Examples.AddressBook
+{
+ internal class SampleUsage
+ {
+ private static void Main()
+ {
+ byte[] bytes;
+ // Create a new person
+ Person person = new Person
+ {
+ Id = 1,
+ Name = "Foo",
+ Email = "foo@bar",
+ Phones = { new Person.Types.PhoneNumber { Number = "555-1212" } }
+ };
+ using (MemoryStream stream = new MemoryStream())
+ {
+ // Save the person to a stream
+ person.WriteTo(stream);
+ bytes = stream.ToArray();
+ }
+ Person copy = Person.Parser.ParseFrom(bytes);
+
+ AddressBook book = new AddressBook
+ {
+ People = { copy }
+ };
+ bytes = book.ToByteArray();
+ // And read the address book back again
+ AddressBook restored = AddressBook.Parser.ParseFrom(bytes);
+ // The message performs a deep-comparison on equality:
+ if (restored.People.Count != 1 || !person.Equals(restored.People[0]))
+ {
+ throw new Exception("There is a bad person in here!");
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj b/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
index ac8e009adc..fee5f65257 100644
--- a/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
+++ b/csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
@@ -2,7 +2,7 @@
Exe
- net5.0
+ net6.0
../../keys/Google.Protobuf.snk
true
False
@@ -15,7 +15,7 @@
-
+
diff --git a/csharp/src/Google.Protobuf.JsonDump/Program.cs b/csharp/src/Google.Protobuf.JsonDump/Program.cs
index 296b2f3f1e..56e32ad86e 100644
--- a/csharp/src/Google.Protobuf.JsonDump/Program.cs
+++ b/csharp/src/Google.Protobuf.JsonDump/Program.cs
@@ -1,73 +1,73 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-using System.Reflection;
-
-namespace Google.Protobuf.ProtoDump
-{
- ///
- /// Small utility to load a binary message and dump it in JSON format.
- ///
- internal class Program
- {
- private static int Main(string[] args)
- {
- if (args.Length != 2)
- {
- Console.Error.WriteLine("Usage: Google.Protobuf.JsonDump ");
- Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
- Console.Error.WriteLine("including assembly e.g. ProjectNamespace.Message,Company.Project");
- return 1;
- }
- Type type = Type.GetType(args[0]);
- if (type == null)
- {
- Console.Error.WriteLine("Unable to load type {0}.", args[0]);
- return 1;
- }
- if (!typeof(IMessage).GetTypeInfo().IsAssignableFrom(type))
- {
- Console.Error.WriteLine("Type {0} doesn't implement IMessage.", args[0]);
- return 1;
- }
- IMessage message = (IMessage) Activator.CreateInstance(type);
- using (var input = File.OpenRead(args[1]))
- {
- message.MergeFrom(input);
- }
- Console.WriteLine(message);
- return 0;
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+using System.Reflection;
+
+namespace Google.Protobuf.ProtoDump
+{
+ ///
+ /// Small utility to load a binary message and dump it in JSON format.
+ ///
+ internal class Program
+ {
+ private static int Main(string[] args)
+ {
+ if (args.Length != 2)
+ {
+ Console.Error.WriteLine("Usage: Google.Protobuf.JsonDump ");
+ Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
+ Console.Error.WriteLine("including assembly e.g. ProjectNamespace.Message,Company.Project");
+ return 1;
+ }
+ Type type = Type.GetType(args[0]);
+ if (type == null)
+ {
+ Console.Error.WriteLine("Unable to load type {0}.", args[0]);
+ return 1;
+ }
+ if (!typeof(IMessage).GetTypeInfo().IsAssignableFrom(type))
+ {
+ Console.Error.WriteLine("Type {0} doesn't implement IMessage.", args[0]);
+ return 1;
+ }
+ IMessage message = (IMessage) Activator.CreateInstance(type);
+ using (var input = File.OpenRead(args[1]))
+ {
+ message.MergeFrom(input);
+ }
+ Console.WriteLine(message);
+ return 0;
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj b/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
index ad8445b7bf..8d9d64b37e 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/Google.Protobuf.Test.TestProtos.csproj
@@ -1,25 +1,25 @@
-
-
-
-
- net462;netstandard1.1;netstandard2.0
- 3.0
- ../../keys/Google.Protobuf.snk
- true
- False
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ net462;netstandard1.1;netstandard2.0
+ 3.0
+ ../../keys/Google.Protobuf.snk
+ true
+ False
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
index 71e803c3f6..5a2aa3bd5f 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
@@ -226,7 +226,7 @@ namespace ProtobufTestMessages.Proto3 {
"dG8zLkZvcmVpZ25FbnVtOgI4ASI5CgpOZXN0ZWRFbnVtEgcKA0ZPTxAAEgcK",
"A0JBUhABEgcKA0JBWhACEhAKA05FRxD///////////8BIlkKC0FsaWFzZWRF",
"bnVtEg0KCUFMSUFTX0ZPTxAAEg0KCUFMSUFTX0JBUhABEg0KCUFMSUFTX0JB",
- "WhACEgcKA1FVWBACEgcKA3F1eBACEgcKA2JBehACGgIQAUINCgtvbmVvZl9m",
+ "WhACEgcKA01PTxACEgcKA21vbxACEgcKA2JBehACGgIQAUINCgtvbmVvZl9m",
"aWVsZEoGCPUDEP8DIhsKDkZvcmVpZ25NZXNzYWdlEgkKAWMYASABKAUiFgoU",
"TnVsbEh5cG90aGVzaXNQcm90bzMiLwoORW51bU9ubHlQcm90bzMiHQoEQm9v",
"bBIKCgZrRmFsc2UQABIJCgVrVHJ1ZRABKkAKC0ZvcmVpZ25FbnVtEg8KC0ZP",
@@ -5432,8 +5432,8 @@ namespace ProtobufTestMessages.Proto3 {
[pbr::OriginalName("ALIAS_FOO")] AliasFoo = 0,
[pbr::OriginalName("ALIAS_BAR")] AliasBar = 1,
[pbr::OriginalName("ALIAS_BAZ")] AliasBaz = 2,
- [pbr::OriginalName("QUX", PreferredAlias = false)] Qux = 2,
- [pbr::OriginalName("qux", PreferredAlias = false)] Qux_ = 2,
+ [pbr::OriginalName("MOO", PreferredAlias = false)] Moo = 2,
+ [pbr::OriginalName("moo", PreferredAlias = false)] Moo_ = 2,
[pbr::OriginalName("bAz", PreferredAlias = false)] BAz = 2,
}
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
index ec4c07b112..3440105514 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
@@ -54,11 +54,13 @@ namespace UnitTest.Issues.TestProtos {
"dEluT25lb2YSLgoKbnVsbF92YWx1ZRgCIAEoDjIaLmdvb2dsZS5wcm90b2J1",
"Zi5OdWxsVmFsdWUiYAoXTWl4ZWRSZWd1bGFyQW5kT3B0aW9uYWwSFQoNcmVn",
"dWxhcl9maWVsZBgBIAEoCRIbCg5vcHRpb25hbF9maWVsZBgCIAEoCUgAiAEB",
- "QhEKD19vcHRpb25hbF9maWVsZCpVCgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJ",
- "VkVfRU5VTV9aRVJPEAASFgoJRml2ZUJlbG93EPv//////////wESFQoITWlu",
- "dXNPbmUQ////////////ASouCg5EZXByZWNhdGVkRW51bRITCg9ERVBSRUNB",
- "VEVEX1pFUk8QABIHCgNvbmUQAUIdqgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQ",
- "cm90b3NiBnByb3RvMw=="));
+ "QhEKD19vcHRpb25hbF9maWVsZCI5ChJPbmVvZldpdGhOb25lRmllbGQSCwoB",
+ "eBgBIAEoCUgAEg4KBG5vbmUYAiABKAlIAEIGCgR0ZXN0IjUKEU9uZW9mV2l0",
+ "aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSpV",
+ "CgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJVkVfRU5VTV9aRVJPEAASFgoJRml2",
+ "ZUJlbG93EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5E",
+ "ZXByZWNhdGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUId",
+ "qgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
@@ -73,7 +75,9 @@ namespace UnitTest.Issues.TestProtos {
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging), global::UnitTest.Issues.TestProtos.OneofMerging.Parser, new[]{ "Text", "Nested" }, new[]{ "Value" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested), global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested.Parser, new[]{ "X", "Y" }, null, null, null, null)}),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueOutsideStruct), global::UnitTest.Issues.TestProtos.NullValueOutsideStruct.Parser, new[]{ "StringValue", "NullValue" }, new[]{ "Value" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueNotInOneof), global::UnitTest.Issues.TestProtos.NullValueNotInOneof.Parser, new[]{ "NullValue" }, null, null, null, null),
- new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneField), global::UnitTest.Issues.TestProtos.OneofWithNoneField.Parser, new[]{ "X", "None" }, new[]{ "Test" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneName), global::UnitTest.Issues.TestProtos.OneofWithNoneName.Parser, new[]{ "X", "Y" }, new[]{ "None" }, null, null, null)
}));
}
#endregion
@@ -3825,6 +3829,524 @@ namespace UnitTest.Issues.TestProtos {
}
+ public sealed partial class OneofWithNoneField : pb::IMessage
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+ #endif
+ {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new OneofWithNoneField());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[12]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneField() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneField(OneofWithNoneField other) : this() {
+ switch (other.TestCase) {
+ case TestOneofCase.X:
+ X = other.X;
+ break;
+ case TestOneofCase.None_:
+ None = other.None;
+ break;
+ }
+
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneField Clone() {
+ return new OneofWithNoneField(this);
+ }
+
+ /// Field number for the "x" field.
+ public const int XFieldNumber = 1;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string X {
+ get { return testCase_ == TestOneofCase.X ? (string) test_ : ""; }
+ set {
+ test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ testCase_ = TestOneofCase.X;
+ }
+ }
+
+ /// Field number for the "none" field.
+ public const int NoneFieldNumber = 2;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string None {
+ get { return testCase_ == TestOneofCase.None_ ? (string) test_ : ""; }
+ set {
+ test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ testCase_ = TestOneofCase.None_;
+ }
+ }
+
+ private object test_;
+ /// Enum of possible cases for the "test" oneof.
+ public enum TestOneofCase {
+ None = 0,
+ X = 1,
+ None_ = 2,
+ }
+ private TestOneofCase testCase_ = TestOneofCase.None;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public TestOneofCase TestCase {
+ get { return testCase_; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void ClearTest() {
+ testCase_ = TestOneofCase.None;
+ test_ = null;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as OneofWithNoneField);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(OneofWithNoneField other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (X != other.X) return false;
+ if (None != other.None) return false;
+ if (TestCase != other.TestCase) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (testCase_ == TestOneofCase.X) hash ^= X.GetHashCode();
+ if (testCase_ == TestOneofCase.None_) hash ^= None.GetHashCode();
+ hash ^= (int) testCase_;
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ if (testCase_ == TestOneofCase.X) {
+ output.WriteRawTag(10);
+ output.WriteString(X);
+ }
+ if (testCase_ == TestOneofCase.None_) {
+ output.WriteRawTag(18);
+ output.WriteString(None);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ if (testCase_ == TestOneofCase.X) {
+ output.WriteRawTag(10);
+ output.WriteString(X);
+ }
+ if (testCase_ == TestOneofCase.None_) {
+ output.WriteRawTag(18);
+ output.WriteString(None);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ if (testCase_ == TestOneofCase.X) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
+ }
+ if (testCase_ == TestOneofCase.None_) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(None);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(OneofWithNoneField other) {
+ if (other == null) {
+ return;
+ }
+ switch (other.TestCase) {
+ case TestOneofCase.X:
+ X = other.X;
+ break;
+ case TestOneofCase.None_:
+ None = other.None;
+ break;
+ }
+
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ X = input.ReadString();
+ break;
+ }
+ case 18: {
+ None = input.ReadString();
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 10: {
+ X = input.ReadString();
+ break;
+ }
+ case 18: {
+ None = input.ReadString();
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ }
+
+ public sealed partial class OneofWithNoneName : pb::IMessage
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+ #endif
+ {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new OneofWithNoneName());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[13]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneName() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneName(OneofWithNoneName other) : this() {
+ switch (other.NoneCase) {
+ case NoneOneofCase.X:
+ X = other.X;
+ break;
+ case NoneOneofCase.Y:
+ Y = other.Y;
+ break;
+ }
+
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public OneofWithNoneName Clone() {
+ return new OneofWithNoneName(this);
+ }
+
+ /// Field number for the "x" field.
+ public const int XFieldNumber = 1;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string X {
+ get { return noneCase_ == NoneOneofCase.X ? (string) none_ : ""; }
+ set {
+ none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ noneCase_ = NoneOneofCase.X;
+ }
+ }
+
+ /// Field number for the "y" field.
+ public const int YFieldNumber = 2;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public string Y {
+ get { return noneCase_ == NoneOneofCase.Y ? (string) none_ : ""; }
+ set {
+ none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ noneCase_ = NoneOneofCase.Y;
+ }
+ }
+
+ private object none_;
+ /// Enum of possible cases for the "none" oneof.
+ public enum NoneOneofCase {
+ None = 0,
+ X = 1,
+ Y = 2,
+ }
+ private NoneOneofCase noneCase_ = NoneOneofCase.None;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public NoneOneofCase NoneCase {
+ get { return noneCase_; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void ClearNone() {
+ noneCase_ = NoneOneofCase.None;
+ none_ = null;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as OneofWithNoneName);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(OneofWithNoneName other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (X != other.X) return false;
+ if (Y != other.Y) return false;
+ if (NoneCase != other.NoneCase) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (noneCase_ == NoneOneofCase.X) hash ^= X.GetHashCode();
+ if (noneCase_ == NoneOneofCase.Y) hash ^= Y.GetHashCode();
+ hash ^= (int) noneCase_;
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ if (noneCase_ == NoneOneofCase.X) {
+ output.WriteRawTag(10);
+ output.WriteString(X);
+ }
+ if (noneCase_ == NoneOneofCase.Y) {
+ output.WriteRawTag(18);
+ output.WriteString(Y);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ if (noneCase_ == NoneOneofCase.X) {
+ output.WriteRawTag(10);
+ output.WriteString(X);
+ }
+ if (noneCase_ == NoneOneofCase.Y) {
+ output.WriteRawTag(18);
+ output.WriteString(Y);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ if (noneCase_ == NoneOneofCase.X) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
+ }
+ if (noneCase_ == NoneOneofCase.Y) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(Y);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(OneofWithNoneName other) {
+ if (other == null) {
+ return;
+ }
+ switch (other.NoneCase) {
+ case NoneOneofCase.X:
+ X = other.X;
+ break;
+ case NoneOneofCase.Y:
+ Y = other.Y;
+ break;
+ }
+
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ X = input.ReadString();
+ break;
+ }
+ case 18: {
+ Y = input.ReadString();
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 10: {
+ X = input.ReadString();
+ break;
+ }
+ case 18: {
+ Y = input.ReadString();
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ }
+
#endregion
}
diff --git a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
index 04d68b5bdc..4876e09798 100644
--- a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
@@ -1,439 +1,439 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.Text;
-using NUnit.Framework;
-using System.IO;
-using System.Collections.Generic;
-using System.Collections;
-using System.Linq;
-using System.Buffers;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Runtime.CompilerServices;
-#if !NET35
-using System.Threading.Tasks;
-#endif
-
-namespace Google.Protobuf
-{
- public class ByteStringTest
- {
- [Test]
- public void Equality()
- {
- ByteString b1 = ByteString.CopyFrom(1, 2, 3);
- ByteString b2 = ByteString.CopyFrom(1, 2, 3);
- ByteString b3 = ByteString.CopyFrom(1, 2, 4);
- ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
- EqualityTester.AssertEquality(b1, b1);
- EqualityTester.AssertEquality(b1, b2);
- EqualityTester.AssertInequality(b1, b3);
- EqualityTester.AssertInequality(b1, b4);
- EqualityTester.AssertInequality(b1, null);
- EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);
-#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
- Assert.IsTrue(b1 == b1);
- Assert.IsTrue(b1 == b2);
- Assert.IsFalse(b1 == b3);
- Assert.IsFalse(b1 == b4);
- Assert.IsFalse(b1 == null);
- Assert.IsTrue((ByteString) null == null);
- Assert.IsFalse(b1 != b1);
- Assert.IsFalse(b1 != b2);
- Assert.IsTrue(ByteString.Empty == ByteString.Empty);
-#pragma warning disable 1718
- Assert.IsTrue(b1 != b3);
- Assert.IsTrue(b1 != b4);
- Assert.IsTrue(b1 != null);
- Assert.IsFalse((ByteString) null != null);
- }
-
- [Test]
- public void EmptyByteStringHasZeroSize()
- {
- Assert.AreEqual(0, ByteString.Empty.Length);
- }
-
- [Test]
- public void CopyFromStringWithExplicitEncoding()
- {
- ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
- Assert.AreEqual(4, bs.Length);
- Assert.AreEqual(65, bs[0]);
- Assert.AreEqual(0, bs[1]);
- Assert.AreEqual(66, bs[2]);
- Assert.AreEqual(0, bs[3]);
- }
-
- [Test]
- public void IsEmptyWhenEmpty()
- {
- Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
- }
-
- [Test]
- public void IsEmptyWhenNotEmpty()
- {
- Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
- }
-
- [Test]
- public void CopyFromByteArrayCopiesContents()
- {
- byte[] data = new byte[1];
- data[0] = 10;
- ByteString bs = ByteString.CopyFrom(data);
- Assert.AreEqual(10, bs[0]);
- data[0] = 5;
- Assert.AreEqual(10, bs[0]);
- }
-
- [Test]
- public void CopyFromReadOnlySpanCopiesContents()
- {
- byte[] data = new byte[1];
- data[0] = 10;
- ReadOnlySpan byteSpan = data;
- var bs = ByteString.CopyFrom(byteSpan);
- Assert.AreEqual(10, bs[0]);
- data[0] = 5;
- Assert.AreEqual(10, bs[0]);
- }
-
- [Test]
- public void ToByteArrayCopiesContents()
- {
- ByteString bs = ByteString.CopyFromUtf8("Hello");
- byte[] data = bs.ToByteArray();
- Assert.AreEqual((byte)'H', data[0]);
- Assert.AreEqual((byte)'H', bs[0]);
- data[0] = 0;
- Assert.AreEqual(0, data[0]);
- Assert.AreEqual((byte)'H', bs[0]);
- }
-
- [Test]
- public void CopyFromUtf8UsesUtf8()
- {
- ByteString bs = ByteString.CopyFromUtf8("\u20ac");
- Assert.AreEqual(3, bs.Length);
- Assert.AreEqual(0xe2, bs[0]);
- Assert.AreEqual(0x82, bs[1]);
- Assert.AreEqual(0xac, bs[2]);
- }
-
- [Test]
- public void CopyFromPortion()
- {
- byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
- ByteString bs = ByteString.CopyFrom(data, 2, 3);
- Assert.AreEqual(3, bs.Length);
- Assert.AreEqual(2, bs[0]);
- Assert.AreEqual(3, bs[1]);
- }
-
- [Test]
- public void CopyTo()
- {
- byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
- ByteString bs = ByteString.CopyFrom(data);
-
- byte[] dest = new byte[data.Length];
- bs.CopyTo(dest, 0);
-
- CollectionAssert.AreEqual(data, dest);
- }
-
- [Test]
- public void GetEnumerator()
- {
- byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
- ByteString bs = ByteString.CopyFrom(data);
-
- IEnumerator genericEnumerator = bs.GetEnumerator();
- Assert.IsTrue(genericEnumerator.MoveNext());
- Assert.AreEqual(0, genericEnumerator.Current);
-
- IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
- Assert.IsTrue(enumerator.MoveNext());
- Assert.AreEqual(0, enumerator.Current);
-
- // Call via LINQ
- CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
- }
-
- [Test]
- public void UnsafeWrap()
- {
- byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
- ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
- ReadOnlySpan s = bs.Span;
-
- Assert.AreEqual(3, s.Length);
- Assert.AreEqual(2, s[0]);
- Assert.AreEqual(3, s[1]);
- Assert.AreEqual(4, s[2]);
-
- // Check that the value is not a copy
- data[2] = byte.MaxValue;
- Assert.AreEqual(byte.MaxValue, s[0]);
- }
-
- [Test]
- public void CreateCodedInput_FromArraySegment()
- {
- byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
- ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
- CodedInputStream codedInputStream = bs.CreateCodedInput();
-
- byte[] bytes = codedInputStream.ReadRawBytes(3);
-
- Assert.AreEqual(3, bytes.Length);
- Assert.AreEqual(2, bytes[0]);
- Assert.AreEqual(3, bytes[1]);
- Assert.AreEqual(4, bytes[2]);
- Assert.IsTrue(codedInputStream.IsAtEnd);
- }
-
- [Test]
- public void WriteToStream()
- {
- byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
- ByteString bs = ByteString.CopyFrom(data);
-
- MemoryStream ms = new MemoryStream();
- bs.WriteTo(ms);
-
- CollectionAssert.AreEqual(data, ms.ToArray());
- }
-
- [Test]
- public void WriteToStream_Stackalloc()
- {
- byte[] data = Encoding.UTF8.GetBytes("Hello world");
- Span s = stackalloc byte[data.Length];
- data.CopyTo(s);
-
- MemoryStream ms = new MemoryStream();
-
- using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
- {
- ByteString bs = ByteString.AttachBytes(manager.Memory);
-
- bs.WriteTo(ms);
- }
-
- CollectionAssert.AreEqual(data, ms.ToArray());
- }
-
- [Test]
- public void ToStringUtf8()
- {
- ByteString bs = ByteString.CopyFromUtf8("\u20ac");
- Assert.AreEqual("\u20ac", bs.ToStringUtf8());
- }
-
- [Test]
- public void ToStringWithExplicitEncoding()
- {
- ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
- Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
- }
-
- [Test]
- public void ToString_Stackalloc()
- {
- byte[] data = Encoding.UTF8.GetBytes("Hello world");
- Span s = stackalloc byte[data.Length];
- data.CopyTo(s);
-
- using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
- {
- ByteString bs = ByteString.AttachBytes(manager.Memory);
-
- Assert.AreEqual("Hello world", bs.ToString(Encoding.UTF8));
- }
- }
-
- [Test]
- public void FromBase64_WithText()
- {
- byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
- string base64 = Convert.ToBase64String(data);
- ByteString bs = ByteString.FromBase64(base64);
- Assert.AreEqual(data, bs.ToByteArray());
- }
-
- [Test]
- public void FromBase64_Empty()
- {
- // Optimization which also fixes issue 61.
- Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
- }
-
- [Test]
- public void ToBase64_Array()
- {
- ByteString bs = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world"));
-
- Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
- }
-
- [Test]
- public void ToBase64_Stackalloc()
- {
- byte[] data = Encoding.UTF8.GetBytes("Hello world");
- Span s = stackalloc byte[data.Length];
- data.CopyTo(s);
-
- using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
- {
- ByteString bs = ByteString.AttachBytes(manager.Memory);
-
- Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
- }
- }
-
- [Test]
- public void FromStream_Seekable()
- {
- var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
- // Consume the first byte, just to test that it's "from current position"
- stream.ReadByte();
- var actual = ByteString.FromStream(stream);
- ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
- Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
- }
-
- [Test]
- public void FromStream_NotSeekable()
- {
- var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
- // Consume the first byte, just to test that it's "from current position"
- stream.ReadByte();
- // Wrap the original stream in LimitedInputStream, which has CanSeek=false
- var limitedStream = new LimitedInputStream(stream, 3);
- var actual = ByteString.FromStream(limitedStream);
- ByteString expected = ByteString.CopyFrom(2, 3, 4);
- Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
- }
-
-#if !NET35
- [Test]
- public async Task FromStreamAsync_Seekable()
- {
- var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
- // Consume the first byte, just to test that it's "from current position"
- stream.ReadByte();
- var actual = await ByteString.FromStreamAsync(stream);
- ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
- Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
- }
-
- [Test]
- public async Task FromStreamAsync_NotSeekable()
- {
- var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
- // Consume the first byte, just to test that it's "from current position"
- stream.ReadByte();
- // Wrap the original stream in LimitedInputStream, which has CanSeek=false
- var limitedStream = new LimitedInputStream(stream, 3);
- var actual = await ByteString.FromStreamAsync(limitedStream);
- ByteString expected = ByteString.CopyFrom(2, 3, 4);
- Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
- }
-#endif
-
- [Test]
- public void GetHashCode_Regression()
- {
- // We used to have an awful hash algorithm where only the last four
- // bytes were relevant. This is a regression test for
- // https://github.com/protocolbuffers/protobuf/issues/2511
-
- ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);
- ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);
- Assert.AreNotEqual(b1.GetHashCode(), b2.GetHashCode());
- }
-
- [Test]
- public void GetContentsAsReadOnlySpan()
- {
- var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
- var copied = byteString.Span.ToArray();
- CollectionAssert.AreEqual(byteString, copied);
- }
-
- [Test]
- public void GetContentsAsReadOnlyMemory()
- {
- var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
- var copied = byteString.Memory.ToArray();
- CollectionAssert.AreEqual(byteString, copied);
- }
-
- // Create Memory from non-array source.
- // Use by ByteString tests that have optimized path for array backed Memory.
- private sealed unsafe class UnmanagedMemoryManager : MemoryManager where T : unmanaged
- {
- private readonly T* _pointer;
- private readonly int _length;
-
- public UnmanagedMemoryManager(Span span)
- {
- fixed (T* ptr = &MemoryMarshal.GetReference(span))
- {
- _pointer = ptr;
- _length = span.Length;
- }
- }
-
- public override Span GetSpan() => new Span(_pointer, _length);
-
- public override MemoryHandle Pin(int elementIndex = 0)
- {
- if (elementIndex < 0 || elementIndex >= _length)
- {
- throw new ArgumentOutOfRangeException(nameof(elementIndex));
- }
-
- return new MemoryHandle(_pointer + elementIndex);
- }
-
- public override void Unpin() { }
-
- protected override void Dispose(bool disposing) { }
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.Text;
+using NUnit.Framework;
+using System.IO;
+using System.Collections.Generic;
+using System.Collections;
+using System.Linq;
+using System.Buffers;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Runtime.CompilerServices;
+#if !NET35
+using System.Threading.Tasks;
+#endif
+
+namespace Google.Protobuf
+{
+ public class ByteStringTest
+ {
+ [Test]
+ public void Equality()
+ {
+ ByteString b1 = ByteString.CopyFrom(1, 2, 3);
+ ByteString b2 = ByteString.CopyFrom(1, 2, 3);
+ ByteString b3 = ByteString.CopyFrom(1, 2, 4);
+ ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
+ EqualityTester.AssertEquality(b1, b1);
+ EqualityTester.AssertEquality(b1, b2);
+ EqualityTester.AssertInequality(b1, b3);
+ EqualityTester.AssertInequality(b1, b4);
+ EqualityTester.AssertInequality(b1, null);
+ EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);
+#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
+ Assert.IsTrue(b1 == b1);
+ Assert.IsTrue(b1 == b2);
+ Assert.IsFalse(b1 == b3);
+ Assert.IsFalse(b1 == b4);
+ Assert.IsFalse(b1 == null);
+ Assert.IsTrue((ByteString) null == null);
+ Assert.IsFalse(b1 != b1);
+ Assert.IsFalse(b1 != b2);
+ Assert.IsTrue(ByteString.Empty == ByteString.Empty);
+#pragma warning disable 1718
+ Assert.IsTrue(b1 != b3);
+ Assert.IsTrue(b1 != b4);
+ Assert.IsTrue(b1 != null);
+ Assert.IsFalse((ByteString) null != null);
+ }
+
+ [Test]
+ public void EmptyByteStringHasZeroSize()
+ {
+ Assert.AreEqual(0, ByteString.Empty.Length);
+ }
+
+ [Test]
+ public void CopyFromStringWithExplicitEncoding()
+ {
+ ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
+ Assert.AreEqual(4, bs.Length);
+ Assert.AreEqual(65, bs[0]);
+ Assert.AreEqual(0, bs[1]);
+ Assert.AreEqual(66, bs[2]);
+ Assert.AreEqual(0, bs[3]);
+ }
+
+ [Test]
+ public void IsEmptyWhenEmpty()
+ {
+ Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
+ }
+
+ [Test]
+ public void IsEmptyWhenNotEmpty()
+ {
+ Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
+ }
+
+ [Test]
+ public void CopyFromByteArrayCopiesContents()
+ {
+ byte[] data = new byte[1];
+ data[0] = 10;
+ ByteString bs = ByteString.CopyFrom(data);
+ Assert.AreEqual(10, bs[0]);
+ data[0] = 5;
+ Assert.AreEqual(10, bs[0]);
+ }
+
+ [Test]
+ public void CopyFromReadOnlySpanCopiesContents()
+ {
+ byte[] data = new byte[1];
+ data[0] = 10;
+ ReadOnlySpan byteSpan = data;
+ var bs = ByteString.CopyFrom(byteSpan);
+ Assert.AreEqual(10, bs[0]);
+ data[0] = 5;
+ Assert.AreEqual(10, bs[0]);
+ }
+
+ [Test]
+ public void ToByteArrayCopiesContents()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("Hello");
+ byte[] data = bs.ToByteArray();
+ Assert.AreEqual((byte)'H', data[0]);
+ Assert.AreEqual((byte)'H', bs[0]);
+ data[0] = 0;
+ Assert.AreEqual(0, data[0]);
+ Assert.AreEqual((byte)'H', bs[0]);
+ }
+
+ [Test]
+ public void CopyFromUtf8UsesUtf8()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+ Assert.AreEqual(3, bs.Length);
+ Assert.AreEqual(0xe2, bs[0]);
+ Assert.AreEqual(0x82, bs[1]);
+ Assert.AreEqual(0xac, bs[2]);
+ }
+
+ [Test]
+ public void CopyFromPortion()
+ {
+ byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+ ByteString bs = ByteString.CopyFrom(data, 2, 3);
+ Assert.AreEqual(3, bs.Length);
+ Assert.AreEqual(2, bs[0]);
+ Assert.AreEqual(3, bs[1]);
+ }
+
+ [Test]
+ public void CopyTo()
+ {
+ byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+ ByteString bs = ByteString.CopyFrom(data);
+
+ byte[] dest = new byte[data.Length];
+ bs.CopyTo(dest, 0);
+
+ CollectionAssert.AreEqual(data, dest);
+ }
+
+ [Test]
+ public void GetEnumerator()
+ {
+ byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+ ByteString bs = ByteString.CopyFrom(data);
+
+ IEnumerator genericEnumerator = bs.GetEnumerator();
+ Assert.IsTrue(genericEnumerator.MoveNext());
+ Assert.AreEqual(0, genericEnumerator.Current);
+
+ IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
+ Assert.IsTrue(enumerator.MoveNext());
+ Assert.AreEqual(0, enumerator.Current);
+
+ // Call via LINQ
+ CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
+ }
+
+ [Test]
+ public void UnsafeWrap()
+ {
+ byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+ ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
+ ReadOnlySpan s = bs.Span;
+
+ Assert.AreEqual(3, s.Length);
+ Assert.AreEqual(2, s[0]);
+ Assert.AreEqual(3, s[1]);
+ Assert.AreEqual(4, s[2]);
+
+ // Check that the value is not a copy
+ data[2] = byte.MaxValue;
+ Assert.AreEqual(byte.MaxValue, s[0]);
+ }
+
+ [Test]
+ public void CreateCodedInput_FromArraySegment()
+ {
+ byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+ ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
+ CodedInputStream codedInputStream = bs.CreateCodedInput();
+
+ byte[] bytes = codedInputStream.ReadRawBytes(3);
+
+ Assert.AreEqual(3, bytes.Length);
+ Assert.AreEqual(2, bytes[0]);
+ Assert.AreEqual(3, bytes[1]);
+ Assert.AreEqual(4, bytes[2]);
+ Assert.IsTrue(codedInputStream.IsAtEnd);
+ }
+
+ [Test]
+ public void WriteToStream()
+ {
+ byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
+ ByteString bs = ByteString.CopyFrom(data);
+
+ MemoryStream ms = new MemoryStream();
+ bs.WriteTo(ms);
+
+ CollectionAssert.AreEqual(data, ms.ToArray());
+ }
+
+ [Test]
+ public void WriteToStream_Stackalloc()
+ {
+ byte[] data = Encoding.UTF8.GetBytes("Hello world");
+ Span s = stackalloc byte[data.Length];
+ data.CopyTo(s);
+
+ MemoryStream ms = new MemoryStream();
+
+ using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
+ {
+ ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+ bs.WriteTo(ms);
+ }
+
+ CollectionAssert.AreEqual(data, ms.ToArray());
+ }
+
+ [Test]
+ public void ToStringUtf8()
+ {
+ ByteString bs = ByteString.CopyFromUtf8("\u20ac");
+ Assert.AreEqual("\u20ac", bs.ToStringUtf8());
+ }
+
+ [Test]
+ public void ToStringWithExplicitEncoding()
+ {
+ ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
+ Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
+ }
+
+ [Test]
+ public void ToString_Stackalloc()
+ {
+ byte[] data = Encoding.UTF8.GetBytes("Hello world");
+ Span s = stackalloc byte[data.Length];
+ data.CopyTo(s);
+
+ using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
+ {
+ ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+ Assert.AreEqual("Hello world", bs.ToString(Encoding.UTF8));
+ }
+ }
+
+ [Test]
+ public void FromBase64_WithText()
+ {
+ byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
+ string base64 = Convert.ToBase64String(data);
+ ByteString bs = ByteString.FromBase64(base64);
+ Assert.AreEqual(data, bs.ToByteArray());
+ }
+
+ [Test]
+ public void FromBase64_Empty()
+ {
+ // Optimization which also fixes issue 61.
+ Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
+ }
+
+ [Test]
+ public void ToBase64_Array()
+ {
+ ByteString bs = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world"));
+
+ Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
+ }
+
+ [Test]
+ public void ToBase64_Stackalloc()
+ {
+ byte[] data = Encoding.UTF8.GetBytes("Hello world");
+ Span s = stackalloc byte[data.Length];
+ data.CopyTo(s);
+
+ using (UnmanagedMemoryManager manager = new UnmanagedMemoryManager(s))
+ {
+ ByteString bs = ByteString.AttachBytes(manager.Memory);
+
+ Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
+ }
+ }
+
+ [Test]
+ public void FromStream_Seekable()
+ {
+ var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+ // Consume the first byte, just to test that it's "from current position"
+ stream.ReadByte();
+ var actual = ByteString.FromStream(stream);
+ ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
+ Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+ }
+
+ [Test]
+ public void FromStream_NotSeekable()
+ {
+ var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+ // Consume the first byte, just to test that it's "from current position"
+ stream.ReadByte();
+ // Wrap the original stream in LimitedInputStream, which has CanSeek=false
+ var limitedStream = new LimitedInputStream(stream, 3);
+ var actual = ByteString.FromStream(limitedStream);
+ ByteString expected = ByteString.CopyFrom(2, 3, 4);
+ Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+ }
+
+#if !NET35
+ [Test]
+ public async Task FromStreamAsync_Seekable()
+ {
+ var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+ // Consume the first byte, just to test that it's "from current position"
+ stream.ReadByte();
+ var actual = await ByteString.FromStreamAsync(stream);
+ ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
+ Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+ }
+
+ [Test]
+ public async Task FromStreamAsync_NotSeekable()
+ {
+ var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
+ // Consume the first byte, just to test that it's "from current position"
+ stream.ReadByte();
+ // Wrap the original stream in LimitedInputStream, which has CanSeek=false
+ var limitedStream = new LimitedInputStream(stream, 3);
+ var actual = await ByteString.FromStreamAsync(limitedStream);
+ ByteString expected = ByteString.CopyFrom(2, 3, 4);
+ Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
+ }
+#endif
+
+ [Test]
+ public void GetHashCode_Regression()
+ {
+ // We used to have an awful hash algorithm where only the last four
+ // bytes were relevant. This is a regression test for
+ // https://github.com/protocolbuffers/protobuf/issues/2511
+
+ ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);
+ ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);
+ Assert.AreNotEqual(b1.GetHashCode(), b2.GetHashCode());
+ }
+
+ [Test]
+ public void GetContentsAsReadOnlySpan()
+ {
+ var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
+ var copied = byteString.Span.ToArray();
+ CollectionAssert.AreEqual(byteString, copied);
+ }
+
+ [Test]
+ public void GetContentsAsReadOnlyMemory()
+ {
+ var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
+ var copied = byteString.Memory.ToArray();
+ CollectionAssert.AreEqual(byteString, copied);
+ }
+
+ // Create Memory from non-array source.
+ // Use by ByteString tests that have optimized path for array backed Memory.
+ private sealed unsafe class UnmanagedMemoryManager : MemoryManager where T : unmanaged
+ {
+ private readonly T* _pointer;
+ private readonly int _length;
+
+ public UnmanagedMemoryManager(Span span)
+ {
+ fixed (T* ptr = &MemoryMarshal.GetReference(span))
+ {
+ _pointer = ptr;
+ _length = span.Length;
+ }
+ }
+
+ public override Span GetSpan() => new Span(_pointer, _length);
+
+ public override MemoryHandle Pin(int elementIndex = 0)
+ {
+ if (elementIndex < 0 || elementIndex >= _length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(elementIndex));
+ }
+
+ return new MemoryHandle(_pointer + elementIndex);
+ }
+
+ public override void Unpin() { }
+
+ protected override void Dispose(bool disposing) { }
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 234155975a..b84a1b7ce6 100644
--- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
@@ -1,1008 +1,1008 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.Buffers;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using Proto2 = Google.Protobuf.TestProtos.Proto2;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class CodedInputStreamTest
- {
- ///
- /// Helper to construct a byte array from a bunch of bytes. The inputs are
- /// actually ints so that I can use hex notation and not get stupid errors
- /// about precision.
- ///
- private static byte[] Bytes(params int[] bytesAsInts)
- {
- byte[] bytes = new byte[bytesAsInts.Length];
- for (int i = 0; i < bytesAsInts.Length; i++)
- {
- bytes[i] = (byte) bytesAsInts[i];
- }
- return bytes;
- }
-
- ///
- /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
- ///
- private static void AssertReadVarint(byte[] data, ulong value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual((uint) value, input.ReadRawVarint32());
- Assert.IsTrue(input.IsAtEnd);
-
- input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawVarint64());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- Assert.AreEqual((uint) value, ctx.ReadUInt32());
- }, true);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadUInt64());
- }, true);
-
- // Try different block sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
- Assert.AreEqual((uint) value, input.ReadRawVarint32());
-
- input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
- Assert.AreEqual(value, input.ReadRawVarint64());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
- {
- Assert.AreEqual((uint) value, ctx.ReadUInt32());
- }, true);
-
- AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadUInt64());
- }, true);
- }
-
- // Try reading directly from a MemoryStream. We want to verify that it
- // doesn't read past the end of the input, so write an extra byte - this
- // lets us test the position at the end.
- MemoryStream memoryStream = new MemoryStream();
- memoryStream.Write(data, 0, data.Length);
- memoryStream.WriteByte(0);
- memoryStream.Position = 0;
- Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
- Assert.AreEqual(data.Length, memoryStream.Position);
- }
-
- ///
- /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
- /// expects them to fail with an InvalidProtocolBufferException whose
- /// description matches the given one.
- ///
- private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
- {
- CodedInputStream input = new CodedInputStream(data);
- var exception = Assert.Throws(() => input.ReadRawVarint32());
- Assert.AreEqual(expected.Message, exception.Message);
-
- input = new CodedInputStream(data);
- exception = Assert.Throws(() => input.ReadRawVarint64());
- Assert.AreEqual(expected.Message, exception.Message);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- try
- {
- ctx.ReadUInt32();
- Assert.Fail();
- }
- catch (InvalidProtocolBufferException ex)
- {
- Assert.AreEqual(expected.Message, ex.Message);
- }
- }, false);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- try
- {
- ctx.ReadUInt64();
- Assert.Fail();
- }
- catch (InvalidProtocolBufferException ex)
- {
- Assert.AreEqual(expected.Message, ex.Message);
- }
- }, false);
-
- // Make sure we get the same error when reading directly from a Stream.
- exception = Assert.Throws(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
- Assert.AreEqual(expected.Message, exception.Message);
- }
-
- private delegate void ParseContextAssertAction(ref ParseContext ctx);
-
- private static void AssertReadFromParseContext(ReadOnlySequence input, ParseContextAssertAction assertAction, bool assertIsAtEnd)
- {
- // Check as ReadOnlySequence
- ParseContext.Initialize(input, out ParseContext parseCtx);
- assertAction(ref parseCtx);
- if (assertIsAtEnd)
- {
- Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));
- }
-
- // Check as ReadOnlySpan
- ParseContext.Initialize(input.ToArray().AsSpan(), out ParseContext spanParseContext);
- assertAction(ref spanParseContext);
- if (assertIsAtEnd)
- {
- Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref spanParseContext.buffer, ref spanParseContext.state));
- }
- }
-
- [Test]
- public void ReadVarint()
- {
- AssertReadVarint(Bytes(0x00), 0);
- AssertReadVarint(Bytes(0x01), 1);
- AssertReadVarint(Bytes(0x7f), 127);
- // 14882
- AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
- // 2961488830
- AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
-
- // 64-bit
- // 7256456126
- AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
- // 41256202580718336
- AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
- // 11964378330978735131
- AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
- (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
- (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
-
- // Failures
- AssertReadVarintFailure(
- InvalidProtocolBufferException.MalformedVarint(),
- Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x00));
- AssertReadVarintFailure(
- InvalidProtocolBufferException.TruncatedMessage(),
- Bytes(0x80));
- }
-
- ///
- /// Parses the given bytes using ReadRawLittleEndian32() and checks
- /// that the result matches the given value.
- ///
- private static void AssertReadLittleEndian32(byte[] data, uint value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawLittleEndian32());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadFixed32());
- }, true);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- input = new CodedInputStream(
- new SmallBlockInputStream(data, blockSize));
- Assert.AreEqual(value, input.ReadRawLittleEndian32());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadFixed32());
- }, true);
- }
- }
-
- ///
- /// Parses the given bytes using ReadRawLittleEndian64() and checks
- /// that the result matches the given value.
- ///
- private static void AssertReadLittleEndian64(byte[] data, ulong value)
- {
- CodedInputStream input = new CodedInputStream(data);
- Assert.AreEqual(value, input.ReadRawLittleEndian64());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadFixed64());
- }, true);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- input = new CodedInputStream(
- new SmallBlockInputStream(data, blockSize));
- Assert.AreEqual(value, input.ReadRawLittleEndian64());
- Assert.IsTrue(input.IsAtEnd);
-
- AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(value, ctx.ReadFixed64());
- }, true);
- }
- }
-
- [Test]
- public void ReadLittleEndian()
- {
- AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
- AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
-
- AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
- 0x123456789abcdef0L);
- AssertReadLittleEndian64(
- Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
- }
-
- [Test]
- public void DecodeZigZag32()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
- Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
- Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
- Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
- Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
- Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
- }
-
- [Test]
- public void DecodeZigZag64()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
- Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
- Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
- Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
- Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
- Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
- Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
- Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
- }
-
- [Test]
- public void ReadWholeMessage_VaryingBlockSizes()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- byte[] rawBytes = message.ToByteArray();
- Assert.AreEqual(rawBytes.Length, message.CalculateSize());
- TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
- Assert.AreEqual(message, message2);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2)
- {
- message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
- Assert.AreEqual(message, message2);
- }
- }
-
- [Test]
- public void ReadWholeMessage_VaryingBlockSizes_FromSequence()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- byte[] rawBytes = message.ToByteArray();
- Assert.AreEqual(rawBytes.Length, message.CalculateSize());
- TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
- Assert.AreEqual(message, message2);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2)
- {
- message2 = TestAllTypes.Parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize));
- Assert.AreEqual(message, message2);
- }
- }
-
- [Test]
- public void ReadInt32Wrapper_VariableBlockSizes()
- {
- byte[] rawBytes = new byte[] { 202, 1, 11, 8, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1 };
-
- for (int blockSize = 1; blockSize <= rawBytes.Length; blockSize++)
- {
- ReadOnlySequence data = ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize);
- AssertReadFromParseContext(data, (ref ParseContext ctx) =>
- {
- ctx.ReadTag();
-
- var value = ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx);
-
- Assert.AreEqual(-2, value);
- }, true);
- }
- }
-
- [Test]
- public void ReadHugeBlob()
- {
- // Allocate and initialize a 1MB blob.
- byte[] blob = new byte[1 << 20];
- for (int i = 0; i < blob.Length; i++)
- {
- blob[i] = (byte) i;
- }
-
- // Make a message containing it.
- var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
-
- // Serialize and parse it. Make sure to parse from an InputStream, not
- // directly from a ByteString, so that CodedInputStream uses buffered
- // reading.
- TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
-
- Assert.AreEqual(message, message2);
- }
-
- [Test]
- public void ReadMaliciouslyLargeBlob()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(0x7FFFFFFF);
- output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
- Assert.AreEqual(tag, input.ReadTag());
-
- Assert.Throws(() => input.ReadBytes());
- }
-
- [Test]
- public void ReadBlobGreaterThanCurrentLimit()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(4);
- output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
- Assert.AreEqual(tag, input.ReadTag());
-
- // Specify limit smaller than data length
- input.PushLimit(3);
- Assert.Throws(() => input.ReadBytes());
-
- AssertReadFromParseContext(new ReadOnlySequence(ms.ToArray()), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(tag, ctx.ReadTag());
- SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
- try
- {
- ctx.ReadBytes();
- Assert.Fail();
- }
- catch (InvalidProtocolBufferException) {}
- }, true);
- }
-
- [Test]
- public void ReadStringGreaterThanCurrentLimit()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(4);
- output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms.ToArray());
- Assert.AreEqual(tag, input.ReadTag());
-
- // Specify limit smaller than data length
- input.PushLimit(3);
- Assert.Throws(() => input.ReadString());
-
- AssertReadFromParseContext(new ReadOnlySequence(ms.ToArray()), (ref ParseContext ctx) =>
- {
- Assert.AreEqual(tag, ctx.ReadTag());
- SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
- try
- {
- ctx.ReadString();
- Assert.Fail();
- }
- catch (InvalidProtocolBufferException) { }
- }, true);
- }
-
- // Representations of a tag for field 0 with various wire types
- [Test]
- [TestCase(0)]
- [TestCase(1)]
- [TestCase(2)]
- [TestCase(3)]
- [TestCase(4)]
- [TestCase(5)]
- public void ReadTag_ZeroFieldRejected(byte tag)
- {
- CodedInputStream cis = new CodedInputStream(new byte[] { tag });
- Assert.Throws(() => cis.ReadTag());
- }
-
- internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
- {
- if (depth == 0)
- {
- return new TestRecursiveMessage { I = 5 };
- }
- else
- {
- return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
- }
- }
-
- internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
- {
- if (depth == 0)
- {
- Assert.IsNull(message.A);
- Assert.AreEqual(5, message.I);
- }
- else
- {
- Assert.IsNotNull(message.A);
- AssertMessageDepth(message.A, depth - 1);
- }
- }
-
- [Test]
- public void MaliciousRecursion()
- {
- ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
- ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
-
- AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
-
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
-
- CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(input));
- }
-
- private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)
- {
- // generate recursively nested groups that will be parsed as unknown fields
- int unknownFieldNumber = 14; // an unused field number
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
- for (int i = 0; i < recursionDepth; i++)
- {
- output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));
- }
- for (int i = 0; i < recursionDepth; i++)
- {
- output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));
- }
- output.Flush();
- return ms.ToArray();
- }
-
- [Test]
- public void MaliciousRecursion_UnknownFields()
- {
- byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);
- byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);
-
- Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));
- }
-
- [Test]
- public void ReadGroup_WrongEndGroupTag()
- {
- int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;
-
- // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
- output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));
- output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });
- // end group with different field number
- output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));
- output.Flush();
- var payload = ms.ToArray();
-
- Assert.Throws(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));
- }
-
- [Test]
- public void ReadGroup_UnknownFields_WrongEndGroupTag()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
- output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));
- // end group with different field number
- output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));
- output.Flush();
- var payload = ms.ToArray();
-
- Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(payload));
- }
-
- [Test]
- public void SizeLimit()
- {
- // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
- // apply to the latter case.
- MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
- CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
- Assert.Throws(() => TestAllTypes.Parser.ParseFrom(input));
- }
-
- ///
- /// Tests that if we read an string that contains invalid UTF-8, no exception
- /// is thrown. Instead, the invalid bytes are replaced with the Unicode
- /// "replacement character" U+FFFD.
- ///
- [Test]
- public void ReadInvalidUtf8()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteRawVarint32(1);
- output.WriteRawBytes(new byte[] {0x80});
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
-
- Assert.AreEqual(tag, input.ReadTag());
- string text = input.ReadString();
- Assert.AreEqual('\ufffd', text[0]);
- }
-
- [Test]
- public void ReadNegativeSizedStringThrowsInvalidProtocolBufferException()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteLength(-1);
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
-
- Assert.AreEqual(tag, input.ReadTag());
- Assert.Throws(() => input.ReadString());
- }
-
- [Test]
- public void ReadNegativeSizedBytesThrowsInvalidProtocolBufferException()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteLength(-1);
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
-
- Assert.AreEqual(tag, input.ReadTag());
- Assert.Throws(() => input.ReadBytes());
- }
-
- ///
- /// A stream which limits the number of bytes it reads at a time.
- /// We use this to make sure that CodedInputStream doesn't screw up when
- /// reading in small blocks.
- ///
- private sealed class SmallBlockInputStream : MemoryStream
- {
- private readonly int blockSize;
-
- public SmallBlockInputStream(byte[] data, int blockSize)
- : base(data)
- {
- this.blockSize = blockSize;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return base.Read(buffer, offset, Math.Min(count, blockSize));
- }
- }
-
- [Test]
- public void TestNegativeEnum()
- {
- byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
- CodedInputStream input = new CodedInputStream(bytes);
- Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
- Assert.IsTrue(input.IsAtEnd);
- }
-
- //Issue 71: CodedInputStream.ReadBytes go to slow path unnecessarily
- [Test]
- public void TestSlowPathAvoidance()
- {
- using (var ms = new MemoryStream())
- {
- CodedOutputStream output = new CodedOutputStream(ms);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteBytes(ByteString.CopyFrom(new byte[100]));
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteBytes(ByteString.CopyFrom(new byte[100]));
- output.Flush();
-
- ms.Position = 0;
- CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
-
- uint tag = input.ReadTag();
- Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
- Assert.AreEqual(100, input.ReadBytes().Length);
-
- tag = input.ReadTag();
- Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
- Assert.AreEqual(100, input.ReadBytes().Length);
- }
- }
-
- [Test]
- public void MaximumFieldNumber()
- {
- MemoryStream ms = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(ms);
-
- int fieldNumber = 0x1FFFFFFF;
- uint tag = WireFormat.MakeTag(fieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteRawVarint32(tag);
- output.WriteString("field 1");
- output.Flush();
- ms.Position = 0;
-
- CodedInputStream input = new CodedInputStream(ms);
-
- Assert.AreEqual(tag, input.ReadTag());
- Assert.AreEqual(fieldNumber, WireFormat.GetTagFieldNumber(tag));
- }
-
- [Test]
- public void Tag0Throws()
- {
- var input = new CodedInputStream(new byte[] { 0 });
- Assert.Throws(() => input.ReadTag());
- }
-
- [Test]
- public void SkipGroup()
- {
- // Create an output stream with a group in:
- // Field 1: string "field 1"
- // Field 2: group containing:
- // Field 1: fixed int32 value 100
- // Field 2: string "ignore me"
- // Field 3: nested group containing
- // Field 1: fixed int64 value 1000
- // Field 3: string "field 3"
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 1");
-
- // The outer group...
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(1, WireFormat.WireType.Fixed32);
- output.WriteFixed32(100);
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteString("ignore me");
- // The nested group...
- output.WriteTag(3, WireFormat.WireType.StartGroup);
- output.WriteTag(1, WireFormat.WireType.Fixed64);
- output.WriteFixed64(1000);
- // Note: Not sure the field number is relevant for end group...
- output.WriteTag(3, WireFormat.WireType.EndGroup);
-
- // End the outer group
- output.WriteTag(2, WireFormat.WireType.EndGroup);
-
- output.WriteTag(3, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 3");
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 1", input.ReadString());
- Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
- input.SkipLastField(); // Should consume the whole group, including the nested one.
- Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 3", input.ReadString());
- }
-
- [Test]
- public void SkipGroup_WrongEndGroupTag()
- {
- // Create an output stream with:
- // Field 1: string "field 1"
- // Start group 2
- // Field 3: fixed int32
- // End group 4 (should give an error)
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString("field 1");
-
- // The outer group...
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(3, WireFormat.WireType.Fixed32);
- output.WriteFixed32(100);
- output.WriteTag(4, WireFormat.WireType.EndGroup);
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
- Assert.AreEqual("field 1", input.ReadString());
- Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void RogueEndGroupTag()
- {
- // If we have an end-group tag without a leading start-group tag, generated
- // code will just call SkipLastField... so that should fail.
-
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.EndGroup);
- output.Flush();
- stream.Position = 0;
-
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void EndOfStreamReachedWhileSkippingGroup()
- {
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- output.WriteTag(1, WireFormat.WireType.StartGroup);
- output.WriteTag(2, WireFormat.WireType.StartGroup);
- output.WriteTag(2, WireFormat.WireType.EndGroup);
-
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- input.ReadTag();
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void RecursionLimitAppliedWhileSkippingGroup()
- {
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
- {
- output.WriteTag(1, WireFormat.WireType.StartGroup);
- }
- for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
- {
- output.WriteTag(1, WireFormat.WireType.EndGroup);
- }
- output.Flush();
- stream.Position = 0;
-
- // Now act like a generated client
- var input = new CodedInputStream(stream);
- Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
- Assert.Throws(input.SkipLastField);
- }
-
- [Test]
- public void Construction_Invalid()
- {
- Assert.Throws(() => new CodedInputStream((byte[]) null));
- Assert.Throws(() => new CodedInputStream(null, 0, 0));
- Assert.Throws(() => new CodedInputStream((Stream) null));
- Assert.Throws(() => new CodedInputStream(new byte[10], 100, 0));
- Assert.Throws(() => new CodedInputStream(new byte[10], 5, 10));
- }
-
- [Test]
- public void CreateWithLimits_InvalidLimits()
- {
- var stream = new MemoryStream();
- Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
- Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
- }
-
- [Test]
- public void Dispose_DisposesUnderlyingStream()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanRead);
- using (var cis = new CodedInputStream(memoryStream))
- {
- }
- Assert.IsFalse(memoryStream.CanRead); // Disposed
- }
-
- [Test]
- public void Dispose_WithLeaveOpen()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanRead);
- using (var cis = new CodedInputStream(memoryStream, true))
- {
- }
- Assert.IsTrue(memoryStream.CanRead); // We left the stream open
- }
-
- [Test]
- public void Dispose_FromByteArray()
- {
- var stream = new CodedInputStream(new byte[10]);
- stream.Dispose();
- }
-
- [Test]
- public void TestParseMessagesCloseTo2G()
- {
- byte[] serializedMessage = GenerateBigSerializedMessage();
- // How many of these big messages do we need to take us near our 2GB limit?
- int count = Int32.MaxValue / serializedMessage.Length;
- // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
- // our big serialized message 'count' times.
- using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
- {
- Assert.DoesNotThrow(()=>TestAllTypes.Parser.ParseFrom(stream));
- }
- }
-
- [Test]
- public void TestParseMessagesOver2G()
- {
- byte[] serializedMessage = GenerateBigSerializedMessage();
- // How many of these big messages do we need to take us near our 2GB limit?
- int count = Int32.MaxValue / serializedMessage.Length;
- // Now add one to take us over the 2GB limit
- count++;
- // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
- // our big serialized message 'count' times.
- using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
- {
- Assert.Throws(() => TestAllTypes.Parser.ParseFrom(stream),
- "Protocol message was too large. May be malicious. " +
- "Use CodedInputStream.SetSizeLimit() to increase the size limit.");
- }
- }
-
- /// A serialized big message
- private static byte[] GenerateBigSerializedMessage()
- {
- byte[] value = new byte[16 * 1024 * 1024];
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
- message.SingleBytes = ByteString.CopyFrom(value);
- return message.ToByteArray();
- }
-
- ///
- /// A MemoryStream that repeats a byte arrays' content a number of times.
- /// Simulates really large input without consuming loads of memory. Used above
- /// to test the parsing behavior when the input size exceeds 2GB or close to it.
- ///
- private class RepeatingMemoryStream: MemoryStream
- {
- private readonly byte[] bytes;
- private readonly int maxIterations;
- private int index = 0;
-
- public RepeatingMemoryStream(byte[] bytes, int maxIterations)
- {
- this.bytes = bytes;
- this.maxIterations = maxIterations;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (bytes.Length == 0)
- {
- return 0;
- }
- int numBytesCopiedTotal = 0;
- while (numBytesCopiedTotal < count && index < maxIterations)
- {
- int numBytesToCopy = Math.Min(bytes.Length - (int)Position, count);
- Array.Copy(bytes, (int)Position, buffer, offset, numBytesToCopy);
- numBytesCopiedTotal += numBytesToCopy;
- offset += numBytesToCopy;
- count -= numBytesCopiedTotal;
- Position += numBytesToCopy;
- if (Position >= bytes.Length)
- {
- Position = 0;
- index++;
- }
- }
- return numBytesCopiedTotal;
- }
- }
- }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.Buffers;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class CodedInputStreamTest
+ {
+ ///
+ /// Helper to construct a byte array from a bunch of bytes. The inputs are
+ /// actually ints so that I can use hex notation and not get stupid errors
+ /// about precision.
+ ///
+ private static byte[] Bytes(params int[] bytesAsInts)
+ {
+ byte[] bytes = new byte[bytesAsInts.Length];
+ for (int i = 0; i < bytesAsInts.Length; i++)
+ {
+ bytes[i] = (byte) bytesAsInts[i];
+ }
+ return bytes;
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
+ ///
+ private static void AssertReadVarint(byte[] data, ulong value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual((uint) value, input.ReadRawVarint32());
+ Assert.IsTrue(input.IsAtEnd);
+
+ input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawVarint64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual((uint) value, ctx.ReadUInt32());
+ }, true);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadUInt64());
+ }, true);
+
+ // Try different block sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+ Assert.AreEqual((uint) value, input.ReadRawVarint32());
+
+ input = new CodedInputStream(new SmallBlockInputStream(data, bufferSize));
+ Assert.AreEqual(value, input.ReadRawVarint64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual((uint) value, ctx.ReadUInt32());
+ }, true);
+
+ AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, bufferSize), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadUInt64());
+ }, true);
+ }
+
+ // Try reading directly from a MemoryStream. We want to verify that it
+ // doesn't read past the end of the input, so write an extra byte - this
+ // lets us test the position at the end.
+ MemoryStream memoryStream = new MemoryStream();
+ memoryStream.Write(data, 0, data.Length);
+ memoryStream.WriteByte(0);
+ memoryStream.Position = 0;
+ Assert.AreEqual((uint) value, CodedInputStream.ReadRawVarint32(memoryStream));
+ Assert.AreEqual(data.Length, memoryStream.Position);
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
+ /// expects them to fail with an InvalidProtocolBufferException whose
+ /// description matches the given one.
+ ///
+ private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ var exception = Assert.Throws(() => input.ReadRawVarint32());
+ Assert.AreEqual(expected.Message, exception.Message);
+
+ input = new CodedInputStream(data);
+ exception = Assert.Throws(() => input.ReadRawVarint64());
+ Assert.AreEqual(expected.Message, exception.Message);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ try
+ {
+ ctx.ReadUInt32();
+ Assert.Fail();
+ }
+ catch (InvalidProtocolBufferException ex)
+ {
+ Assert.AreEqual(expected.Message, ex.Message);
+ }
+ }, false);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ try
+ {
+ ctx.ReadUInt64();
+ Assert.Fail();
+ }
+ catch (InvalidProtocolBufferException ex)
+ {
+ Assert.AreEqual(expected.Message, ex.Message);
+ }
+ }, false);
+
+ // Make sure we get the same error when reading directly from a Stream.
+ exception = Assert.Throws(() => CodedInputStream.ReadRawVarint32(new MemoryStream(data)));
+ Assert.AreEqual(expected.Message, exception.Message);
+ }
+
+ private delegate void ParseContextAssertAction(ref ParseContext ctx);
+
+ private static void AssertReadFromParseContext(ReadOnlySequence input, ParseContextAssertAction assertAction, bool assertIsAtEnd)
+ {
+ // Check as ReadOnlySequence
+ ParseContext.Initialize(input, out ParseContext parseCtx);
+ assertAction(ref parseCtx);
+ if (assertIsAtEnd)
+ {
+ Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));
+ }
+
+ // Check as ReadOnlySpan
+ ParseContext.Initialize(input.ToArray().AsSpan(), out ParseContext spanParseContext);
+ assertAction(ref spanParseContext);
+ if (assertIsAtEnd)
+ {
+ Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref spanParseContext.buffer, ref spanParseContext.state));
+ }
+ }
+
+ [Test]
+ public void ReadVarint()
+ {
+ AssertReadVarint(Bytes(0x00), 0);
+ AssertReadVarint(Bytes(0x01), 1);
+ AssertReadVarint(Bytes(0x7f), 127);
+ // 14882
+ AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
+ // 2961488830
+ AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x0bL << 28));
+
+ // 64-bit
+ // 7256456126
+ AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x1bL << 28));
+ // 41256202580718336
+ AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+ (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+ (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+ // 11964378330978735131
+ AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+ (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+ (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
+ (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
+
+ // Failures
+ AssertReadVarintFailure(
+ InvalidProtocolBufferException.MalformedVarint(),
+ Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x00));
+ AssertReadVarintFailure(
+ InvalidProtocolBufferException.TruncatedMessage(),
+ Bytes(0x80));
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawLittleEndian32() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertReadLittleEndian32(byte[] data, uint value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawLittleEndian32());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadFixed32());
+ }, true);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ input = new CodedInputStream(
+ new SmallBlockInputStream(data, blockSize));
+ Assert.AreEqual(value, input.ReadRawLittleEndian32());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadFixed32());
+ }, true);
+ }
+ }
+
+ ///
+ /// Parses the given bytes using ReadRawLittleEndian64() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertReadLittleEndian64(byte[] data, ulong value)
+ {
+ CodedInputStream input = new CodedInputStream(data);
+ Assert.AreEqual(value, input.ReadRawLittleEndian64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(new ReadOnlySequence(data), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadFixed64());
+ }, true);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ input = new CodedInputStream(
+ new SmallBlockInputStream(data, blockSize));
+ Assert.AreEqual(value, input.ReadRawLittleEndian64());
+ Assert.IsTrue(input.IsAtEnd);
+
+ AssertReadFromParseContext(ReadOnlySequenceFactory.CreateWithContent(data, blockSize), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(value, ctx.ReadFixed64());
+ }, true);
+ }
+ }
+
+ [Test]
+ public void ReadLittleEndian()
+ {
+ AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
+ AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
+
+ AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
+ 0x123456789abcdef0L);
+ AssertReadLittleEndian64(
+ Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
+ }
+
+ [Test]
+ public void DecodeZigZag32()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(0));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(1));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(2));
+ Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag32(3));
+ Assert.AreEqual(0x3FFFFFFF, ParsingPrimitives.DecodeZigZag32(0x7FFFFFFE));
+ Assert.AreEqual(unchecked((int) 0xC0000000), ParsingPrimitives.DecodeZigZag32(0x7FFFFFFF));
+ Assert.AreEqual(0x7FFFFFFF, ParsingPrimitives.DecodeZigZag32(0xFFFFFFFE));
+ Assert.AreEqual(unchecked((int) 0x80000000), ParsingPrimitives.DecodeZigZag32(0xFFFFFFFF));
+ }
+
+ [Test]
+ public void DecodeZigZag64()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(0));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(1));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(2));
+ Assert.AreEqual(-2, ParsingPrimitives.DecodeZigZag64(3));
+ Assert.AreEqual(0x000000003FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0xFFFFFFFFC0000000L), ParsingPrimitives.DecodeZigZag64(0x000000007FFFFFFFL));
+ Assert.AreEqual(0x000000007FFFFFFFL, ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0xFFFFFFFF80000000L), ParsingPrimitives.DecodeZigZag64(0x00000000FFFFFFFFL));
+ Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+ Assert.AreEqual(unchecked((long) 0x8000000000000000L), ParsingPrimitives.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+ }
+
+ [Test]
+ public void ReadWholeMessage_VaryingBlockSizes()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ byte[] rawBytes = message.ToByteArray();
+ Assert.AreEqual(rawBytes.Length, message.CalculateSize());
+ TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
+ Assert.AreEqual(message, message2);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+ {
+ message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
+ Assert.AreEqual(message, message2);
+ }
+ }
+
+ [Test]
+ public void ReadWholeMessage_VaryingBlockSizes_FromSequence()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ byte[] rawBytes = message.ToByteArray();
+ Assert.AreEqual(rawBytes.Length, message.CalculateSize());
+ TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
+ Assert.AreEqual(message, message2);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+ {
+ message2 = TestAllTypes.Parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize));
+ Assert.AreEqual(message, message2);
+ }
+ }
+
+ [Test]
+ public void ReadInt32Wrapper_VariableBlockSizes()
+ {
+ byte[] rawBytes = new byte[] { 202, 1, 11, 8, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1 };
+
+ for (int blockSize = 1; blockSize <= rawBytes.Length; blockSize++)
+ {
+ ReadOnlySequence data = ReadOnlySequenceFactory.CreateWithContent(rawBytes, blockSize);
+ AssertReadFromParseContext(data, (ref ParseContext ctx) =>
+ {
+ ctx.ReadTag();
+
+ var value = ParsingPrimitivesWrappers.ReadInt32Wrapper(ref ctx);
+
+ Assert.AreEqual(-2, value);
+ }, true);
+ }
+ }
+
+ [Test]
+ public void ReadHugeBlob()
+ {
+ // Allocate and initialize a 1MB blob.
+ byte[] blob = new byte[1 << 20];
+ for (int i = 0; i < blob.Length; i++)
+ {
+ blob[i] = (byte) i;
+ }
+
+ // Make a message containing it.
+ var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
+
+ // Serialize and parse it. Make sure to parse from an InputStream, not
+ // directly from a ByteString, so that CodedInputStream uses buffered
+ // reading.
+ TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
+
+ Assert.AreEqual(message, message2);
+ }
+
+ [Test]
+ public void ReadMaliciouslyLargeBlob()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(0x7FFFFFFF);
+ output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+ Assert.AreEqual(tag, input.ReadTag());
+
+ Assert.Throws(() => input.ReadBytes());
+ }
+
+ [Test]
+ public void ReadBlobGreaterThanCurrentLimit()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(4);
+ output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+ Assert.AreEqual(tag, input.ReadTag());
+
+ // Specify limit smaller than data length
+ input.PushLimit(3);
+ Assert.Throws(() => input.ReadBytes());
+
+ AssertReadFromParseContext(new ReadOnlySequence(ms.ToArray()), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(tag, ctx.ReadTag());
+ SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
+ try
+ {
+ ctx.ReadBytes();
+ Assert.Fail();
+ }
+ catch (InvalidProtocolBufferException) {}
+ }, true);
+ }
+
+ [Test]
+ public void ReadStringGreaterThanCurrentLimit()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(4);
+ output.WriteRawBytes(new byte[4]); // Pad with a few random bytes.
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms.ToArray());
+ Assert.AreEqual(tag, input.ReadTag());
+
+ // Specify limit smaller than data length
+ input.PushLimit(3);
+ Assert.Throws(() => input.ReadString());
+
+ AssertReadFromParseContext(new ReadOnlySequence(ms.ToArray()), (ref ParseContext ctx) =>
+ {
+ Assert.AreEqual(tag, ctx.ReadTag());
+ SegmentedBufferHelper.PushLimit(ref ctx.state, 3);
+ try
+ {
+ ctx.ReadString();
+ Assert.Fail();
+ }
+ catch (InvalidProtocolBufferException) { }
+ }, true);
+ }
+
+ // Representations of a tag for field 0 with various wire types
+ [Test]
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(3)]
+ [TestCase(4)]
+ [TestCase(5)]
+ public void ReadTag_ZeroFieldRejected(byte tag)
+ {
+ CodedInputStream cis = new CodedInputStream(new byte[] { tag });
+ Assert.Throws(() => cis.ReadTag());
+ }
+
+ internal static TestRecursiveMessage MakeRecursiveMessage(int depth)
+ {
+ if (depth == 0)
+ {
+ return new TestRecursiveMessage { I = 5 };
+ }
+ else
+ {
+ return new TestRecursiveMessage { A = MakeRecursiveMessage(depth - 1) };
+ }
+ }
+
+ internal static void AssertMessageDepth(TestRecursiveMessage message, int depth)
+ {
+ if (depth == 0)
+ {
+ Assert.IsNull(message.A);
+ Assert.AreEqual(5, message.I);
+ }
+ else
+ {
+ Assert.IsNotNull(message.A);
+ AssertMessageDepth(message.A, depth - 1);
+ }
+ }
+
+ [Test]
+ public void MaliciousRecursion()
+ {
+ ByteString atRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit).ToByteString();
+ ByteString beyondRecursiveLimit = MakeRecursiveMessage(CodedInputStream.DefaultRecursionLimit + 1).ToByteString();
+
+ AssertMessageDepth(TestRecursiveMessage.Parser.ParseFrom(atRecursiveLimit), CodedInputStream.DefaultRecursionLimit);
+
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(beyondRecursiveLimit));
+
+ CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(atRecursiveLimit.ToByteArray()), 1000000, CodedInputStream.DefaultRecursionLimit - 1);
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(input));
+ }
+
+ private static byte[] MakeMaliciousRecursionUnknownFieldsPayload(int recursionDepth)
+ {
+ // generate recursively nested groups that will be parsed as unknown fields
+ int unknownFieldNumber = 14; // an unused field number
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+ for (int i = 0; i < recursionDepth; i++)
+ {
+ output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.StartGroup));
+ }
+ for (int i = 0; i < recursionDepth; i++)
+ {
+ output.WriteTag(WireFormat.MakeTag(unknownFieldNumber, WireFormat.WireType.EndGroup));
+ }
+ output.Flush();
+ return ms.ToArray();
+ }
+
+ [Test]
+ public void MaliciousRecursion_UnknownFields()
+ {
+ byte[] payloadAtRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit);
+ byte[] payloadBeyondRecursiveLimit = MakeMaliciousRecursionUnknownFieldsPayload(CodedInputStream.DefaultRecursionLimit + 1);
+
+ Assert.DoesNotThrow(() => TestRecursiveMessage.Parser.ParseFrom(payloadAtRecursiveLimit));
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(payloadBeyondRecursiveLimit));
+ }
+
+ [Test]
+ public void ReadGroup_WrongEndGroupTag()
+ {
+ int groupFieldNumber = Proto2.TestAllTypes.OptionalGroupFieldNumber;
+
+ // write Proto2.TestAllTypes with "optional_group" set, but use wrong EndGroup closing tag
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+ output.WriteTag(WireFormat.MakeTag(groupFieldNumber, WireFormat.WireType.StartGroup));
+ output.WriteGroup(new Proto2.TestAllTypes.Types.OptionalGroup { A = 12345 });
+ // end group with different field number
+ output.WriteTag(WireFormat.MakeTag(groupFieldNumber + 1, WireFormat.WireType.EndGroup));
+ output.Flush();
+ var payload = ms.ToArray();
+
+ Assert.Throws(() => Proto2.TestAllTypes.Parser.ParseFrom(payload));
+ }
+
+ [Test]
+ public void ReadGroup_UnknownFields_WrongEndGroupTag()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+ output.WriteTag(WireFormat.MakeTag(14, WireFormat.WireType.StartGroup));
+ // end group with different field number
+ output.WriteTag(WireFormat.MakeTag(15, WireFormat.WireType.EndGroup));
+ output.Flush();
+ var payload = ms.ToArray();
+
+ Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(payload));
+ }
+
+ [Test]
+ public void SizeLimit()
+ {
+ // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
+ // apply to the latter case.
+ MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray());
+ CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100);
+ Assert.Throws(() => TestAllTypes.Parser.ParseFrom(input));
+ }
+
+ ///
+ /// Tests that if we read an string that contains invalid UTF-8, no exception
+ /// is thrown. Instead, the invalid bytes are replaced with the Unicode
+ /// "replacement character" U+FFFD.
+ ///
+ [Test]
+ public void ReadInvalidUtf8()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteRawVarint32(1);
+ output.WriteRawBytes(new byte[] {0x80});
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+
+ Assert.AreEqual(tag, input.ReadTag());
+ string text = input.ReadString();
+ Assert.AreEqual('\ufffd', text[0]);
+ }
+
+ [Test]
+ public void ReadNegativeSizedStringThrowsInvalidProtocolBufferException()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteLength(-1);
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+
+ Assert.AreEqual(tag, input.ReadTag());
+ Assert.Throws(() => input.ReadString());
+ }
+
+ [Test]
+ public void ReadNegativeSizedBytesThrowsInvalidProtocolBufferException()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteLength(-1);
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+
+ Assert.AreEqual(tag, input.ReadTag());
+ Assert.Throws(() => input.ReadBytes());
+ }
+
+ ///
+ /// A stream which limits the number of bytes it reads at a time.
+ /// We use this to make sure that CodedInputStream doesn't screw up when
+ /// reading in small blocks.
+ ///
+ private sealed class SmallBlockInputStream : MemoryStream
+ {
+ private readonly int blockSize;
+
+ public SmallBlockInputStream(byte[] data, int blockSize)
+ : base(data)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return base.Read(buffer, offset, Math.Min(count, blockSize));
+ }
+ }
+
+ [Test]
+ public void TestNegativeEnum()
+ {
+ byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
+ CodedInputStream input = new CodedInputStream(bytes);
+ Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
+ Assert.IsTrue(input.IsAtEnd);
+ }
+
+ //Issue 71: CodedInputStream.ReadBytes go to slow path unnecessarily
+ [Test]
+ public void TestSlowPathAvoidance()
+ {
+ using (var ms = new MemoryStream())
+ {
+ CodedOutputStream output = new CodedOutputStream(ms);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteBytes(ByteString.CopyFrom(new byte[100]));
+ output.Flush();
+
+ ms.Position = 0;
+ CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0, false);
+
+ uint tag = input.ReadTag();
+ Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag));
+ Assert.AreEqual(100, input.ReadBytes().Length);
+
+ tag = input.ReadTag();
+ Assert.AreEqual(2, WireFormat.GetTagFieldNumber(tag));
+ Assert.AreEqual(100, input.ReadBytes().Length);
+ }
+ }
+
+ [Test]
+ public void MaximumFieldNumber()
+ {
+ MemoryStream ms = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(ms);
+
+ int fieldNumber = 0x1FFFFFFF;
+ uint tag = WireFormat.MakeTag(fieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteRawVarint32(tag);
+ output.WriteString("field 1");
+ output.Flush();
+ ms.Position = 0;
+
+ CodedInputStream input = new CodedInputStream(ms);
+
+ Assert.AreEqual(tag, input.ReadTag());
+ Assert.AreEqual(fieldNumber, WireFormat.GetTagFieldNumber(tag));
+ }
+
+ [Test]
+ public void Tag0Throws()
+ {
+ var input = new CodedInputStream(new byte[] { 0 });
+ Assert.Throws(() => input.ReadTag());
+ }
+
+ [Test]
+ public void SkipGroup()
+ {
+ // Create an output stream with a group in:
+ // Field 1: string "field 1"
+ // Field 2: group containing:
+ // Field 1: fixed int32 value 100
+ // Field 2: string "ignore me"
+ // Field 3: nested group containing
+ // Field 1: fixed int64 value 1000
+ // Field 3: string "field 3"
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 1");
+
+ // The outer group...
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(1, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(100);
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteString("ignore me");
+ // The nested group...
+ output.WriteTag(3, WireFormat.WireType.StartGroup);
+ output.WriteTag(1, WireFormat.WireType.Fixed64);
+ output.WriteFixed64(1000);
+ // Note: Not sure the field number is relevant for end group...
+ output.WriteTag(3, WireFormat.WireType.EndGroup);
+
+ // End the outer group
+ output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+ output.WriteTag(3, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 3");
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 1", input.ReadString());
+ Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+ input.SkipLastField(); // Should consume the whole group, including the nested one.
+ Assert.AreEqual(WireFormat.MakeTag(3, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 3", input.ReadString());
+ }
+
+ [Test]
+ public void SkipGroup_WrongEndGroupTag()
+ {
+ // Create an output stream with:
+ // Field 1: string "field 1"
+ // Start group 2
+ // Field 3: fixed int32
+ // End group 4 (should give an error)
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString("field 1");
+
+ // The outer group...
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(3, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(100);
+ output.WriteTag(4, WireFormat.WireType.EndGroup);
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited), input.ReadTag());
+ Assert.AreEqual("field 1", input.ReadString());
+ Assert.AreEqual(WireFormat.MakeTag(2, WireFormat.WireType.StartGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void RogueEndGroupTag()
+ {
+ // If we have an end-group tag without a leading start-group tag, generated
+ // code will just call SkipLastField... so that should fail.
+
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.EndGroup);
+ output.Flush();
+ stream.Position = 0;
+
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.EndGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void EndOfStreamReachedWhileSkippingGroup()
+ {
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ output.WriteTag(1, WireFormat.WireType.StartGroup);
+ output.WriteTag(2, WireFormat.WireType.StartGroup);
+ output.WriteTag(2, WireFormat.WireType.EndGroup);
+
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ input.ReadTag();
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void RecursionLimitAppliedWhileSkippingGroup()
+ {
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+ {
+ output.WriteTag(1, WireFormat.WireType.StartGroup);
+ }
+ for (int i = 0; i < CodedInputStream.DefaultRecursionLimit + 1; i++)
+ {
+ output.WriteTag(1, WireFormat.WireType.EndGroup);
+ }
+ output.Flush();
+ stream.Position = 0;
+
+ // Now act like a generated client
+ var input = new CodedInputStream(stream);
+ Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag());
+ Assert.Throws(input.SkipLastField);
+ }
+
+ [Test]
+ public void Construction_Invalid()
+ {
+ Assert.Throws(() => new CodedInputStream((byte[]) null));
+ Assert.Throws(() => new CodedInputStream(null, 0, 0));
+ Assert.Throws(() => new CodedInputStream((Stream) null));
+ Assert.Throws(() => new CodedInputStream(new byte[10], 100, 0));
+ Assert.Throws(() => new CodedInputStream(new byte[10], 5, 10));
+ }
+
+ [Test]
+ public void CreateWithLimits_InvalidLimits()
+ {
+ var stream = new MemoryStream();
+ Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 0, 1));
+ Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 1, 0));
+ }
+
+ [Test]
+ public void Dispose_DisposesUnderlyingStream()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanRead);
+ using (var cis = new CodedInputStream(memoryStream))
+ {
+ }
+ Assert.IsFalse(memoryStream.CanRead); // Disposed
+ }
+
+ [Test]
+ public void Dispose_WithLeaveOpen()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanRead);
+ using (var cis = new CodedInputStream(memoryStream, true))
+ {
+ }
+ Assert.IsTrue(memoryStream.CanRead); // We left the stream open
+ }
+
+ [Test]
+ public void Dispose_FromByteArray()
+ {
+ var stream = new CodedInputStream(new byte[10]);
+ stream.Dispose();
+ }
+
+ [Test]
+ public void TestParseMessagesCloseTo2G()
+ {
+ byte[] serializedMessage = GenerateBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2GB limit?
+ int count = Int32.MaxValue / serializedMessage.Length;
+ // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
+ // our big serialized message 'count' times.
+ using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
+ {
+ Assert.DoesNotThrow(()=>TestAllTypes.Parser.ParseFrom(stream));
+ }
+ }
+
+ [Test]
+ public void TestParseMessagesOver2G()
+ {
+ byte[] serializedMessage = GenerateBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2GB limit?
+ int count = Int32.MaxValue / serializedMessage.Length;
+ // Now add one to take us over the 2GB limit
+ count++;
+ // Now make a MemoryStream that will fake a near-2GB stream of messages by returning
+ // our big serialized message 'count' times.
+ using (RepeatingMemoryStream stream = new RepeatingMemoryStream(serializedMessage, count))
+ {
+ Assert.Throws(() => TestAllTypes.Parser.ParseFrom(stream),
+ "Protocol message was too large. May be malicious. " +
+ "Use CodedInputStream.SetSizeLimit() to increase the size limit.");
+ }
+ }
+
+ /// A serialized big message
+ private static byte[] GenerateBigSerializedMessage()
+ {
+ byte[] value = new byte[16 * 1024 * 1024];
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+ message.SingleBytes = ByteString.CopyFrom(value);
+ return message.ToByteArray();
+ }
+
+ ///
+ /// A MemoryStream that repeats a byte arrays' content a number of times.
+ /// Simulates really large input without consuming loads of memory. Used above
+ /// to test the parsing behavior when the input size exceeds 2GB or close to it.
+ ///
+ private class RepeatingMemoryStream: MemoryStream
+ {
+ private readonly byte[] bytes;
+ private readonly int maxIterations;
+ private int index = 0;
+
+ public RepeatingMemoryStream(byte[] bytes, int maxIterations)
+ {
+ this.bytes = bytes;
+ this.maxIterations = maxIterations;
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (bytes.Length == 0)
+ {
+ return 0;
+ }
+ int numBytesCopiedTotal = 0;
+ while (numBytesCopiedTotal < count && index < maxIterations)
+ {
+ int numBytesToCopy = Math.Min(bytes.Length - (int)Position, count);
+ Array.Copy(bytes, (int)Position, buffer, offset, numBytesToCopy);
+ numBytesCopiedTotal += numBytesToCopy;
+ offset += numBytesToCopy;
+ count -= numBytesCopiedTotal;
+ Position += numBytesToCopy;
+ if (Position >= bytes.Length)
+ {
+ Position = 0;
+ index++;
+ }
+ }
+ return numBytesCopiedTotal;
+ }
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
index 14440098f1..13f83a2c35 100644
--- a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
@@ -1,583 +1,583 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using Google.Protobuf.Buffers;
-using NUnit.Framework;
-using System.Text;
-
-namespace Google.Protobuf
-{
- public class CodedOutputStreamTest
- {
- ///
- /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
- /// checks that the result matches the given bytes
- ///
- private static void AssertWriteVarint(byte[] data, ulong value)
- {
- // Only do 32-bit write if the value fits in 32 bits.
- if ((value >> 32) == 0)
- {
- // CodedOutputStream
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawVarint32((uint) value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- // IBufferWriter
- var bufferWriter = new TestArrayBufferWriter();
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteUInt32((uint) value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
-
- // Also try computing size.
- Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
- }
-
- {
- // CodedOutputStream
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput);
- output.WriteRawVarint64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- // IBufferWriter
- var bufferWriter = new TestArrayBufferWriter();
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteUInt64(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
-
- // Also try computing size.
- Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
- }
-
- // Try different buffer sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- // Only do 32-bit write if the value fits in 32 bits.
- if ((value >> 32) == 0)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output =
- new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawVarint32((uint) value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- bufferWriter.MaxGrowBy = bufferSize;
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteUInt32((uint) value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
-
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawVarint64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- bufferWriter.MaxGrowBy = bufferSize;
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteUInt64(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
-
- }
- }
-
- ///
- /// Tests WriteRawVarint32() and WriteRawVarint64()
- ///
- [Test]
- public void WriteVarint()
- {
- AssertWriteVarint(new byte[] {0x00}, 0);
- AssertWriteVarint(new byte[] {0x01}, 1);
- AssertWriteVarint(new byte[] {0x7f}, 127);
- // 14882
- AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
- // 2961488830
- AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
-
- // 64-bit
- // 7256456126
- AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
- // 41256202580718336
- AssertWriteVarint(
- new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
- // 11964378330978735131
- AssertWriteVarint(
- new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
- unchecked((ulong)
- ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
- (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
- }
-
- ///
- /// Parses the given bytes using WriteRawLittleEndian32() and checks
- /// that the result matches the given value.
- ///
- private static void AssertWriteLittleEndian32(byte[] data, uint value)
- {
- {
- var rawOutput = new MemoryStream();
- var output = new CodedOutputStream(rawOutput);
- output.WriteRawLittleEndian32(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteFixed32(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
-
- // Try different buffer sizes.
- for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
- {
- var rawOutput = new MemoryStream();
- var output = new CodedOutputStream(rawOutput, bufferSize);
- output.WriteRawLittleEndian32(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- bufferWriter.MaxGrowBy = bufferSize;
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteFixed32(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
- }
-
- ///
- /// Parses the given bytes using WriteRawLittleEndian64() and checks
- /// that the result matches the given value.
- ///
- private static void AssertWriteLittleEndian64(byte[] data, ulong value)
- {
- {
- var rawOutput = new MemoryStream();
- var output = new CodedOutputStream(rawOutput);
- output.WriteRawLittleEndian64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteFixed64(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
- {
- var rawOutput = new MemoryStream();
- var output = new CodedOutputStream(rawOutput, blockSize);
- output.WriteRawLittleEndian64(value);
- output.Flush();
- Assert.AreEqual(data, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- bufferWriter.MaxGrowBy = blockSize;
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteFixed64(value);
- ctx.Flush();
- Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
- }
- }
-
- ///
- /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
- ///
- [Test]
- public void WriteLittleEndian()
- {
- AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
- AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
-
- AssertWriteLittleEndian64(
- new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
- 0x123456789abcdef0L);
- AssertWriteLittleEndian64(
- new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
- 0x9abcdef012345678UL);
- }
-
- [Test]
- public void WriteWholeMessage_VaryingBlockSizes()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- byte[] rawBytes = message.ToByteArray();
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2)
- {
- MemoryStream rawOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
- message.WriteTo(output);
- output.Flush();
- Assert.AreEqual(rawBytes, rawOutput.ToArray());
-
- var bufferWriter = new TestArrayBufferWriter();
- bufferWriter.MaxGrowBy = blockSize;
- message.WriteTo(bufferWriter);
- Assert.AreEqual(rawBytes, bufferWriter.WrittenSpan.ToArray());
- }
- }
-
- [Test]
- public void WriteContext_WritesWithFlushes()
- {
- TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
-
- MemoryStream expectedOutput = new MemoryStream();
- CodedOutputStream output = new CodedOutputStream(expectedOutput);
- output.WriteMessage(message);
- output.Flush();
- byte[] expectedBytes1 = expectedOutput.ToArray();
-
- output.WriteMessage(message);
- output.Flush();
- byte[] expectedBytes2 = expectedOutput.ToArray();
-
- var bufferWriter = new TestArrayBufferWriter();
- WriteContext.Initialize(bufferWriter, out WriteContext ctx);
- ctx.WriteMessage(message);
- ctx.Flush();
- Assert.AreEqual(expectedBytes1, bufferWriter.WrittenSpan.ToArray());
-
- ctx.WriteMessage(message);
- ctx.Flush();
- Assert.AreEqual(expectedBytes2, bufferWriter.WrittenSpan.ToArray());
- }
-
- [Test]
- public void EncodeZigZag32()
- {
- Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
- Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
- Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
- Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
- Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
- Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
- Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
- Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
- }
-
- [Test]
- public void EncodeZigZag64()
- {
- Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
- Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
- Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
- Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
- Assert.AreEqual(0x000000007FFFFFFEuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
- Assert.AreEqual(0x000000007FFFFFFFuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
- Assert.AreEqual(0x00000000FFFFFFFEuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
- Assert.AreEqual(0x00000000FFFFFFFFuL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
- Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
- Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
- WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
- }
-
- [Test]
- public void RoundTripZigZag32()
- {
- // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
- // were chosen semi-randomly via keyboard bashing.
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
- Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
- Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
- }
-
- [Test]
- public void RoundTripZigZag64()
- {
- Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
- Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
- Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
- Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
- Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
-
- Assert.AreEqual(856912304801416L,
- ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
- Assert.AreEqual(-75123905439571256L,
- ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
- }
-
- [Test]
- public void TestNegativeEnumNoTag()
- {
- Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
- Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
-
- byte[] bytes = new byte[10];
- CodedOutputStream output = new CodedOutputStream(bytes);
- output.WriteEnum((int) SampleEnum.NegativeValue);
-
- Assert.AreEqual(0, output.SpaceLeft);
- Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
- }
-
- [Test]
- public void TestCodedInputOutputPosition()
- {
- byte[] content = new byte[110];
- for (int i = 0; i < content.Length; i++)
- content[i] = (byte)i;
-
- byte[] child = new byte[120];
- {
- MemoryStream ms = new MemoryStream(child);
- CodedOutputStream cout = new CodedOutputStream(ms, 20);
- // Field 11: numeric value: 500
- cout.WriteTag(11, WireFormat.WireType.Varint);
- Assert.AreEqual(1, cout.Position);
- cout.WriteInt32(500);
- Assert.AreEqual(3, cout.Position);
- //Field 12: length delimited 120 bytes
- cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
- Assert.AreEqual(4, cout.Position);
- cout.WriteBytes(ByteString.CopyFrom(content));
- Assert.AreEqual(115, cout.Position);
- // Field 13: fixed numeric value: 501
- cout.WriteTag(13, WireFormat.WireType.Fixed32);
- Assert.AreEqual(116, cout.Position);
- cout.WriteSFixed32(501);
- Assert.AreEqual(120, cout.Position);
- cout.Flush();
- }
-
- byte[] bytes = new byte[130];
- {
- CodedOutputStream cout = new CodedOutputStream(bytes);
- // Field 1: numeric value: 500
- cout.WriteTag(1, WireFormat.WireType.Varint);
- Assert.AreEqual(1, cout.Position);
- cout.WriteInt32(500);
- Assert.AreEqual(3, cout.Position);
- //Field 2: length delimited 120 bytes
- cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
- Assert.AreEqual(4, cout.Position);
- cout.WriteBytes(ByteString.CopyFrom(child));
- Assert.AreEqual(125, cout.Position);
- // Field 3: fixed numeric value: 500
- cout.WriteTag(3, WireFormat.WireType.Fixed32);
- Assert.AreEqual(126, cout.Position);
- cout.WriteSFixed32(501);
- Assert.AreEqual(130, cout.Position);
- cout.Flush();
- }
- // Now test Input stream:
- {
- CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
- Assert.AreEqual(0, cin.Position);
- // Field 1:
- uint tag = cin.ReadTag();
- Assert.AreEqual(1, tag >> 3);
- Assert.AreEqual(1, cin.Position);
- Assert.AreEqual(500, cin.ReadInt32());
- Assert.AreEqual(3, cin.Position);
- //Field 2:
- tag = cin.ReadTag();
- Assert.AreEqual(2, tag >> 3);
- Assert.AreEqual(4, cin.Position);
- int childlen = cin.ReadLength();
- Assert.AreEqual(120, childlen);
- Assert.AreEqual(5, cin.Position);
- int oldlimit = cin.PushLimit((int)childlen);
- Assert.AreEqual(5, cin.Position);
- // Now we are reading child message
- {
- // Field 11: numeric value: 500
- tag = cin.ReadTag();
- Assert.AreEqual(11, tag >> 3);
- Assert.AreEqual(6, cin.Position);
- Assert.AreEqual(500, cin.ReadInt32());
- Assert.AreEqual(8, cin.Position);
- //Field 12: length delimited 120 bytes
- tag = cin.ReadTag();
- Assert.AreEqual(12, tag >> 3);
- Assert.AreEqual(9, cin.Position);
- ByteString bstr = cin.ReadBytes();
- Assert.AreEqual(110, bstr.Length);
- Assert.AreEqual((byte) 109, bstr[109]);
- Assert.AreEqual(120, cin.Position);
- // Field 13: fixed numeric value: 501
- tag = cin.ReadTag();
- Assert.AreEqual(13, tag >> 3);
- // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
- Assert.AreEqual(121, cin.Position);
- Assert.AreEqual(501, cin.ReadSFixed32());
- Assert.AreEqual(125, cin.Position);
- Assert.IsTrue(cin.IsAtEnd);
- }
- cin.PopLimit(oldlimit);
- Assert.AreEqual(125, cin.Position);
- // Field 3: fixed numeric value: 501
- tag = cin.ReadTag();
- Assert.AreEqual(3, tag >> 3);
- Assert.AreEqual(126, cin.Position);
- Assert.AreEqual(501, cin.ReadSFixed32());
- Assert.AreEqual(130, cin.Position);
- Assert.IsTrue(cin.IsAtEnd);
- }
- }
-
- [Test]
- public void Dispose_DisposesUnderlyingStream()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanWrite);
- using (var cos = new CodedOutputStream(memoryStream))
- {
- cos.WriteRawBytes(new byte[] {0});
- Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
- }
- Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
- Assert.IsFalse(memoryStream.CanWrite); // Disposed
- }
-
- [Test]
- public void Dispose_WithLeaveOpen()
- {
- var memoryStream = new MemoryStream();
- Assert.IsTrue(memoryStream.CanWrite);
- using (var cos = new CodedOutputStream(memoryStream, true))
- {
- cos.WriteRawBytes(new byte[] {0});
- Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
- }
- Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
- Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
- }
-
- [Test]
- public void Dispose_FromByteArray()
- {
- var stream = new CodedOutputStream(new byte[10]);
- stream.Dispose();
- }
-
- [Test]
- public void WriteString_AsciiSmall_MaxUtf8SizeExceedsBuffer()
- {
- var buffer = new byte[5];
- var output = new CodedOutputStream(buffer);
- output.WriteString("ABC");
-
- output.Flush();
-
- // Verify written content
- var input = new CodedInputStream(buffer);
- Assert.AreEqual("ABC", input.ReadString());
- }
-
- [Test]
- public void WriteStringsOfDifferentSizes_Ascii()
- {
- for (int i = 1; i <= 1024; i++)
- {
- var buffer = new byte[4096];
- var output = new CodedOutputStream(buffer);
- var sb = new StringBuilder();
- for (int j = 0; j < i; j++)
- {
- sb.Append((j % 10).ToString()); // incrementing numbers, repeating
- }
- var s = sb.ToString();
- output.WriteString(s);
-
- output.Flush();
-
- // Verify written content
- var input = new CodedInputStream(buffer);
- Assert.AreEqual(s, input.ReadString());
- }
- }
-
- [Test]
- public void WriteStringsOfDifferentSizes_Unicode()
- {
- for (int i = 1; i <= 1024; i++)
- {
- var buffer = new byte[4096];
- var output = new CodedOutputStream(buffer);
- var sb = new StringBuilder();
- for (int j = 0; j < i; j++)
- {
- char c = (char)((j % 10) + 10112);
- sb.Append(c.ToString()); // incrementing unicode numbers, repeating
- }
- var s = sb.ToString();
- output.WriteString(s);
-
- output.Flush();
-
- // Verify written content
- var input = new CodedInputStream(buffer);
-
- Assert.AreEqual(s, input.ReadString());
- }
- }
- }
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using Google.Protobuf.Buffers;
+using NUnit.Framework;
+using System.Text;
+
+namespace Google.Protobuf
+{
+ public class CodedOutputStreamTest
+ {
+ ///
+ /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
+ /// checks that the result matches the given bytes
+ ///
+ private static void AssertWriteVarint(byte[] data, ulong value)
+ {
+ // Only do 32-bit write if the value fits in 32 bits.
+ if ((value >> 32) == 0)
+ {
+ // CodedOutputStream
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawVarint32((uint) value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ // IBufferWriter
+ var bufferWriter = new TestArrayBufferWriter();
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteUInt32((uint) value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
+ // Also try computing size.
+ Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
+ }
+
+ {
+ // CodedOutputStream
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput);
+ output.WriteRawVarint64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ // IBufferWriter
+ var bufferWriter = new TestArrayBufferWriter();
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteUInt64(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+
+ // Also try computing size.
+ Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
+ }
+
+ // Try different buffer sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ // Only do 32-bit write if the value fits in 32 bits.
+ if ((value >> 32) == 0)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output =
+ new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawVarint32((uint) value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ bufferWriter.MaxGrowBy = bufferSize;
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteUInt32((uint) value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawVarint64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ bufferWriter.MaxGrowBy = bufferSize;
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteUInt64(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+
+ }
+ }
+
+ ///
+ /// Tests WriteRawVarint32() and WriteRawVarint64()
+ ///
+ [Test]
+ public void WriteVarint()
+ {
+ AssertWriteVarint(new byte[] {0x00}, 0);
+ AssertWriteVarint(new byte[] {0x01}, 1);
+ AssertWriteVarint(new byte[] {0x7f}, 127);
+ // 14882
+ AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7));
+ // 2961488830
+ AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b},
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x0bL << 28));
+
+ // 64-bit
+ // 7256456126
+ AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b},
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
+ (0x1bL << 28));
+ // 41256202580718336
+ AssertWriteVarint(
+ new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
+ (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
+ (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
+ // 11964378330978735131
+ AssertWriteVarint(
+ new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
+ unchecked((ulong)
+ ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+ (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
+ (0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
+ }
+
+ ///
+ /// Parses the given bytes using WriteRawLittleEndian32() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertWriteLittleEndian32(byte[] data, uint value)
+ {
+ {
+ var rawOutput = new MemoryStream();
+ var output = new CodedOutputStream(rawOutput);
+ output.WriteRawLittleEndian32(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteFixed32(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+
+ // Try different buffer sizes.
+ for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2)
+ {
+ var rawOutput = new MemoryStream();
+ var output = new CodedOutputStream(rawOutput, bufferSize);
+ output.WriteRawLittleEndian32(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ bufferWriter.MaxGrowBy = bufferSize;
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteFixed32(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+ }
+
+ ///
+ /// Parses the given bytes using WriteRawLittleEndian64() and checks
+ /// that the result matches the given value.
+ ///
+ private static void AssertWriteLittleEndian64(byte[] data, ulong value)
+ {
+ {
+ var rawOutput = new MemoryStream();
+ var output = new CodedOutputStream(rawOutput);
+ output.WriteRawLittleEndian64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteFixed64(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2)
+ {
+ var rawOutput = new MemoryStream();
+ var output = new CodedOutputStream(rawOutput, blockSize);
+ output.WriteRawLittleEndian64(value);
+ output.Flush();
+ Assert.AreEqual(data, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ bufferWriter.MaxGrowBy = blockSize;
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteFixed64(value);
+ ctx.Flush();
+ Assert.AreEqual(data, bufferWriter.WrittenSpan.ToArray());
+ }
+ }
+
+ ///
+ /// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
+ ///
+ [Test]
+ public void WriteLittleEndian()
+ {
+ AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678);
+ AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0);
+
+ AssertWriteLittleEndian64(
+ new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12},
+ 0x123456789abcdef0L);
+ AssertWriteLittleEndian64(
+ new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a},
+ 0x9abcdef012345678UL);
+ }
+
+ [Test]
+ public void WriteWholeMessage_VaryingBlockSizes()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ byte[] rawBytes = message.ToByteArray();
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2)
+ {
+ MemoryStream rawOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize);
+ message.WriteTo(output);
+ output.Flush();
+ Assert.AreEqual(rawBytes, rawOutput.ToArray());
+
+ var bufferWriter = new TestArrayBufferWriter();
+ bufferWriter.MaxGrowBy = blockSize;
+ message.WriteTo(bufferWriter);
+ Assert.AreEqual(rawBytes, bufferWriter.WrittenSpan.ToArray());
+ }
+ }
+
+ [Test]
+ public void WriteContext_WritesWithFlushes()
+ {
+ TestAllTypes message = SampleMessages.CreateFullTestAllTypes();
+
+ MemoryStream expectedOutput = new MemoryStream();
+ CodedOutputStream output = new CodedOutputStream(expectedOutput);
+ output.WriteMessage(message);
+ output.Flush();
+ byte[] expectedBytes1 = expectedOutput.ToArray();
+
+ output.WriteMessage(message);
+ output.Flush();
+ byte[] expectedBytes2 = expectedOutput.ToArray();
+
+ var bufferWriter = new TestArrayBufferWriter();
+ WriteContext.Initialize(bufferWriter, out WriteContext ctx);
+ ctx.WriteMessage(message);
+ ctx.Flush();
+ Assert.AreEqual(expectedBytes1, bufferWriter.WrittenSpan.ToArray());
+
+ ctx.WriteMessage(message);
+ ctx.Flush();
+ Assert.AreEqual(expectedBytes2, bufferWriter.WrittenSpan.ToArray());
+ }
+
+ [Test]
+ public void EncodeZigZag32()
+ {
+ Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag32(0));
+ Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag32(-1));
+ Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag32(1));
+ Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag32(-2));
+ Assert.AreEqual(0x7FFFFFFEu, WritingPrimitives.EncodeZigZag32(0x3FFFFFFF));
+ Assert.AreEqual(0x7FFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0xC0000000)));
+ Assert.AreEqual(0xFFFFFFFEu, WritingPrimitives.EncodeZigZag32(0x7FFFFFFF));
+ Assert.AreEqual(0xFFFFFFFFu, WritingPrimitives.EncodeZigZag32(unchecked((int) 0x80000000)));
+ }
+
+ [Test]
+ public void EncodeZigZag64()
+ {
+ Assert.AreEqual(0u, WritingPrimitives.EncodeZigZag64(0));
+ Assert.AreEqual(1u, WritingPrimitives.EncodeZigZag64(-1));
+ Assert.AreEqual(2u, WritingPrimitives.EncodeZigZag64(1));
+ Assert.AreEqual(3u, WritingPrimitives.EncodeZigZag64(-2));
+ Assert.AreEqual(0x000000007FFFFFFEuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL)));
+ Assert.AreEqual(0x000000007FFFFFFFuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL)));
+ Assert.AreEqual(0x00000000FFFFFFFEuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL)));
+ Assert.AreEqual(0x00000000FFFFFFFFuL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL)));
+ Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL)));
+ Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
+ WritingPrimitives.EncodeZigZag64(unchecked((long) 0x8000000000000000UL)));
+ }
+
+ [Test]
+ public void RoundTripZigZag32()
+ {
+ // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
+ // were chosen semi-randomly via keyboard bashing.
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(0)));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(1)));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-1)));
+ Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(14927)));
+ Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag32(WritingPrimitives.EncodeZigZag32(-3612)));
+ }
+
+ [Test]
+ public void RoundTripZigZag64()
+ {
+ Assert.AreEqual(0, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(0)));
+ Assert.AreEqual(1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(1)));
+ Assert.AreEqual(-1, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-1)));
+ Assert.AreEqual(14927, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(14927)));
+ Assert.AreEqual(-3612, ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-3612)));
+
+ Assert.AreEqual(856912304801416L,
+ ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(856912304801416L)));
+ Assert.AreEqual(-75123905439571256L,
+ ParsingPrimitives.DecodeZigZag64(WritingPrimitives.EncodeZigZag64(-75123905439571256L)));
+ }
+
+ [Test]
+ public void TestNegativeEnumNoTag()
+ {
+ Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
+ Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
+
+ byte[] bytes = new byte[10];
+ CodedOutputStream output = new CodedOutputStream(bytes);
+ output.WriteEnum((int) SampleEnum.NegativeValue);
+
+ Assert.AreEqual(0, output.SpaceLeft);
+ Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
+ }
+
+ [Test]
+ public void TestCodedInputOutputPosition()
+ {
+ byte[] content = new byte[110];
+ for (int i = 0; i < content.Length; i++)
+ content[i] = (byte)i;
+
+ byte[] child = new byte[120];
+ {
+ MemoryStream ms = new MemoryStream(child);
+ CodedOutputStream cout = new CodedOutputStream(ms, 20);
+ // Field 11: numeric value: 500
+ cout.WriteTag(11, WireFormat.WireType.Varint);
+ Assert.AreEqual(1, cout.Position);
+ cout.WriteInt32(500);
+ Assert.AreEqual(3, cout.Position);
+ //Field 12: length delimited 120 bytes
+ cout.WriteTag(12, WireFormat.WireType.LengthDelimited);
+ Assert.AreEqual(4, cout.Position);
+ cout.WriteBytes(ByteString.CopyFrom(content));
+ Assert.AreEqual(115, cout.Position);
+ // Field 13: fixed numeric value: 501
+ cout.WriteTag(13, WireFormat.WireType.Fixed32);
+ Assert.AreEqual(116, cout.Position);
+ cout.WriteSFixed32(501);
+ Assert.AreEqual(120, cout.Position);
+ cout.Flush();
+ }
+
+ byte[] bytes = new byte[130];
+ {
+ CodedOutputStream cout = new CodedOutputStream(bytes);
+ // Field 1: numeric value: 500
+ cout.WriteTag(1, WireFormat.WireType.Varint);
+ Assert.AreEqual(1, cout.Position);
+ cout.WriteInt32(500);
+ Assert.AreEqual(3, cout.Position);
+ //Field 2: length delimited 120 bytes
+ cout.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ Assert.AreEqual(4, cout.Position);
+ cout.WriteBytes(ByteString.CopyFrom(child));
+ Assert.AreEqual(125, cout.Position);
+ // Field 3: fixed numeric value: 500
+ cout.WriteTag(3, WireFormat.WireType.Fixed32);
+ Assert.AreEqual(126, cout.Position);
+ cout.WriteSFixed32(501);
+ Assert.AreEqual(130, cout.Position);
+ cout.Flush();
+ }
+ // Now test Input stream:
+ {
+ CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0, false);
+ Assert.AreEqual(0, cin.Position);
+ // Field 1:
+ uint tag = cin.ReadTag();
+ Assert.AreEqual(1, tag >> 3);
+ Assert.AreEqual(1, cin.Position);
+ Assert.AreEqual(500, cin.ReadInt32());
+ Assert.AreEqual(3, cin.Position);
+ //Field 2:
+ tag = cin.ReadTag();
+ Assert.AreEqual(2, tag >> 3);
+ Assert.AreEqual(4, cin.Position);
+ int childlen = cin.ReadLength();
+ Assert.AreEqual(120, childlen);
+ Assert.AreEqual(5, cin.Position);
+ int oldlimit = cin.PushLimit((int)childlen);
+ Assert.AreEqual(5, cin.Position);
+ // Now we are reading child message
+ {
+ // Field 11: numeric value: 500
+ tag = cin.ReadTag();
+ Assert.AreEqual(11, tag >> 3);
+ Assert.AreEqual(6, cin.Position);
+ Assert.AreEqual(500, cin.ReadInt32());
+ Assert.AreEqual(8, cin.Position);
+ //Field 12: length delimited 120 bytes
+ tag = cin.ReadTag();
+ Assert.AreEqual(12, tag >> 3);
+ Assert.AreEqual(9, cin.Position);
+ ByteString bstr = cin.ReadBytes();
+ Assert.AreEqual(110, bstr.Length);
+ Assert.AreEqual((byte) 109, bstr[109]);
+ Assert.AreEqual(120, cin.Position);
+ // Field 13: fixed numeric value: 501
+ tag = cin.ReadTag();
+ Assert.AreEqual(13, tag >> 3);
+ // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit
+ Assert.AreEqual(121, cin.Position);
+ Assert.AreEqual(501, cin.ReadSFixed32());
+ Assert.AreEqual(125, cin.Position);
+ Assert.IsTrue(cin.IsAtEnd);
+ }
+ cin.PopLimit(oldlimit);
+ Assert.AreEqual(125, cin.Position);
+ // Field 3: fixed numeric value: 501
+ tag = cin.ReadTag();
+ Assert.AreEqual(3, tag >> 3);
+ Assert.AreEqual(126, cin.Position);
+ Assert.AreEqual(501, cin.ReadSFixed32());
+ Assert.AreEqual(130, cin.Position);
+ Assert.IsTrue(cin.IsAtEnd);
+ }
+ }
+
+ [Test]
+ public void Dispose_DisposesUnderlyingStream()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanWrite);
+ using (var cos = new CodedOutputStream(memoryStream))
+ {
+ cos.WriteRawBytes(new byte[] {0});
+ Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+ }
+ Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream
+ Assert.IsFalse(memoryStream.CanWrite); // Disposed
+ }
+
+ [Test]
+ public void Dispose_WithLeaveOpen()
+ {
+ var memoryStream = new MemoryStream();
+ Assert.IsTrue(memoryStream.CanWrite);
+ using (var cos = new CodedOutputStream(memoryStream, true))
+ {
+ cos.WriteRawBytes(new byte[] {0});
+ Assert.AreEqual(0, memoryStream.Position); // Not flushed yet
+ }
+ Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream
+ Assert.IsTrue(memoryStream.CanWrite); // We left the stream open
+ }
+
+ [Test]
+ public void Dispose_FromByteArray()
+ {
+ var stream = new CodedOutputStream(new byte[10]);
+ stream.Dispose();
+ }
+
+ [Test]
+ public void WriteString_AsciiSmall_MaxUtf8SizeExceedsBuffer()
+ {
+ var buffer = new byte[5];
+ var output = new CodedOutputStream(buffer);
+ output.WriteString("ABC");
+
+ output.Flush();
+
+ // Verify written content
+ var input = new CodedInputStream(buffer);
+ Assert.AreEqual("ABC", input.ReadString());
+ }
+
+ [Test]
+ public void WriteStringsOfDifferentSizes_Ascii()
+ {
+ for (int i = 1; i <= 1024; i++)
+ {
+ var buffer = new byte[4096];
+ var output = new CodedOutputStream(buffer);
+ var sb = new StringBuilder();
+ for (int j = 0; j < i; j++)
+ {
+ sb.Append((j % 10).ToString()); // incrementing numbers, repeating
+ }
+ var s = sb.ToString();
+ output.WriteString(s);
+
+ output.Flush();
+
+ // Verify written content
+ var input = new CodedInputStream(buffer);
+ Assert.AreEqual(s, input.ReadString());
+ }
+ }
+
+ [Test]
+ public void WriteStringsOfDifferentSizes_Unicode()
+ {
+ for (int i = 1; i <= 1024; i++)
+ {
+ var buffer = new byte[4096];
+ var output = new CodedOutputStream(buffer);
+ var sb = new StringBuilder();
+ for (int j = 0; j < i; j++)
+ {
+ char c = (char)((j % 10) + 10112);
+ sb.Append(c.ToString()); // incrementing unicode numbers, repeating
+ }
+ var s = sb.ToString();
+ output.WriteString(s);
+
+ output.Flush();
+
+ // Verify written content
+ var input = new CodedInputStream(buffer);
+
+ Assert.AreEqual(s, input.ReadString());
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs b/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
index 34d5b9f98c..fd041e0171 100644
--- a/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
+++ b/csharp/src/Google.Protobuf.Test/DeprecatedMemberTest.cs
@@ -1,55 +1,55 @@
-#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;
-using System.Reflection;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-
-namespace Google.Protobuf
-{
- public class DeprecatedMemberTest
- {
- private static void AssertIsDeprecated(MemberInfo member)
- {
- Assert.NotNull(member);
- Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
- }
-
- [Test]
- public void TestDepreatedPrimitiveValue()
- {
- AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
- }
-
- }
-}
+#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;
+using System.Reflection;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+
+namespace Google.Protobuf
+{
+ public class DeprecatedMemberTest
+ {
+ private static void AssertIsDeprecated(MemberInfo member)
+ {
+ Assert.NotNull(member);
+ Assert.IsTrue(member.IsDefined(typeof(ObsoleteAttribute), false), "Member not obsolete: " + member);
+ }
+
+ [Test]
+ public void TestDepreatedPrimitiveValue()
+ {
+ AssertIsDeprecated(typeof(TestDeprecatedFields).GetProperty("DeprecatedInt32"));
+ }
+
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
index d163810b6e..b2c24dd201 100644
--- a/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ExtensionSetTest.cs
@@ -1,196 +1,196 @@
-using System;
-using System.Collections;
-using Google.Protobuf.TestProtos.Proto2;
-using NUnit.Framework;
-
-using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
-
-namespace Google.Protobuf
-{
- public class ExtensionSetTest
- {
- [Test]
- public void EmptyExtensionSet()
- {
- ExtensionSet extensions = new ExtensionSet();
- Assert.AreEqual(0, extensions.CalculateSize());
- }
-
- [Test]
- public void MergeExtensionSet()
- {
- ExtensionSet extensions = null;
- ExtensionSet.Set(ref extensions, OptionalBoolExtension, true);
-
- ExtensionSet other = null;
-
- Assert.IsFalse(ExtensionSet.Has(ref other, OptionalBoolExtension));
- ExtensionSet.MergeFrom(ref other, extensions);
- Assert.IsTrue(ExtensionSet.Has(ref other, OptionalBoolExtension));
- }
-
- [Test]
- public void TestMergeCodedInput()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalBoolExtension, true);
- var serialized = message.ToByteArray();
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertReadingMessage(
- TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),
- serialized,
- other =>
- {
- Assert.AreEqual(message, other);
- Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
- });
- }
-
- [Test]
- public void TestMergeMessage()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalBoolExtension, true);
-
- var other = new TestAllExtensions();
-
- Assert.AreNotEqual(message, other);
- Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
-
- other.MergeFrom(message);
-
- Assert.AreEqual(message, other);
- Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
- }
-
- [Test]
- public void TryMergeFieldFrom_CodedInputStream()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalStringExtension, "abcd");
-
- var input = new CodedInputStream(message.ToByteArray());
- input.ExtensionRegistry = new ExtensionRegistry() { OptionalStringExtension };
- input.ReadTag(); // TryMergeFieldFrom expects that a tag was just read and will inspect the LastTag value
-
- ExtensionSet extensionSet = null;
- // test the legacy overload of TryMergeFieldFrom that takes a CodedInputStream
- Assert.IsTrue(ExtensionSet.TryMergeFieldFrom(ref extensionSet, input));
- Assert.AreEqual("abcd", ExtensionSet.Get(ref extensionSet, OptionalStringExtension));
- }
-
- [Test]
- public void GetSingle()
- {
- var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
- var untypedExtension = new Extension(OptionalNestedMessageExtension.FieldNumber, codec: null);
- var wrongTypedExtension = new Extension(OptionalNestedMessageExtension.FieldNumber, codec: null);
-
- var message = new TestAllExtensions();
-
- var value1 = message.GetExtension(untypedExtension);
- Assert.IsNull(value1);
-
- message.SetExtension(OptionalNestedMessageExtension, extensionValue);
- var value2 = message.GetExtension(untypedExtension);
- Assert.IsNotNull(value2);
-
- var valueBytes = ((IMessage)value2).ToByteArray();
- var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
- Assert.AreEqual(extensionValue, parsedValue);
-
- var ex = Assert.Throws(() => message.GetExtension(wrongTypedExtension));
-
- var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
- "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
- Assert.AreEqual(expectedMessage, ex.Message);
- }
-
- [Test]
- public void GetRepeated()
- {
- var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
- var untypedExtension = new Extension(RepeatedNestedMessageExtension.FieldNumber, codec: null);
- var wrongTypedExtension = new RepeatedExtension(RepeatedNestedMessageExtension.FieldNumber, codec: null);
-
- var message = new TestAllExtensions();
-
- var value1 = message.GetExtension(untypedExtension);
- Assert.IsNull(value1);
-
- var repeatedField = message.GetOrInitializeExtension(RepeatedNestedMessageExtension);
- repeatedField.Add(extensionValue);
-
- var value2 = message.GetExtension(untypedExtension);
- Assert.IsNotNull(value2);
- Assert.AreEqual(1, value2.Count);
-
- var valueBytes = ((IMessage)value2[0]).ToByteArray();
- var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
- Assert.AreEqual(extensionValue, parsedValue);
-
- var ex = Assert.Throws(() => message.GetExtension(wrongTypedExtension));
-
- var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
- "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
- Assert.AreEqual(expectedMessage, ex.Message);
- }
-
- [Test]
- public void TestEquals()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalBoolExtension, true);
-
- var other = new TestAllExtensions();
-
- Assert.AreNotEqual(message, other);
- Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
-
- other.SetExtension(OptionalBoolExtension, true);
-
- Assert.AreEqual(message, other);
- Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
- }
-
- [Test]
- public void TestHashCode()
- {
- var message = new TestAllExtensions();
- var hashCode = message.GetHashCode();
-
- message.SetExtension(OptionalBoolExtension, true);
-
- Assert.AreNotEqual(hashCode, message.GetHashCode());
- }
-
- [Test]
- public void TestClone()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalBoolExtension, true);
-
- var other = message.Clone();
-
- Assert.AreEqual(message, other);
- Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
- }
-
- [Test]
- public void TestDefaultValueRoundTrip()
- {
- var message = new TestAllExtensions();
- message.SetExtension(OptionalBoolExtension, false);
- Assert.IsFalse(message.GetExtension(OptionalBoolExtension));
- Assert.IsTrue(message.HasExtension(OptionalBoolExtension));
-
- var bytes = message.ToByteArray();
- var registry = new ExtensionRegistry { OptionalBoolExtension };
- var parsed = TestAllExtensions.Parser.WithExtensionRegistry(registry).ParseFrom(bytes);
- Assert.IsFalse(parsed.GetExtension(OptionalBoolExtension));
- Assert.IsTrue(parsed.HasExtension(OptionalBoolExtension));
- }
- }
-}
+using System;
+using System.Collections;
+using Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
+
+namespace Google.Protobuf
+{
+ public class ExtensionSetTest
+ {
+ [Test]
+ public void EmptyExtensionSet()
+ {
+ ExtensionSet extensions = new ExtensionSet();
+ Assert.AreEqual(0, extensions.CalculateSize());
+ }
+
+ [Test]
+ public void MergeExtensionSet()
+ {
+ ExtensionSet extensions = null;
+ ExtensionSet.Set(ref extensions, OptionalBoolExtension, true);
+
+ ExtensionSet other = null;
+
+ Assert.IsFalse(ExtensionSet.Has(ref other, OptionalBoolExtension));
+ ExtensionSet.MergeFrom(ref other, extensions);
+ Assert.IsTrue(ExtensionSet.Has(ref other, OptionalBoolExtension));
+ }
+
+ [Test]
+ public void TestMergeCodedInput()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalBoolExtension, true);
+ var serialized = message.ToByteArray();
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { OptionalBoolExtension }),
+ serialized,
+ other =>
+ {
+ Assert.AreEqual(message, other);
+ Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+ });
+ }
+
+ [Test]
+ public void TestMergeMessage()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalBoolExtension, true);
+
+ var other = new TestAllExtensions();
+
+ Assert.AreNotEqual(message, other);
+ Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
+
+ other.MergeFrom(message);
+
+ Assert.AreEqual(message, other);
+ Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+ }
+
+ [Test]
+ public void TryMergeFieldFrom_CodedInputStream()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalStringExtension, "abcd");
+
+ var input = new CodedInputStream(message.ToByteArray());
+ input.ExtensionRegistry = new ExtensionRegistry() { OptionalStringExtension };
+ input.ReadTag(); // TryMergeFieldFrom expects that a tag was just read and will inspect the LastTag value
+
+ ExtensionSet extensionSet = null;
+ // test the legacy overload of TryMergeFieldFrom that takes a CodedInputStream
+ Assert.IsTrue(ExtensionSet.TryMergeFieldFrom(ref extensionSet, input));
+ Assert.AreEqual("abcd", ExtensionSet.Get(ref extensionSet, OptionalStringExtension));
+ }
+
+ [Test]
+ public void GetSingle()
+ {
+ var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
+ var untypedExtension = new Extension(OptionalNestedMessageExtension.FieldNumber, codec: null);
+ var wrongTypedExtension = new Extension(OptionalNestedMessageExtension.FieldNumber, codec: null);
+
+ var message = new TestAllExtensions();
+
+ var value1 = message.GetExtension(untypedExtension);
+ Assert.IsNull(value1);
+
+ message.SetExtension(OptionalNestedMessageExtension, extensionValue);
+ var value2 = message.GetExtension(untypedExtension);
+ Assert.IsNotNull(value2);
+
+ var valueBytes = ((IMessage)value2).ToByteArray();
+ var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
+ Assert.AreEqual(extensionValue, parsedValue);
+
+ var ex = Assert.Throws(() => message.GetExtension(wrongTypedExtension));
+
+ var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
+ "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
+ Assert.AreEqual(expectedMessage, ex.Message);
+ }
+
+ [Test]
+ public void GetRepeated()
+ {
+ var extensionValue = new TestAllTypes.Types.NestedMessage() { Bb = 42 };
+ var untypedExtension = new Extension(RepeatedNestedMessageExtension.FieldNumber, codec: null);
+ var wrongTypedExtension = new RepeatedExtension(RepeatedNestedMessageExtension.FieldNumber, codec: null);
+
+ var message = new TestAllExtensions();
+
+ var value1 = message.GetExtension(untypedExtension);
+ Assert.IsNull(value1);
+
+ var repeatedField = message.GetOrInitializeExtension(RepeatedNestedMessageExtension);
+ repeatedField.Add(extensionValue);
+
+ var value2 = message.GetExtension(untypedExtension);
+ Assert.IsNotNull(value2);
+ Assert.AreEqual(1, value2.Count);
+
+ var valueBytes = ((IMessage)value2[0]).ToByteArray();
+ var parsedValue = TestProtos.Proto2.TestAllTypes.Types.NestedMessage.Parser.ParseFrom(valueBytes);
+ Assert.AreEqual(extensionValue, parsedValue);
+
+ var ex = Assert.Throws(() => message.GetExtension(wrongTypedExtension));
+
+ var expectedMessage = "The stored extension value has a type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes+Types+NestedMessage, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'. " +
+ "This a different from the requested type of 'Google.Protobuf.TestProtos.Proto2.TestAllTypes, Google.Protobuf.Test.TestProtos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604'.";
+ Assert.AreEqual(expectedMessage, ex.Message);
+ }
+
+ [Test]
+ public void TestEquals()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalBoolExtension, true);
+
+ var other = new TestAllExtensions();
+
+ Assert.AreNotEqual(message, other);
+ Assert.AreNotEqual(message.CalculateSize(), other.CalculateSize());
+
+ other.SetExtension(OptionalBoolExtension, true);
+
+ Assert.AreEqual(message, other);
+ Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+ }
+
+ [Test]
+ public void TestHashCode()
+ {
+ var message = new TestAllExtensions();
+ var hashCode = message.GetHashCode();
+
+ message.SetExtension(OptionalBoolExtension, true);
+
+ Assert.AreNotEqual(hashCode, message.GetHashCode());
+ }
+
+ [Test]
+ public void TestClone()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalBoolExtension, true);
+
+ var other = message.Clone();
+
+ Assert.AreEqual(message, other);
+ Assert.AreEqual(message.CalculateSize(), other.CalculateSize());
+ }
+
+ [Test]
+ public void TestDefaultValueRoundTrip()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(OptionalBoolExtension, false);
+ Assert.IsFalse(message.GetExtension(OptionalBoolExtension));
+ Assert.IsTrue(message.HasExtension(OptionalBoolExtension));
+
+ var bytes = message.ToByteArray();
+ var registry = new ExtensionRegistry { OptionalBoolExtension };
+ var parsed = TestAllExtensions.Parser.WithExtensionRegistry(registry).ParseFrom(bytes);
+ Assert.IsFalse(parsed.GetExtension(OptionalBoolExtension));
+ Assert.IsTrue(parsed.HasExtension(OptionalBoolExtension));
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
index 894d914124..fa5f927738 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs
@@ -1,397 +1,397 @@
-using Google.Protobuf.TestProtos.Proto2;
-using Proto2 = Google.Protobuf.TestProtos.Proto2;
-using NUnit.Framework;
-
-using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
-
-namespace Google.Protobuf
-{
- ///
- /// Tests around the generated TestAllTypes message in unittest.proto
- ///
- public partial class GeneratedMessageTest
- {
- [Test]
- public void DefaultProto2Values()
- {
- var message = new TestAllTypes();
- Assert.AreEqual(false, message.OptionalBool);
- Assert.AreEqual(ByteString.Empty, message.OptionalBytes);
- Assert.AreEqual(0.0, message.OptionalDouble);
- Assert.AreEqual(0, message.OptionalFixed32);
- Assert.AreEqual(0L, message.OptionalFixed64);
- Assert.AreEqual(0.0f, message.OptionalFloat);
- Assert.AreEqual(ForeignEnum.ForeignFoo, message.OptionalForeignEnum);
- Assert.IsNull(message.OptionalForeignMessage);
- Assert.AreEqual(ImportEnum.ImportFoo, message.OptionalImportEnum);
- Assert.IsNull(message.OptionalImportMessage);
- Assert.AreEqual(0, message.OptionalInt32);
- Assert.AreEqual(0L, message.OptionalInt64);
- Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.OptionalNestedEnum);
- Assert.IsNull(message.OptionalNestedMessage);
- Assert.IsNull(message.OptionalPublicImportMessage);
- Assert.AreEqual(0, message.OptionalSfixed32);
- Assert.AreEqual(0L, message.OptionalSfixed64);
- Assert.AreEqual(0, message.OptionalSint32);
- Assert.AreEqual(0L, message.OptionalSint64);
- Assert.AreEqual("", message.OptionalString);
- Assert.AreEqual(0U, message.OptionalUint32);
- Assert.AreEqual(0UL, message.OptionalUint64);
-
- // Repeated fields
- Assert.AreEqual(0, message.RepeatedBool.Count);
- Assert.AreEqual(0, message.RepeatedBytes.Count);
- Assert.AreEqual(0, message.RepeatedDouble.Count);
- Assert.AreEqual(0, message.RepeatedFixed32.Count);
- Assert.AreEqual(0, message.RepeatedFixed64.Count);
- Assert.AreEqual(0, message.RepeatedFloat.Count);
- Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
- Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
- Assert.AreEqual(0, message.RepeatedImportEnum.Count);
- Assert.AreEqual(0, message.RepeatedImportMessage.Count);
- Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
- Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
- Assert.AreEqual(0, message.RepeatedSfixed32.Count);
- Assert.AreEqual(0, message.RepeatedSfixed64.Count);
- Assert.AreEqual(0, message.RepeatedSint32.Count);
- Assert.AreEqual(0, message.RepeatedSint64.Count);
- Assert.AreEqual(0, message.RepeatedString.Count);
- Assert.AreEqual(0, message.RepeatedUint32.Count);
- Assert.AreEqual(0, message.RepeatedUint64.Count);
-
- // Oneof fields
- Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
-
- Assert.AreEqual(true, message.DefaultBool);
- Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.DefaultBytes);
- Assert.AreEqual("123", message.DefaultCord);
- Assert.AreEqual(52e3, message.DefaultDouble);
- Assert.AreEqual(47, message.DefaultFixed32);
- Assert.AreEqual(48, message.DefaultFixed64);
- Assert.AreEqual(51.5, message.DefaultFloat);
- Assert.AreEqual(ForeignEnum.ForeignBar, message.DefaultForeignEnum);
- Assert.AreEqual(ImportEnum.ImportBar, message.DefaultImportEnum);
- Assert.AreEqual(41, message.DefaultInt32);
- Assert.AreEqual(42, message.DefaultInt64);
- Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.DefaultNestedEnum);
- Assert.AreEqual(49, message.DefaultSfixed32);
- Assert.AreEqual(-50, message.DefaultSfixed64);
- Assert.AreEqual(-45, message.DefaultSint32);
- Assert.AreEqual(46, message.DefaultSint64);
- Assert.AreEqual("hello", message.DefaultString);
- Assert.AreEqual("abc", message.DefaultStringPiece);
- Assert.AreEqual(43, message.DefaultUint32);
- Assert.AreEqual(44, message.DefaultUint64);
-
- Assert.False(message.HasDefaultBool);
- Assert.False(message.HasDefaultBytes);
- Assert.False(message.HasDefaultCord);
- Assert.False(message.HasDefaultDouble);
- Assert.False(message.HasDefaultFixed32);
- Assert.False(message.HasDefaultFixed64);
- Assert.False(message.HasDefaultFloat);
- Assert.False(message.HasDefaultForeignEnum);
- Assert.False(message.HasDefaultImportEnum);
- Assert.False(message.HasDefaultInt32);
- Assert.False(message.HasDefaultInt64);
- Assert.False(message.HasDefaultNestedEnum);
- Assert.False(message.HasDefaultSfixed32);
- Assert.False(message.HasDefaultSfixed64);
- Assert.False(message.HasDefaultSint32);
- Assert.False(message.HasDefaultSint64);
- Assert.False(message.HasDefaultString);
- Assert.False(message.HasDefaultStringPiece);
- Assert.False(message.HasDefaultUint32);
- Assert.False(message.HasDefaultUint64);
- }
-
- [Test]
- public void DefaultExtensionValues()
- {
- var message = new TestAllExtensions();
- Assert.AreEqual(false, message.GetExtension(OptionalBoolExtension));
- Assert.AreEqual(ByteString.Empty, message.GetExtension(OptionalBytesExtension));
- Assert.AreEqual(0.0, message.GetExtension(OptionalDoubleExtension));
- Assert.AreEqual(0, message.GetExtension(OptionalFixed32Extension));
- Assert.AreEqual(0L, message.GetExtension(OptionalFixed64Extension));
- Assert.AreEqual(0.0f, message.GetExtension(OptionalFloatExtension));
- Assert.AreEqual(ForeignEnum.ForeignFoo, message.GetExtension(OptionalForeignEnumExtension));
- Assert.IsNull(message.GetExtension(OptionalForeignMessageExtension));
- Assert.AreEqual(ImportEnum.ImportFoo, message.GetExtension(OptionalImportEnumExtension));
- Assert.IsNull(message.GetExtension(OptionalImportMessageExtension));
- Assert.AreEqual(0, message.GetExtension(OptionalInt32Extension));
- Assert.AreEqual(0L, message.GetExtension(OptionalInt64Extension));
- Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.GetExtension(OptionalNestedEnumExtension));
- Assert.IsNull(message.GetExtension(OptionalNestedMessageExtension));
- Assert.IsNull(message.GetExtension(OptionalPublicImportMessageExtension));
- Assert.AreEqual(0, message.GetExtension(OptionalSfixed32Extension));
- Assert.AreEqual(0L, message.GetExtension(OptionalSfixed64Extension));
- Assert.AreEqual(0, message.GetExtension(OptionalSint32Extension));
- Assert.AreEqual(0L, message.GetExtension(OptionalSint64Extension));
- Assert.AreEqual("", message.GetExtension(OptionalStringExtension));
- Assert.AreEqual(0U, message.GetExtension(OptionalUint32Extension));
- Assert.AreEqual(0UL, message.GetExtension(OptionalUint64Extension));
-
- // Repeated fields
- Assert.IsNull(message.GetExtension(RepeatedBoolExtension));
- Assert.IsNull(message.GetExtension(RepeatedBytesExtension));
- Assert.IsNull(message.GetExtension(RepeatedDoubleExtension));
- Assert.IsNull(message.GetExtension(RepeatedFixed32Extension));
- Assert.IsNull(message.GetExtension(RepeatedFixed64Extension));
- Assert.IsNull(message.GetExtension(RepeatedFloatExtension));
- Assert.IsNull(message.GetExtension(RepeatedForeignEnumExtension));
- Assert.IsNull(message.GetExtension(RepeatedForeignMessageExtension));
- Assert.IsNull(message.GetExtension(RepeatedImportEnumExtension));
- Assert.IsNull(message.GetExtension(RepeatedImportMessageExtension));
- Assert.IsNull(message.GetExtension(RepeatedNestedEnumExtension));
- Assert.IsNull(message.GetExtension(RepeatedNestedMessageExtension));
- Assert.IsNull(message.GetExtension(RepeatedSfixed32Extension));
- Assert.IsNull(message.GetExtension(RepeatedSfixed64Extension));
- Assert.IsNull(message.GetExtension(RepeatedSint32Extension));
- Assert.IsNull(message.GetExtension(RepeatedSint64Extension));
- Assert.IsNull(message.GetExtension(RepeatedStringExtension));
- Assert.IsNull(message.GetExtension(RepeatedUint32Extension));
- Assert.IsNull(message.GetExtension(RepeatedUint64Extension));
-
- // Oneof fields
- Assert.AreEqual(0, message.GetExtension(OneofUint32Extension));
- Assert.AreEqual("", message.GetExtension(OneofStringExtension));
- Assert.AreEqual(ByteString.Empty, message.GetExtension(OneofBytesExtension));
- Assert.IsNull(message.GetExtension(OneofNestedMessageExtension));
-
- Assert.AreEqual(true, message.GetExtension(DefaultBoolExtension));
- Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.GetExtension(DefaultBytesExtension));
- Assert.AreEqual("123", message.GetExtension(DefaultCordExtension));
- Assert.AreEqual(52e3, message.GetExtension(DefaultDoubleExtension));
- Assert.AreEqual(47, message.GetExtension(DefaultFixed32Extension));
- Assert.AreEqual(48, message.GetExtension(DefaultFixed64Extension));
- Assert.AreEqual(51.5, message.GetExtension(DefaultFloatExtension));
- Assert.AreEqual(ForeignEnum.ForeignBar, message.GetExtension(DefaultForeignEnumExtension));
- Assert.AreEqual(ImportEnum.ImportBar, message.GetExtension(DefaultImportEnumExtension));
- Assert.AreEqual(41, message.GetExtension(DefaultInt32Extension));
- Assert.AreEqual(42, message.GetExtension(DefaultInt64Extension));
- Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.GetExtension(DefaultNestedEnumExtension));
- Assert.AreEqual(49, message.GetExtension(DefaultSfixed32Extension));
- Assert.AreEqual(-50, message.GetExtension(DefaultSfixed64Extension));
- Assert.AreEqual(-45, message.GetExtension(DefaultSint32Extension));
- Assert.AreEqual(46, message.GetExtension(DefaultSint64Extension));
- Assert.AreEqual("hello", message.GetExtension(DefaultStringExtension));
- Assert.AreEqual("abc", message.GetExtension(DefaultStringPieceExtension));
- Assert.AreEqual(43, message.GetExtension(DefaultUint32Extension));
- Assert.AreEqual(44, message.GetExtension(DefaultUint64Extension));
-
- Assert.False(message.HasExtension(DefaultBoolExtension));
- Assert.False(message.HasExtension(DefaultBytesExtension));
- Assert.False(message.HasExtension(DefaultCordExtension));
- Assert.False(message.HasExtension(DefaultDoubleExtension));
- Assert.False(message.HasExtension(DefaultFixed32Extension));
- Assert.False(message.HasExtension(DefaultFixed64Extension));
- Assert.False(message.HasExtension(DefaultFloatExtension));
- Assert.False(message.HasExtension(DefaultForeignEnumExtension));
- Assert.False(message.HasExtension(DefaultImportEnumExtension));
- Assert.False(message.HasExtension(DefaultInt32Extension));
- Assert.False(message.HasExtension(DefaultInt64Extension));
- Assert.False(message.HasExtension(DefaultNestedEnumExtension));
- Assert.False(message.HasExtension(DefaultSfixed32Extension));
- Assert.False(message.HasExtension(DefaultSfixed64Extension));
- Assert.False(message.HasExtension(DefaultSint32Extension));
- Assert.False(message.HasExtension(DefaultSint64Extension));
- Assert.False(message.HasExtension(DefaultStringExtension));
- Assert.False(message.HasExtension(DefaultStringPieceExtension));
- Assert.False(message.HasExtension(DefaultUint32Extension));
- Assert.False(message.HasExtension(DefaultUint64Extension));
- }
-
- [Test]
- public void FieldPresence()
- {
- var message = new TestAllTypes();
-
- Assert.False(message.HasOptionalBool);
- Assert.False(message.OptionalBool);
-
- message.OptionalBool = true;
-
- Assert.True(message.HasOptionalBool);
- Assert.True(message.OptionalBool);
-
- message.OptionalBool = false;
-
- Assert.True(message.HasOptionalBool);
- Assert.False(message.OptionalBool);
-
- message.ClearOptionalBool();
-
- Assert.False(message.HasOptionalBool);
- Assert.False(message.OptionalBool);
-
- Assert.False(message.HasDefaultBool);
- Assert.True(message.DefaultBool);
-
- message.DefaultBool = false;
-
- Assert.True(message.HasDefaultBool);
- Assert.False(message.DefaultBool);
-
- message.DefaultBool = true;
-
- Assert.True(message.HasDefaultBool);
- Assert.True(message.DefaultBool);
-
- message.ClearDefaultBool();
-
- Assert.False(message.HasDefaultBool);
- Assert.True(message.DefaultBool);
- }
-
- [Test]
- public void RequiredFields()
- {
- var message = new TestRequired();
- Assert.False(message.IsInitialized());
-
- message.A = 1;
- message.B = 2;
- message.C = 3;
-
- Assert.True(message.IsInitialized());
- }
-
- ///
- /// Code was accidentally left in message parser that threw exceptions when missing required fields after parsing.
- /// We've decided to not throw exceptions on missing fields, instead leaving it up to the consumer how they
- /// want to check and handle missing fields.
- ///
- [Test]
- public void RequiredFieldsNoThrow()
- {
- Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser, new byte[0], m => { }));
- Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser as MessageParser, new byte[0], m => { }));
- }
-
- [Test]
- public void RequiredFieldsInExtensions()
- {
- var message = new TestAllExtensions();
- Assert.True(message.IsInitialized());
-
- message.SetExtension(TestRequired.Extensions.Single, new TestRequired());
-
- Assert.False(message.IsInitialized());
-
- var extensionMessage = message.GetExtension(TestRequired.Extensions.Single);
- extensionMessage.A = 1;
- extensionMessage.B = 2;
- extensionMessage.C = 3;
-
- Assert.True(message.IsInitialized());
-
- message.GetOrInitializeExtension(TestRequired.Extensions.Multi);
-
- Assert.True(message.IsInitialized());
-
- message.GetExtension(TestRequired.Extensions.Multi).Add(new TestRequired());
-
- Assert.False(message.IsInitialized());
-
- extensionMessage = message.GetExtension(TestRequired.Extensions.Multi)[0];
- extensionMessage.A = 1;
- extensionMessage.B = 2;
- extensionMessage.C = 3;
-
- Assert.True(message.IsInitialized());
-
- message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
-
- Assert.True(message.IsInitialized());
-
- message.GetOrInitializeExtension(UnittestExtensions.RepeatedBoolExtension).Add(true);
-
- Assert.True(message.IsInitialized());
- }
-
- [Test]
- public void RequiredFieldInNestedMessageMapValue()
- {
- var message = new TestRequiredMap();
- message.Foo.Add(0, new TestRequiredMap.Types.NestedMessage());
-
- Assert.False(message.IsInitialized());
-
- message.Foo[0].RequiredInt32 = 12;
-
- Assert.True(message.IsInitialized());
- }
-
- [Test]
- public void RoundTrip_Groups()
- {
- var message = new TestAllTypes
- {
- OptionalGroup = new TestAllTypes.Types.OptionalGroup
- {
- A = 10
- },
- RepeatedGroup =
- {
- new TestAllTypes.Types.RepeatedGroup { A = 10 },
- new TestAllTypes.Types.RepeatedGroup { A = 20 },
- new TestAllTypes.Types.RepeatedGroup { A = 30 }
- }
- };
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);
- }
-
- [Test]
- public void RoundTrip_ExtensionGroups()
- {
- var message = new TestAllExtensions();
- message.SetExtension(UnittestExtensions.OptionalGroupExtension, new OptionalGroup_extension { A = 10 });
- message.GetOrInitializeExtension(UnittestExtensions.RepeatedGroupExtension).AddRange(new[]
- {
- new RepeatedGroup_extension { A = 10 },
- new RepeatedGroup_extension { A = 20 },
- new RepeatedGroup_extension { A = 30 }
- });
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(
- TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),
- message);
- }
-
- [Test]
- public void RoundTrip_NestedExtensionGroup()
- {
- var message = new TestGroupExtension();
- message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(
- TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),
- message);
- }
-
- [Test]
- public void RoundTrip_ParseUsingCodedInput()
- {
- var message = new TestAllExtensions();
- message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
- byte[] bytes = message.ToByteArray();
- using (CodedInputStream input = new CodedInputStream(bytes))
- {
- var parsed = TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalBoolExtension }).ParseFrom(input);
- Assert.AreEqual(message, parsed);
- }
- }
- }
-}
+using Google.Protobuf.TestProtos.Proto2;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+
+using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions;
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests around the generated TestAllTypes message in unittest.proto
+ ///
+ public partial class GeneratedMessageTest
+ {
+ [Test]
+ public void DefaultProto2Values()
+ {
+ var message = new TestAllTypes();
+ Assert.AreEqual(false, message.OptionalBool);
+ Assert.AreEqual(ByteString.Empty, message.OptionalBytes);
+ Assert.AreEqual(0.0, message.OptionalDouble);
+ Assert.AreEqual(0, message.OptionalFixed32);
+ Assert.AreEqual(0L, message.OptionalFixed64);
+ Assert.AreEqual(0.0f, message.OptionalFloat);
+ Assert.AreEqual(ForeignEnum.ForeignFoo, message.OptionalForeignEnum);
+ Assert.IsNull(message.OptionalForeignMessage);
+ Assert.AreEqual(ImportEnum.ImportFoo, message.OptionalImportEnum);
+ Assert.IsNull(message.OptionalImportMessage);
+ Assert.AreEqual(0, message.OptionalInt32);
+ Assert.AreEqual(0L, message.OptionalInt64);
+ Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.OptionalNestedEnum);
+ Assert.IsNull(message.OptionalNestedMessage);
+ Assert.IsNull(message.OptionalPublicImportMessage);
+ Assert.AreEqual(0, message.OptionalSfixed32);
+ Assert.AreEqual(0L, message.OptionalSfixed64);
+ Assert.AreEqual(0, message.OptionalSint32);
+ Assert.AreEqual(0L, message.OptionalSint64);
+ Assert.AreEqual("", message.OptionalString);
+ Assert.AreEqual(0U, message.OptionalUint32);
+ Assert.AreEqual(0UL, message.OptionalUint64);
+
+ // Repeated fields
+ Assert.AreEqual(0, message.RepeatedBool.Count);
+ Assert.AreEqual(0, message.RepeatedBytes.Count);
+ Assert.AreEqual(0, message.RepeatedDouble.Count);
+ Assert.AreEqual(0, message.RepeatedFixed32.Count);
+ Assert.AreEqual(0, message.RepeatedFixed64.Count);
+ Assert.AreEqual(0, message.RepeatedFloat.Count);
+ Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+ Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+ Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+ Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+ Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+ Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+ Assert.AreEqual(0, message.RepeatedSint32.Count);
+ Assert.AreEqual(0, message.RepeatedSint64.Count);
+ Assert.AreEqual(0, message.RepeatedString.Count);
+ Assert.AreEqual(0, message.RepeatedUint32.Count);
+ Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+ // Oneof fields
+ Assert.AreEqual(Proto2.TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+
+ Assert.AreEqual(true, message.DefaultBool);
+ Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.DefaultBytes);
+ Assert.AreEqual("123", message.DefaultCord);
+ Assert.AreEqual(52e3, message.DefaultDouble);
+ Assert.AreEqual(47, message.DefaultFixed32);
+ Assert.AreEqual(48, message.DefaultFixed64);
+ Assert.AreEqual(51.5, message.DefaultFloat);
+ Assert.AreEqual(ForeignEnum.ForeignBar, message.DefaultForeignEnum);
+ Assert.AreEqual(ImportEnum.ImportBar, message.DefaultImportEnum);
+ Assert.AreEqual(41, message.DefaultInt32);
+ Assert.AreEqual(42, message.DefaultInt64);
+ Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.DefaultNestedEnum);
+ Assert.AreEqual(49, message.DefaultSfixed32);
+ Assert.AreEqual(-50, message.DefaultSfixed64);
+ Assert.AreEqual(-45, message.DefaultSint32);
+ Assert.AreEqual(46, message.DefaultSint64);
+ Assert.AreEqual("hello", message.DefaultString);
+ Assert.AreEqual("abc", message.DefaultStringPiece);
+ Assert.AreEqual(43, message.DefaultUint32);
+ Assert.AreEqual(44, message.DefaultUint64);
+
+ Assert.False(message.HasDefaultBool);
+ Assert.False(message.HasDefaultBytes);
+ Assert.False(message.HasDefaultCord);
+ Assert.False(message.HasDefaultDouble);
+ Assert.False(message.HasDefaultFixed32);
+ Assert.False(message.HasDefaultFixed64);
+ Assert.False(message.HasDefaultFloat);
+ Assert.False(message.HasDefaultForeignEnum);
+ Assert.False(message.HasDefaultImportEnum);
+ Assert.False(message.HasDefaultInt32);
+ Assert.False(message.HasDefaultInt64);
+ Assert.False(message.HasDefaultNestedEnum);
+ Assert.False(message.HasDefaultSfixed32);
+ Assert.False(message.HasDefaultSfixed64);
+ Assert.False(message.HasDefaultSint32);
+ Assert.False(message.HasDefaultSint64);
+ Assert.False(message.HasDefaultString);
+ Assert.False(message.HasDefaultStringPiece);
+ Assert.False(message.HasDefaultUint32);
+ Assert.False(message.HasDefaultUint64);
+ }
+
+ [Test]
+ public void DefaultExtensionValues()
+ {
+ var message = new TestAllExtensions();
+ Assert.AreEqual(false, message.GetExtension(OptionalBoolExtension));
+ Assert.AreEqual(ByteString.Empty, message.GetExtension(OptionalBytesExtension));
+ Assert.AreEqual(0.0, message.GetExtension(OptionalDoubleExtension));
+ Assert.AreEqual(0, message.GetExtension(OptionalFixed32Extension));
+ Assert.AreEqual(0L, message.GetExtension(OptionalFixed64Extension));
+ Assert.AreEqual(0.0f, message.GetExtension(OptionalFloatExtension));
+ Assert.AreEqual(ForeignEnum.ForeignFoo, message.GetExtension(OptionalForeignEnumExtension));
+ Assert.IsNull(message.GetExtension(OptionalForeignMessageExtension));
+ Assert.AreEqual(ImportEnum.ImportFoo, message.GetExtension(OptionalImportEnumExtension));
+ Assert.IsNull(message.GetExtension(OptionalImportMessageExtension));
+ Assert.AreEqual(0, message.GetExtension(OptionalInt32Extension));
+ Assert.AreEqual(0L, message.GetExtension(OptionalInt64Extension));
+ Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Foo, message.GetExtension(OptionalNestedEnumExtension));
+ Assert.IsNull(message.GetExtension(OptionalNestedMessageExtension));
+ Assert.IsNull(message.GetExtension(OptionalPublicImportMessageExtension));
+ Assert.AreEqual(0, message.GetExtension(OptionalSfixed32Extension));
+ Assert.AreEqual(0L, message.GetExtension(OptionalSfixed64Extension));
+ Assert.AreEqual(0, message.GetExtension(OptionalSint32Extension));
+ Assert.AreEqual(0L, message.GetExtension(OptionalSint64Extension));
+ Assert.AreEqual("", message.GetExtension(OptionalStringExtension));
+ Assert.AreEqual(0U, message.GetExtension(OptionalUint32Extension));
+ Assert.AreEqual(0UL, message.GetExtension(OptionalUint64Extension));
+
+ // Repeated fields
+ Assert.IsNull(message.GetExtension(RepeatedBoolExtension));
+ Assert.IsNull(message.GetExtension(RepeatedBytesExtension));
+ Assert.IsNull(message.GetExtension(RepeatedDoubleExtension));
+ Assert.IsNull(message.GetExtension(RepeatedFixed32Extension));
+ Assert.IsNull(message.GetExtension(RepeatedFixed64Extension));
+ Assert.IsNull(message.GetExtension(RepeatedFloatExtension));
+ Assert.IsNull(message.GetExtension(RepeatedForeignEnumExtension));
+ Assert.IsNull(message.GetExtension(RepeatedForeignMessageExtension));
+ Assert.IsNull(message.GetExtension(RepeatedImportEnumExtension));
+ Assert.IsNull(message.GetExtension(RepeatedImportMessageExtension));
+ Assert.IsNull(message.GetExtension(RepeatedNestedEnumExtension));
+ Assert.IsNull(message.GetExtension(RepeatedNestedMessageExtension));
+ Assert.IsNull(message.GetExtension(RepeatedSfixed32Extension));
+ Assert.IsNull(message.GetExtension(RepeatedSfixed64Extension));
+ Assert.IsNull(message.GetExtension(RepeatedSint32Extension));
+ Assert.IsNull(message.GetExtension(RepeatedSint64Extension));
+ Assert.IsNull(message.GetExtension(RepeatedStringExtension));
+ Assert.IsNull(message.GetExtension(RepeatedUint32Extension));
+ Assert.IsNull(message.GetExtension(RepeatedUint64Extension));
+
+ // Oneof fields
+ Assert.AreEqual(0, message.GetExtension(OneofUint32Extension));
+ Assert.AreEqual("", message.GetExtension(OneofStringExtension));
+ Assert.AreEqual(ByteString.Empty, message.GetExtension(OneofBytesExtension));
+ Assert.IsNull(message.GetExtension(OneofNestedMessageExtension));
+
+ Assert.AreEqual(true, message.GetExtension(DefaultBoolExtension));
+ Assert.AreEqual(ByteString.CopyFromUtf8("world"), message.GetExtension(DefaultBytesExtension));
+ Assert.AreEqual("123", message.GetExtension(DefaultCordExtension));
+ Assert.AreEqual(52e3, message.GetExtension(DefaultDoubleExtension));
+ Assert.AreEqual(47, message.GetExtension(DefaultFixed32Extension));
+ Assert.AreEqual(48, message.GetExtension(DefaultFixed64Extension));
+ Assert.AreEqual(51.5, message.GetExtension(DefaultFloatExtension));
+ Assert.AreEqual(ForeignEnum.ForeignBar, message.GetExtension(DefaultForeignEnumExtension));
+ Assert.AreEqual(ImportEnum.ImportBar, message.GetExtension(DefaultImportEnumExtension));
+ Assert.AreEqual(41, message.GetExtension(DefaultInt32Extension));
+ Assert.AreEqual(42, message.GetExtension(DefaultInt64Extension));
+ Assert.AreEqual(Proto2.TestAllTypes.Types.NestedEnum.Bar, message.GetExtension(DefaultNestedEnumExtension));
+ Assert.AreEqual(49, message.GetExtension(DefaultSfixed32Extension));
+ Assert.AreEqual(-50, message.GetExtension(DefaultSfixed64Extension));
+ Assert.AreEqual(-45, message.GetExtension(DefaultSint32Extension));
+ Assert.AreEqual(46, message.GetExtension(DefaultSint64Extension));
+ Assert.AreEqual("hello", message.GetExtension(DefaultStringExtension));
+ Assert.AreEqual("abc", message.GetExtension(DefaultStringPieceExtension));
+ Assert.AreEqual(43, message.GetExtension(DefaultUint32Extension));
+ Assert.AreEqual(44, message.GetExtension(DefaultUint64Extension));
+
+ Assert.False(message.HasExtension(DefaultBoolExtension));
+ Assert.False(message.HasExtension(DefaultBytesExtension));
+ Assert.False(message.HasExtension(DefaultCordExtension));
+ Assert.False(message.HasExtension(DefaultDoubleExtension));
+ Assert.False(message.HasExtension(DefaultFixed32Extension));
+ Assert.False(message.HasExtension(DefaultFixed64Extension));
+ Assert.False(message.HasExtension(DefaultFloatExtension));
+ Assert.False(message.HasExtension(DefaultForeignEnumExtension));
+ Assert.False(message.HasExtension(DefaultImportEnumExtension));
+ Assert.False(message.HasExtension(DefaultInt32Extension));
+ Assert.False(message.HasExtension(DefaultInt64Extension));
+ Assert.False(message.HasExtension(DefaultNestedEnumExtension));
+ Assert.False(message.HasExtension(DefaultSfixed32Extension));
+ Assert.False(message.HasExtension(DefaultSfixed64Extension));
+ Assert.False(message.HasExtension(DefaultSint32Extension));
+ Assert.False(message.HasExtension(DefaultSint64Extension));
+ Assert.False(message.HasExtension(DefaultStringExtension));
+ Assert.False(message.HasExtension(DefaultStringPieceExtension));
+ Assert.False(message.HasExtension(DefaultUint32Extension));
+ Assert.False(message.HasExtension(DefaultUint64Extension));
+ }
+
+ [Test]
+ public void FieldPresence()
+ {
+ var message = new TestAllTypes();
+
+ Assert.False(message.HasOptionalBool);
+ Assert.False(message.OptionalBool);
+
+ message.OptionalBool = true;
+
+ Assert.True(message.HasOptionalBool);
+ Assert.True(message.OptionalBool);
+
+ message.OptionalBool = false;
+
+ Assert.True(message.HasOptionalBool);
+ Assert.False(message.OptionalBool);
+
+ message.ClearOptionalBool();
+
+ Assert.False(message.HasOptionalBool);
+ Assert.False(message.OptionalBool);
+
+ Assert.False(message.HasDefaultBool);
+ Assert.True(message.DefaultBool);
+
+ message.DefaultBool = false;
+
+ Assert.True(message.HasDefaultBool);
+ Assert.False(message.DefaultBool);
+
+ message.DefaultBool = true;
+
+ Assert.True(message.HasDefaultBool);
+ Assert.True(message.DefaultBool);
+
+ message.ClearDefaultBool();
+
+ Assert.False(message.HasDefaultBool);
+ Assert.True(message.DefaultBool);
+ }
+
+ [Test]
+ public void RequiredFields()
+ {
+ var message = new TestRequired();
+ Assert.False(message.IsInitialized());
+
+ message.A = 1;
+ message.B = 2;
+ message.C = 3;
+
+ Assert.True(message.IsInitialized());
+ }
+
+ ///
+ /// Code was accidentally left in message parser that threw exceptions when missing required fields after parsing.
+ /// We've decided to not throw exceptions on missing fields, instead leaving it up to the consumer how they
+ /// want to check and handle missing fields.
+ ///
+ [Test]
+ public void RequiredFieldsNoThrow()
+ {
+ Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser, new byte[0], m => { }));
+ Assert.DoesNotThrow(() => MessageParsingHelpers.AssertReadingMessage(TestRequired.Parser as MessageParser, new byte[0], m => { }));
+ }
+
+ [Test]
+ public void RequiredFieldsInExtensions()
+ {
+ var message = new TestAllExtensions();
+ Assert.True(message.IsInitialized());
+
+ message.SetExtension(TestRequired.Extensions.Single, new TestRequired());
+
+ Assert.False(message.IsInitialized());
+
+ var extensionMessage = message.GetExtension(TestRequired.Extensions.Single);
+ extensionMessage.A = 1;
+ extensionMessage.B = 2;
+ extensionMessage.C = 3;
+
+ Assert.True(message.IsInitialized());
+
+ message.GetOrInitializeExtension(TestRequired.Extensions.Multi);
+
+ Assert.True(message.IsInitialized());
+
+ message.GetExtension(TestRequired.Extensions.Multi).Add(new TestRequired());
+
+ Assert.False(message.IsInitialized());
+
+ extensionMessage = message.GetExtension(TestRequired.Extensions.Multi)[0];
+ extensionMessage.A = 1;
+ extensionMessage.B = 2;
+ extensionMessage.C = 3;
+
+ Assert.True(message.IsInitialized());
+
+ message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
+
+ Assert.True(message.IsInitialized());
+
+ message.GetOrInitializeExtension(UnittestExtensions.RepeatedBoolExtension).Add(true);
+
+ Assert.True(message.IsInitialized());
+ }
+
+ [Test]
+ public void RequiredFieldInNestedMessageMapValue()
+ {
+ var message = new TestRequiredMap();
+ message.Foo.Add(0, new TestRequiredMap.Types.NestedMessage());
+
+ Assert.False(message.IsInitialized());
+
+ message.Foo[0].RequiredInt32 = 12;
+
+ Assert.True(message.IsInitialized());
+ }
+
+ [Test]
+ public void RoundTrip_Groups()
+ {
+ var message = new TestAllTypes
+ {
+ OptionalGroup = new TestAllTypes.Types.OptionalGroup
+ {
+ A = 10
+ },
+ RepeatedGroup =
+ {
+ new TestAllTypes.Types.RepeatedGroup { A = 10 },
+ new TestAllTypes.Types.RepeatedGroup { A = 20 },
+ new TestAllTypes.Types.RepeatedGroup { A = 30 }
+ }
+ };
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(Proto2.TestAllTypes.Parser, message);
+ }
+
+ [Test]
+ public void RoundTrip_ExtensionGroups()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(UnittestExtensions.OptionalGroupExtension, new OptionalGroup_extension { A = 10 });
+ message.GetOrInitializeExtension(UnittestExtensions.RepeatedGroupExtension).AddRange(new[]
+ {
+ new RepeatedGroup_extension { A = 10 },
+ new RepeatedGroup_extension { A = 20 },
+ new RepeatedGroup_extension { A = 30 }
+ });
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(
+ TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalGroupExtension, UnittestExtensions.RepeatedGroupExtension }),
+ message);
+ }
+
+ [Test]
+ public void RoundTrip_NestedExtensionGroup()
+ {
+ var message = new TestGroupExtension();
+ message.SetExtension(TestNestedExtension.Extensions.OptionalGroupExtension, new TestNestedExtension.Types.OptionalGroup_extension { A = 10 });
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(
+ TestGroupExtension.Parser.WithExtensionRegistry(new ExtensionRegistry() { TestNestedExtension.Extensions.OptionalGroupExtension }),
+ message);
+ }
+
+ [Test]
+ public void RoundTrip_ParseUsingCodedInput()
+ {
+ var message = new TestAllExtensions();
+ message.SetExtension(UnittestExtensions.OptionalBoolExtension, true);
+ byte[] bytes = message.ToByteArray();
+ using (CodedInputStream input = new CodedInputStream(bytes))
+ {
+ var parsed = TestAllExtensions.Parser.WithExtensionRegistry(new ExtensionRegistry() { UnittestExtensions.OptionalBoolExtension }).ParseFrom(input);
+ Assert.AreEqual(message, parsed);
+ }
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 06af5e9e9a..41a0b914cb 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -1,799 +1,799 @@
-#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;
-using System.IO;
-using Google.Protobuf.TestProtos;
-using Proto2 = Google.Protobuf.TestProtos.Proto2;
-using NUnit.Framework;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using Google.Protobuf.WellKnownTypes;
-
-namespace Google.Protobuf
-{
- ///
- /// Tests around the generated TestAllTypes message.
- ///
- public partial class GeneratedMessageTest
- {
- [Test]
- public void EmptyMessageFieldDistinctFromMissingMessageField()
- {
- // This demonstrates what we're really interested in...
- var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
- var message2 = new TestAllTypes(); // SingleForeignMessage is null
- EqualityTester.AssertInequality(message1, message2);
- }
-
- [Test]
- public void DefaultValues()
- {
- // Single fields
- var message = new TestAllTypes();
- Assert.AreEqual(false, message.SingleBool);
- Assert.AreEqual(ByteString.Empty, message.SingleBytes);
- Assert.AreEqual(0.0, message.SingleDouble);
- Assert.AreEqual(0, message.SingleFixed32);
- Assert.AreEqual(0L, message.SingleFixed64);
- Assert.AreEqual(0.0f, message.SingleFloat);
- Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
- Assert.IsNull(message.SingleForeignMessage);
- Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
- Assert.IsNull(message.SingleImportMessage);
- Assert.AreEqual(0, message.SingleInt32);
- Assert.AreEqual(0L, message.SingleInt64);
- Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
- Assert.IsNull(message.SingleNestedMessage);
- Assert.IsNull(message.SinglePublicImportMessage);
- Assert.AreEqual(0, message.SingleSfixed32);
- Assert.AreEqual(0L, message.SingleSfixed64);
- Assert.AreEqual(0, message.SingleSint32);
- Assert.AreEqual(0L, message.SingleSint64);
- Assert.AreEqual("", message.SingleString);
- Assert.AreEqual(0U, message.SingleUint32);
- Assert.AreEqual(0UL, message.SingleUint64);
-
- // Repeated fields
- Assert.AreEqual(0, message.RepeatedBool.Count);
- Assert.AreEqual(0, message.RepeatedBytes.Count);
- Assert.AreEqual(0, message.RepeatedDouble.Count);
- Assert.AreEqual(0, message.RepeatedFixed32.Count);
- Assert.AreEqual(0, message.RepeatedFixed64.Count);
- Assert.AreEqual(0, message.RepeatedFloat.Count);
- Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
- Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
- Assert.AreEqual(0, message.RepeatedImportEnum.Count);
- Assert.AreEqual(0, message.RepeatedImportMessage.Count);
- Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
- Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
- Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
- Assert.AreEqual(0, message.RepeatedSfixed32.Count);
- Assert.AreEqual(0, message.RepeatedSfixed64.Count);
- Assert.AreEqual(0, message.RepeatedSint32.Count);
- Assert.AreEqual(0, message.RepeatedSint64.Count);
- Assert.AreEqual(0, message.RepeatedString.Count);
- Assert.AreEqual(0, message.RepeatedUint32.Count);
- Assert.AreEqual(0, message.RepeatedUint64.Count);
-
- // Oneof fields
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- }
-
- [Test]
- public void NullStringAndBytesRejected()
- {
- var message = new TestAllTypes();
- Assert.Throws(() => message.SingleString = null);
- Assert.Throws(() => message.OneofString = null);
- Assert.Throws(() => message.SingleBytes = null);
- Assert.Throws(() => message.OneofBytes = null);
- }
-
- [Test]
- public void RoundTrip_Empty()
- {
- var message = new TestAllTypes();
- // Without setting any values, there's nothing to write.
- byte[] bytes = message.ToByteArray();
- Assert.AreEqual(0, bytes.Length);
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
- }
-
- [Test]
- public void RoundTrip_SingleValues()
- {
- var message = new TestAllTypes
- {
- SingleBool = true,
- SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
- SingleDouble = 23.5,
- SingleFixed32 = 23,
- SingleFixed64 = 1234567890123,
- SingleFloat = 12.25f,
- SingleForeignEnum = ForeignEnum.ForeignBar,
- SingleForeignMessage = new ForeignMessage { C = 10 },
- SingleImportEnum = ImportEnum.ImportBaz,
- SingleImportMessage = new ImportMessage { D = 20 },
- SingleInt32 = 100,
- SingleInt64 = 3210987654321,
- SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
- SinglePublicImportMessage = new PublicImportMessage { E = 54 },
- SingleSfixed32 = -123,
- SingleSfixed64 = -12345678901234,
- SingleSint32 = -456,
- SingleSint64 = -12345678901235,
- SingleString = "test",
- SingleUint32 = uint.MaxValue,
- SingleUint64 = ulong.MaxValue
- };
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
- }
-
- [Test]
- public void RoundTrip_RepeatedValues()
- {
- var message = new TestAllTypes
- {
- RepeatedBool = { true, false },
- RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
- RepeatedDouble = { -12.25, 23.5 },
- RepeatedFixed32 = { uint.MaxValue, 23 },
- RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
- RepeatedFloat = { 100f, 12.25f },
- RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
- RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
- RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified },
- RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
- RepeatedInt32 = { 100, 200 },
- RepeatedInt64 = { 3210987654321, long.MaxValue },
- RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
- RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
- RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
- RepeatedSfixed32 = { -123, 123 },
- RepeatedSfixed64 = { -12345678901234, 12345678901234 },
- RepeatedSint32 = { -456, 100 },
- RepeatedSint64 = { -12345678901235, 123 },
- RepeatedString = { "foo", "bar" },
- RepeatedUint32 = { uint.MaxValue, uint.MinValue },
- RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
- };
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
- }
-
- // Note that not every map within map_unittest_proto3 is used. They all go through very
- // similar code paths. The fact that all maps are present is validation that we have codecs
- // for every type.
- [Test]
- public void RoundTrip_Maps()
- {
- var message = new TestMap
- {
- MapBoolBool = {
- { false, true },
- { true, false }
- },
- MapInt32Bytes = {
- { 5, ByteString.CopyFrom(6, 7, 8) },
- { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
- { 10, ByteString.Empty }
- },
- MapInt32ForeignMessage = {
- { 0, new ForeignMessage { C = 10 } },
- { 5, new ForeignMessage() },
- },
- MapInt32Enum = {
- { 1, MapEnum.Bar },
- { 2000, MapEnum.Foo }
- }
- };
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
- }
-
- [Test]
- public void MapWithEmptyEntry()
- {
- var message = new TestMap
- {
- MapInt32Bytes = { { 0, ByteString.Empty } }
- };
-
- byte[] bytes = message.ToByteArray();
- Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- bytes,
- parsed=>
- {
- Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
- Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
- });
- }
-
- [Test]
- public void MapWithOnlyValue()
- {
- // Hand-craft the stream to contain a single entry with just a value.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
- var nestedMessage = new ForeignMessage { C = 20 };
- // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
- output.WriteLength(2 + nestedMessage.CalculateSize());
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteMessage(nestedMessage);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
- });
- }
-
- [Test]
- public void MapWithOnlyKey_PrimitiveValue()
- {
- // Hand-craft the stream to contain a single entry with just a key.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
- int key = 10;
- output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
- });
- }
-
- [Test]
- public void MapWithOnlyKey_MessageValue()
- {
- // Hand-craft the stream to contain a single entry with just a key.
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
- output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
- int key = 10;
- output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
- });
- }
-
- [Test]
- public void MapIgnoresExtraFieldsWithinEntryMessages()
- {
- // Hand-craft the stream to contain a single entry with three fields
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
-
- var key = 10; // Field 1
- var value = 20; // Field 2
- var extra = 30; // Field 3
-
- // Each field can be represented in a single byte, with a single byte tag.
- // Total message size: 6 bytes.
- output.WriteLength(6);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value);
- output.WriteTag(3, WireFormat.WireType.Varint);
- output.WriteInt32(extra);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(value, parsed.MapInt32Int32[key]);
- });
- }
-
- [Test]
- public void MapFieldOrderIsIrrelevant()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
-
- var key = 10;
- var value = 20;
-
- // Each field can be represented in a single byte, with a single byte tag.
- // Total message size: 4 bytes.
- output.WriteLength(4);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(value, parsed.MapInt32Int32[key]);
- });
- }
-
- [Test]
- public void MapNonContiguousEntries()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- // Message structure:
- // Entry for MapInt32Int32
- // Entry for MapStringString
- // Entry for MapInt32Int32
-
- // First entry
- var key1 = 10;
- var value1 = 20;
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key1);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value1);
-
- // Second entry
- var key2 = "a";
- var value2 = "b";
- output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(6); // 3 bytes per entry: tag, size, character
- output.WriteTag(1, WireFormat.WireType.LengthDelimited);
- output.WriteString(key2);
- output.WriteTag(2, WireFormat.WireType.LengthDelimited);
- output.WriteString(value2);
-
- // Third entry
- var key3 = 15;
- var value3 = 25;
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key3);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value3);
-
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- var expected = new TestMap
- {
- MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
- MapStringString = { { key2, value2 } }
- };
- Assert.AreEqual(expected, parsed);
- });
- }
-
- [Test]
- public void DuplicateKeys_LastEntryWins()
- {
- var memoryStream = new MemoryStream();
- var output = new CodedOutputStream(memoryStream);
-
- var key = 10;
- var value1 = 20;
- var value2 = 30;
-
- // First entry
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value1);
-
- // Second entry - same key, different value
- output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteLength(4);
- output.WriteTag(1, WireFormat.WireType.Varint);
- output.WriteInt32(key);
- output.WriteTag(2, WireFormat.WireType.Varint);
- output.WriteInt32(value2);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestMap.Parser,
- memoryStream.ToArray(),
- parsed =>
- {
- Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
- });
- }
-
- [Test]
- public void CloneSingleNonMessageValues()
- {
- var original = new TestAllTypes
- {
- SingleBool = true,
- SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
- SingleDouble = 23.5,
- SingleFixed32 = 23,
- SingleFixed64 = 1234567890123,
- SingleFloat = 12.25f,
- SingleInt32 = 100,
- SingleInt64 = 3210987654321,
- SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
- SingleSfixed32 = -123,
- SingleSfixed64 = -12345678901234,
- SingleSint32 = -456,
- SingleSint64 = -12345678901235,
- SingleString = "test",
- SingleUint32 = uint.MaxValue,
- SingleUint64 = ulong.MaxValue
- };
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
- // Just as a single example
- clone.SingleInt32 = 150;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneRepeatedNonMessageValues()
- {
- var original = new TestAllTypes
- {
- RepeatedBool = { true, false },
- RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
- RepeatedDouble = { -12.25, 23.5 },
- RepeatedFixed32 = { uint.MaxValue, 23 },
- RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
- RepeatedFloat = { 100f, 12.25f },
- RepeatedInt32 = { 100, 200 },
- RepeatedInt64 = { 3210987654321, long.MaxValue },
- RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
- RepeatedSfixed32 = { -123, 123 },
- RepeatedSfixed64 = { -12345678901234, 12345678901234 },
- RepeatedSint32 = { -456, 100 },
- RepeatedSint64 = { -12345678901235, 123 },
- RepeatedString = { "foo", "bar" },
- RepeatedUint32 = { uint.MaxValue, uint.MinValue },
- RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
- // Just as a single example
- clone.RepeatedDouble.Add(25.5);
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneSingleMessageField()
- {
- var original = new TestAllTypes
- {
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
- Assert.AreEqual(original, clone);
-
- clone.SingleNestedMessage.Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneRepeatedMessageField()
- {
- var original = new TestAllTypes
- {
- RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
- Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
- Assert.AreEqual(original, clone);
-
- clone.RepeatedNestedMessage[0].Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void CloneOneofField()
- {
- var original = new TestAllTypes
- {
- OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
- };
-
- var clone = original.Clone();
- Assert.AreNotSame(original, clone);
- Assert.AreEqual(original, clone);
-
- // We should have cloned the message
- original.OneofNestedMessage.Bb = 30;
- Assert.AreNotEqual(original, clone);
- }
-
- [Test]
- public void OneofProperties()
- {
- // Switch the oneof case between each of the different options, and check everything behaves
- // as expected in each case.
- var message = new TestAllTypes();
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
-
- message.OneofString = "sample";
- Assert.AreEqual("sample", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
-
- var bytes = ByteString.CopyFrom(1, 2, 3);
- message.OneofBytes = bytes;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(bytes, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
-
- message.OneofUint32 = 20;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(20, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
-
- var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
- message.OneofNestedMessage = nestedMessage;
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
-
- message.ClearOneofField();
- Assert.AreEqual("", message.OneofString);
- Assert.AreEqual(0, message.OneofUint32);
- Assert.AreEqual(ByteString.Empty, message.OneofBytes);
- Assert.IsNull(message.OneofNestedMessage);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
- }
-
- [Test]
- public void Oneof_DefaultValuesNotEqual()
- {
- var message1 = new TestAllTypes { OneofString = "" };
- var message2 = new TestAllTypes { OneofUint32 = 0 };
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
- Assert.AreNotEqual(message1, message2);
- }
-
- [Test]
- public void OneofSerialization_NonDefaultValue()
- {
- var message = new TestAllTypes();
- message.OneofString = "this would take a bit of space";
- message.OneofUint32 = 10;
- var bytes = message.ToByteArray();
- Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
- {
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
- });
- }
-
- [Test]
- public void OneofSerialization_DefaultValue()
- {
- var message = new TestAllTypes();
- message.OneofString = "this would take a bit of space";
- message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
- var bytes = message.ToByteArray();
- Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
-
- MessageParsingHelpers.AssertWritingMessage(message);
-
- MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
- {
- Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
- });
- }
-
- [Test]
- public void DiscardUnknownFields_RealDataStillRead()
- {
- var message = SampleMessages.CreateFullTestAllTypes();
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
- var unusedFieldNumber = 23456;
- Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
- output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
- output.WriteString("ignore me");
- message.WriteTo(output);
- output.Flush();
-
- MessageParsingHelpers.AssertReadingMessage(
- TestAllTypes.Parser,
- stream.ToArray(),
- parsed =>
- {
- // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
- // Assert.AreEqual(message, parsed);
- });
- }
-
- [Test]
- public void DiscardUnknownFields_AllTypes()
- {
- // Simple way of ensuring we can skip all kinds of fields.
- var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
- var empty = Empty.Parser.ParseFrom(data);
-
- MessageParsingHelpers.AssertReadingMessage(
- Empty.Parser,
- data,
- parsed =>
- {
- // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
- // Assert.AreNotEqual(new Empty(), empty);
- });
- }
-
- // This was originally seen as a conformance test failure.
- [Test]
- public void TruncatedMessageFieldThrows()
- {
- // 130, 3 is the message tag
- // 1 is the data length - but there's no data.
- var data = new byte[] { 130, 3, 1 };
- MessageParsingHelpers.AssertReadingMessageThrows(TestAllTypes.Parser, data);
- }
-
- ///
- /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
- /// for details; we may want to change this.
- ///
- [Test]
- public void ExtraEndGroupThrows()
- {
- var message = SampleMessages.CreateFullTestAllTypes();
- var stream = new MemoryStream();
- var output = new CodedOutputStream(stream);
-
- output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
- output.WriteFixed32(123);
- output.WriteTag(100, WireFormat.WireType.EndGroup);
-
- output.Flush();
-
- stream.Position = 0;
- MessageParsingHelpers.AssertReadingMessageThrows(TestAllTypes.Parser, stream.ToArray());
- }
-
- [Test]
- public void CustomDiagnosticMessage_DirectToStringCall()
- {
- var message = new ForeignMessage { C = 31 };
- Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
- Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void CustomDiagnosticMessage_Nested()
- {
- var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
- Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
- Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void CustomDiagnosticMessage_DirectToTextWriterCall()
- {
- var message = new ForeignMessage { C = 31 };
- var writer = new StringWriter();
- JsonFormatter.Default.Format(message, writer);
- Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
- }
-
- [Test]
- public void NaNComparisons()
- {
- var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
- var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped };
- var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
-
- EqualityTester.AssertInequality(message1, message2);
- EqualityTester.AssertEquality(message1, message3);
- }
- }
+#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;
+using System.IO;
+using Google.Protobuf.TestProtos;
+using Proto2 = Google.Protobuf.TestProtos.Proto2;
+using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Google.Protobuf.WellKnownTypes;
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests around the generated TestAllTypes message.
+ ///
+ public partial class GeneratedMessageTest
+ {
+ [Test]
+ public void EmptyMessageFieldDistinctFromMissingMessageField()
+ {
+ // This demonstrates what we're really interested in...
+ var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
+ var message2 = new TestAllTypes(); // SingleForeignMessage is null
+ EqualityTester.AssertInequality(message1, message2);
+ }
+
+ [Test]
+ public void DefaultValues()
+ {
+ // Single fields
+ var message = new TestAllTypes();
+ Assert.AreEqual(false, message.SingleBool);
+ Assert.AreEqual(ByteString.Empty, message.SingleBytes);
+ Assert.AreEqual(0.0, message.SingleDouble);
+ Assert.AreEqual(0, message.SingleFixed32);
+ Assert.AreEqual(0L, message.SingleFixed64);
+ Assert.AreEqual(0.0f, message.SingleFloat);
+ Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
+ Assert.IsNull(message.SingleForeignMessage);
+ Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
+ Assert.IsNull(message.SingleImportMessage);
+ Assert.AreEqual(0, message.SingleInt32);
+ Assert.AreEqual(0L, message.SingleInt64);
+ Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
+ Assert.IsNull(message.SingleNestedMessage);
+ Assert.IsNull(message.SinglePublicImportMessage);
+ Assert.AreEqual(0, message.SingleSfixed32);
+ Assert.AreEqual(0L, message.SingleSfixed64);
+ Assert.AreEqual(0, message.SingleSint32);
+ Assert.AreEqual(0L, message.SingleSint64);
+ Assert.AreEqual("", message.SingleString);
+ Assert.AreEqual(0U, message.SingleUint32);
+ Assert.AreEqual(0UL, message.SingleUint64);
+
+ // Repeated fields
+ Assert.AreEqual(0, message.RepeatedBool.Count);
+ Assert.AreEqual(0, message.RepeatedBytes.Count);
+ Assert.AreEqual(0, message.RepeatedDouble.Count);
+ Assert.AreEqual(0, message.RepeatedFixed32.Count);
+ Assert.AreEqual(0, message.RepeatedFixed64.Count);
+ Assert.AreEqual(0, message.RepeatedFloat.Count);
+ Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
+ Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
+ Assert.AreEqual(0, message.RepeatedImportEnum.Count);
+ Assert.AreEqual(0, message.RepeatedImportMessage.Count);
+ Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
+ Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
+ Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed32.Count);
+ Assert.AreEqual(0, message.RepeatedSfixed64.Count);
+ Assert.AreEqual(0, message.RepeatedSint32.Count);
+ Assert.AreEqual(0, message.RepeatedSint64.Count);
+ Assert.AreEqual(0, message.RepeatedString.Count);
+ Assert.AreEqual(0, message.RepeatedUint32.Count);
+ Assert.AreEqual(0, message.RepeatedUint64.Count);
+
+ // Oneof fields
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ }
+
+ [Test]
+ public void NullStringAndBytesRejected()
+ {
+ var message = new TestAllTypes();
+ Assert.Throws(() => message.SingleString = null);
+ Assert.Throws(() => message.OneofString = null);
+ Assert.Throws(() => message.SingleBytes = null);
+ Assert.Throws(() => message.OneofBytes = null);
+ }
+
+ [Test]
+ public void RoundTrip_Empty()
+ {
+ var message = new TestAllTypes();
+ // Without setting any values, there's nothing to write.
+ byte[] bytes = message.ToByteArray();
+ Assert.AreEqual(0, bytes.Length);
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+ }
+
+ [Test]
+ public void RoundTrip_SingleValues()
+ {
+ var message = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+ SingleDouble = 23.5,
+ SingleFixed32 = 23,
+ SingleFixed64 = 1234567890123,
+ SingleFloat = 12.25f,
+ SingleForeignEnum = ForeignEnum.ForeignBar,
+ SingleForeignMessage = new ForeignMessage { C = 10 },
+ SingleImportEnum = ImportEnum.ImportBaz,
+ SingleImportMessage = new ImportMessage { D = 20 },
+ SingleInt32 = 100,
+ SingleInt64 = 3210987654321,
+ SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+ SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
+ SinglePublicImportMessage = new PublicImportMessage { E = 54 },
+ SingleSfixed32 = -123,
+ SingleSfixed64 = -12345678901234,
+ SingleSint32 = -456,
+ SingleSint64 = -12345678901235,
+ SingleString = "test",
+ SingleUint32 = uint.MaxValue,
+ SingleUint64 = ulong.MaxValue
+ };
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+ }
+
+ [Test]
+ public void RoundTrip_RepeatedValues()
+ {
+ var message = new TestAllTypes
+ {
+ RepeatedBool = { true, false },
+ RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+ RepeatedDouble = { -12.25, 23.5 },
+ RepeatedFixed32 = { uint.MaxValue, 23 },
+ RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+ RepeatedFloat = { 100f, 12.25f },
+ RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
+ RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
+ RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified },
+ RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
+ RepeatedInt32 = { 100, 200 },
+ RepeatedInt64 = { 3210987654321, long.MaxValue },
+ RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+ RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
+ RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
+ RepeatedSfixed32 = { -123, 123 },
+ RepeatedSfixed64 = { -12345678901234, 12345678901234 },
+ RepeatedSint32 = { -456, 100 },
+ RepeatedSint64 = { -12345678901235, 123 },
+ RepeatedString = { "foo", "bar" },
+ RepeatedUint32 = { uint.MaxValue, uint.MinValue },
+ RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
+ };
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
+ }
+
+ // Note that not every map within map_unittest_proto3 is used. They all go through very
+ // similar code paths. The fact that all maps are present is validation that we have codecs
+ // for every type.
+ [Test]
+ public void RoundTrip_Maps()
+ {
+ var message = new TestMap
+ {
+ MapBoolBool = {
+ { false, true },
+ { true, false }
+ },
+ MapInt32Bytes = {
+ { 5, ByteString.CopyFrom(6, 7, 8) },
+ { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
+ { 10, ByteString.Empty }
+ },
+ MapInt32ForeignMessage = {
+ { 0, new ForeignMessage { C = 10 } },
+ { 5, new ForeignMessage() },
+ },
+ MapInt32Enum = {
+ { 1, MapEnum.Bar },
+ { 2000, MapEnum.Foo }
+ }
+ };
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
+ }
+
+ [Test]
+ public void MapWithEmptyEntry()
+ {
+ var message = new TestMap
+ {
+ MapInt32Bytes = { { 0, ByteString.Empty } }
+ };
+
+ byte[] bytes = message.ToByteArray();
+ Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ bytes,
+ parsed=>
+ {
+ Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+ Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+ });
+ }
+
+ [Test]
+ public void MapWithOnlyValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a value.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+ var nestedMessage = new ForeignMessage { C = 20 };
+ // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
+ output.WriteLength(2 + nestedMessage.CalculateSize());
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteMessage(nestedMessage);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+ });
+ }
+
+ [Test]
+ public void MapWithOnlyKey_PrimitiveValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+ });
+ }
+
+ [Test]
+ public void MapWithOnlyKey_MessageValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+ });
+ }
+
+ [Test]
+ public void MapIgnoresExtraFieldsWithinEntryMessages()
+ {
+ // Hand-craft the stream to contain a single entry with three fields
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+ var key = 10; // Field 1
+ var value = 20; // Field 2
+ var extra = 30; // Field 3
+
+ // Each field can be represented in a single byte, with a single byte tag.
+ // Total message size: 6 bytes.
+ output.WriteLength(6);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value);
+ output.WriteTag(3, WireFormat.WireType.Varint);
+ output.WriteInt32(extra);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+ });
+ }
+
+ [Test]
+ public void MapFieldOrderIsIrrelevant()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+
+ var key = 10;
+ var value = 20;
+
+ // Each field can be represented in a single byte, with a single byte tag.
+ // Total message size: 4 bytes.
+ output.WriteLength(4);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+ });
+ }
+
+ [Test]
+ public void MapNonContiguousEntries()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ // Message structure:
+ // Entry for MapInt32Int32
+ // Entry for MapStringString
+ // Entry for MapInt32Int32
+
+ // First entry
+ var key1 = 10;
+ var value1 = 20;
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key1);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value1);
+
+ // Second entry
+ var key2 = "a";
+ var value2 = "b";
+ output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(6); // 3 bytes per entry: tag, size, character
+ output.WriteTag(1, WireFormat.WireType.LengthDelimited);
+ output.WriteString(key2);
+ output.WriteTag(2, WireFormat.WireType.LengthDelimited);
+ output.WriteString(value2);
+
+ // Third entry
+ var key3 = 15;
+ var value3 = 25;
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key3);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value3);
+
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ var expected = new TestMap
+ {
+ MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+ MapStringString = { { key2, value2 } }
+ };
+ Assert.AreEqual(expected, parsed);
+ });
+ }
+
+ [Test]
+ public void DuplicateKeys_LastEntryWins()
+ {
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+
+ var key = 10;
+ var value1 = 20;
+ var value2 = 30;
+
+ // First entry
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value1);
+
+ // Second entry - same key, different value
+ output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(4);
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.WriteTag(2, WireFormat.WireType.Varint);
+ output.WriteInt32(value2);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestMap.Parser,
+ memoryStream.ToArray(),
+ parsed =>
+ {
+ Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+ });
+ }
+
+ [Test]
+ public void CloneSingleNonMessageValues()
+ {
+ var original = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+ SingleDouble = 23.5,
+ SingleFixed32 = 23,
+ SingleFixed64 = 1234567890123,
+ SingleFloat = 12.25f,
+ SingleInt32 = 100,
+ SingleInt64 = 3210987654321,
+ SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+ SingleSfixed32 = -123,
+ SingleSfixed64 = -12345678901234,
+ SingleSint32 = -456,
+ SingleSint64 = -12345678901235,
+ SingleString = "test",
+ SingleUint32 = uint.MaxValue,
+ SingleUint64 = ulong.MaxValue
+ };
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+ // Just as a single example
+ clone.SingleInt32 = 150;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneRepeatedNonMessageValues()
+ {
+ var original = new TestAllTypes
+ {
+ RepeatedBool = { true, false },
+ RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
+ RepeatedDouble = { -12.25, 23.5 },
+ RepeatedFixed32 = { uint.MaxValue, 23 },
+ RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
+ RepeatedFloat = { 100f, 12.25f },
+ RepeatedInt32 = { 100, 200 },
+ RepeatedInt64 = { 3210987654321, long.MaxValue },
+ RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
+ RepeatedSfixed32 = { -123, 123 },
+ RepeatedSfixed64 = { -12345678901234, 12345678901234 },
+ RepeatedSint32 = { -456, 100 },
+ RepeatedSint64 = { -12345678901235, 123 },
+ RepeatedString = { "foo", "bar" },
+ RepeatedUint32 = { uint.MaxValue, uint.MinValue },
+ RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+ // Just as a single example
+ clone.RepeatedDouble.Add(25.5);
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneSingleMessageField()
+ {
+ var original = new TestAllTypes
+ {
+ SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
+ Assert.AreEqual(original, clone);
+
+ clone.SingleNestedMessage.Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneRepeatedMessageField()
+ {
+ var original = new TestAllTypes
+ {
+ RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
+ Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
+ Assert.AreEqual(original, clone);
+
+ clone.RepeatedNestedMessage[0].Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void CloneOneofField()
+ {
+ var original = new TestAllTypes
+ {
+ OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
+ };
+
+ var clone = original.Clone();
+ Assert.AreNotSame(original, clone);
+ Assert.AreEqual(original, clone);
+
+ // We should have cloned the message
+ original.OneofNestedMessage.Bb = 30;
+ Assert.AreNotEqual(original, clone);
+ }
+
+ [Test]
+ public void OneofProperties()
+ {
+ // Switch the oneof case between each of the different options, and check everything behaves
+ // as expected in each case.
+ var message = new TestAllTypes();
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+
+ message.OneofString = "sample";
+ Assert.AreEqual("sample", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
+
+ var bytes = ByteString.CopyFrom(1, 2, 3);
+ message.OneofBytes = bytes;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(bytes, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
+
+ message.OneofUint32 = 20;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(20, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
+
+ var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
+ message.OneofNestedMessage = nestedMessage;
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
+
+ message.ClearOneofField();
+ Assert.AreEqual("", message.OneofString);
+ Assert.AreEqual(0, message.OneofUint32);
+ Assert.AreEqual(ByteString.Empty, message.OneofBytes);
+ Assert.IsNull(message.OneofNestedMessage);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
+ }
+
+ [Test]
+ public void Oneof_DefaultValuesNotEqual()
+ {
+ var message1 = new TestAllTypes { OneofString = "" };
+ var message2 = new TestAllTypes { OneofUint32 = 0 };
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+ Assert.AreNotEqual(message1, message2);
+ }
+
+ [Test]
+ public void OneofSerialization_NonDefaultValue()
+ {
+ var message = new TestAllTypes();
+ message.OneofString = "this would take a bit of space";
+ message.OneofUint32 = 10;
+ var bytes = message.ToByteArray();
+ Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+ {
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+ });
+ }
+
+ [Test]
+ public void OneofSerialization_DefaultValue()
+ {
+ var message = new TestAllTypes();
+ message.OneofString = "this would take a bit of space";
+ message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
+ var bytes = message.ToByteArray();
+ Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
+
+ MessageParsingHelpers.AssertWritingMessage(message);
+
+ MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+ {
+ Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+ });
+ }
+
+ [Test]
+ public void DiscardUnknownFields_RealDataStillRead()
+ {
+ var message = SampleMessages.CreateFullTestAllTypes();
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+ var unusedFieldNumber = 23456;
+ Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
+ output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteString("ignore me");
+ message.WriteTo(output);
+ output.Flush();
+
+ MessageParsingHelpers.AssertReadingMessage(
+ TestAllTypes.Parser,
+ stream.ToArray(),
+ parsed =>
+ {
+ // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+ // Assert.AreEqual(message, parsed);
+ });
+ }
+
+ [Test]
+ public void DiscardUnknownFields_AllTypes()
+ {
+ // Simple way of ensuring we can skip all kinds of fields.
+ var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
+ var empty = Empty.Parser.ParseFrom(data);
+
+ MessageParsingHelpers.AssertReadingMessage(
+ Empty.Parser,
+ data,
+ parsed =>
+ {
+ // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+ // Assert.AreNotEqual(new Empty(), empty);
+ });
+ }
+
+ // This was originally seen as a conformance test failure.
+ [Test]
+ public void TruncatedMessageFieldThrows()
+ {
+ // 130, 3 is the message tag
+ // 1 is the data length - but there's no data.
+ var data = new byte[] { 130, 3, 1 };
+ MessageParsingHelpers.AssertReadingMessageThrows(TestAllTypes.Parser, data);
+ }
+
+ ///
+ /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
+ /// for details; we may want to change this.
+ ///
+ [Test]
+ public void ExtraEndGroupThrows()
+ {
+ var message = SampleMessages.CreateFullTestAllTypes();
+ var stream = new MemoryStream();
+ var output = new CodedOutputStream(stream);
+
+ output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
+ output.WriteFixed32(123);
+ output.WriteTag(100, WireFormat.WireType.EndGroup);
+
+ output.Flush();
+
+ stream.Position = 0;
+ MessageParsingHelpers.AssertReadingMessageThrows(TestAllTypes.Parser, stream.ToArray());
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_DirectToStringCall()
+ {
+ var message = new ForeignMessage { C = 31 };
+ Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
+ Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_Nested()
+ {
+ var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_DirectToTextWriterCall()
+ {
+ var message = new ForeignMessage { C = 31 };
+ var writer = new StringWriter();
+ JsonFormatter.Default.Format(message, writer);
+ Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
+ }
+
+ [Test]
+ public void NaNComparisons()
+ {
+ var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
+ var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped };
+ var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
+
+ EqualityTester.AssertInequality(message1, message2);
+ EqualityTester.AssertEquality(message1, message3);
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
index 641cb0a088..fe5ff80467 100644
--- a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+++ b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
@@ -14,14 +14,14 @@
-
-
-
+
+
+
-
+
diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs
index 2904c461df..695398918b 100644
--- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs
+++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs
@@ -1,116 +1,132 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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 Google.Protobuf.Reflection;
-using UnitTest.Issues.TestProtos;
-using NUnit.Framework;
-using System.IO;
-using static UnitTest.Issues.TestProtos.OneofMerging.Types;
-
-namespace Google.Protobuf
-{
- ///
- /// Tests for issues which aren't easily compartmentalized into other unit tests.
- ///
- public class IssuesTest
- {
- // Issue 45
- [Test]
- public void FieldCalledItem()
- {
- ItemField message = new ItemField { Item = 3 };
- FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
- Assert.NotNull(field);
- Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
- }
-
- [Test]
- public void ReservedNames()
- {
- var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
- // Underscores aren't reflected in the JSON.
- Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
- }
-
- [Test]
- public void JsonNameParseTest()
- {
- var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
- var parser = new JsonParser(settings);
-
- // It is safe to use either original field name or explicitly specified json_name
- Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
- parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
- }
-
- [Test]
- public void JsonNameFormatTest()
- {
- var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
- Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
- JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void OneofMerging()
- {
- var message1 = new OneofMerging { Nested = new Nested { X = 10 } };
- var message2 = new OneofMerging { Nested = new Nested { Y = 20 } };
- var expected = new OneofMerging { Nested = new Nested { X = 10, Y = 20 } };
-
- var merged = message1.Clone();
- merged.MergeFrom(message2);
- Assert.AreEqual(expected, merged);
- }
-
- // Check that a tag immediately followed by end of limit can still be read.
- [Test]
- public void CodedInputStream_LimitReachedRightAfterTag()
- {
- MemoryStream ms = new MemoryStream();
- var cos = new CodedOutputStream(ms);
- cos.WriteTag(11, WireFormat.WireType.Varint);
- Assert.AreEqual(1, cos.Position);
- cos.WriteString("some extra padding"); // ensure is currentLimit distinct from the end of the buffer.
- cos.Flush();
-
- var cis = new CodedInputStream(ms.ToArray());
- cis.PushLimit(1); // make sure we reach the limit right after reading the tag.
-
- // we still must read the tag correctly, even though the tag is at the very end of our limited input
- // (which is a corner case and will most likely result in an error when trying to read value of the field
- // described by this tag, but it would be a logical error not to read the tag that's actually present).
- // See https://github.com/protocolbuffers/protobuf/pull/7289
- cis.AssertNextTag(WireFormat.MakeTag(11, WireFormat.WireType.Varint));
- }
- }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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 Google.Protobuf.Reflection;
+using UnitTest.Issues.TestProtos;
+using NUnit.Framework;
+using System.IO;
+using static UnitTest.Issues.TestProtos.OneofMerging.Types;
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests for issues which aren't easily compartmentalized into other unit tests.
+ ///
+ public class IssuesTest
+ {
+ // Issue 45
+ [Test]
+ public void FieldCalledItem()
+ {
+ ItemField message = new ItemField { Item = 3 };
+ FieldDescriptor field = ItemField.Descriptor.FindFieldByName("item");
+ Assert.NotNull(field);
+ Assert.AreEqual(3, (int)field.Accessor.GetValue(message));
+ }
+
+ [Test]
+ public void ReservedNames()
+ {
+ var message = new ReservedNames { Types_ = 10, Descriptor_ = 20 };
+ // Underscores aren't reflected in the JSON.
+ Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString());
+ }
+
+ [Test]
+ public void JsonNameParseTest()
+ {
+ var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor));
+ var parser = new JsonParser(settings);
+
+ // It is safe to use either original field name or explicitly specified json_name
+ Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" },
+ parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }"));
+ }
+
+ [Test]
+ public void JsonNameFormatTest()
+ {
+ var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" };
+ Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
+ JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void OneofMerging()
+ {
+ var message1 = new OneofMerging { Nested = new Nested { X = 10 } };
+ var message2 = new OneofMerging { Nested = new Nested { Y = 20 } };
+ var expected = new OneofMerging { Nested = new Nested { X = 10, Y = 20 } };
+
+ var merged = message1.Clone();
+ merged.MergeFrom(message2);
+ Assert.AreEqual(expected, merged);
+ }
+
+ // Check that a tag immediately followed by end of limit can still be read.
+ [Test]
+ public void CodedInputStream_LimitReachedRightAfterTag()
+ {
+ MemoryStream ms = new MemoryStream();
+ var cos = new CodedOutputStream(ms);
+ cos.WriteTag(11, WireFormat.WireType.Varint);
+ Assert.AreEqual(1, cos.Position);
+ cos.WriteString("some extra padding"); // ensure is currentLimit distinct from the end of the buffer.
+ cos.Flush();
+
+ var cis = new CodedInputStream(ms.ToArray());
+ cis.PushLimit(1); // make sure we reach the limit right after reading the tag.
+
+ // we still must read the tag correctly, even though the tag is at the very end of our limited input
+ // (which is a corner case and will most likely result in an error when trying to read value of the field
+ // described by this tag, but it would be a logical error not to read the tag that's actually present).
+ // See https://github.com/protocolbuffers/protobuf/pull/7289
+ cis.AssertNextTag(WireFormat.MakeTag(11, WireFormat.WireType.Varint));
+ }
+
+ [Test]
+ public void NoneFieldInOneof()
+ {
+ var message = new OneofWithNoneField();
+ var emptyHashCode = message.GetHashCode();
+ Assert.AreEqual(OneofWithNoneField.TestOneofCase.None, message.TestCase);
+ message.None = "test";
+ Assert.AreEqual(OneofWithNoneField.TestOneofCase.None_, message.TestCase);
+ Assert.AreNotEqual(emptyHashCode, message.GetHashCode());
+
+ var bytes = message.ToByteArray();
+ var parsed = OneofWithNoneField.Parser.ParseFrom(bytes);
+ Assert.AreEqual(message, parsed);
+ Assert.AreEqual("test", parsed.None);
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
index 51fa5e01d6..3a77990cf1 100644
--- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
@@ -1,705 +1,705 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 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;
-using Google.Protobuf.TestProtos;
-using NUnit.Framework;
-using UnitTest.Issues.TestProtos;
-using Google.Protobuf.WellKnownTypes;
-using Google.Protobuf.Reflection;
-
-using static Google.Protobuf.JsonParserTest; // For WrapInQuotes
-using System.IO;
-using Google.Protobuf.Collections;
-using ProtobufUnittest;
-
-namespace Google.Protobuf
-{
- ///
- /// Tests for the JSON formatter. Note that in these tests, double quotes are replaced with apostrophes
- /// for the sake of readability (embedding \" everywhere is painful). See the AssertJson method for details.
- ///
- public class JsonFormatterTest
- {
- [Test]
- public void DefaultValues_WhenOmitted()
- {
- var formatter = JsonFormatter.Default;
-
- AssertJson("{ }", formatter.Format(new ForeignMessage()));
- AssertJson("{ }", formatter.Format(new TestAllTypes()));
- AssertJson("{ }", formatter.Format(new TestMap()));
- }
-
- [Test]
- public void DefaultValues_WhenIncluded()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));
- }
-
- [Test]
- public void EnumAllowAlias()
- {
- var message = new TestEnumAllowAlias
- {
- Value = TestEnumWithDupValue.Foo2,
- };
- var actualText = JsonFormatter.Default.Format(message);
- var expectedText = "{ 'value': 'FOO1' }";
- AssertJson(expectedText, actualText);
- }
-
- [Test]
- public void EnumAsInt()
- {
- var message = new TestAllTypes
- {
- SingleForeignEnum = ForeignEnum.ForeignBar,
- RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo }
- };
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true));
- var actualText = formatter.Format(message);
- var expectedText = "{ " +
- "'singleForeignEnum': 5, " +
- "'repeatedForeignEnum': [ 6, 100, 4 ]" +
- " }";
- AssertJson(expectedText, actualText);
- }
-
- [Test]
- public void AllSingleFields()
- {
- var message = new TestAllTypes
- {
- SingleBool = true,
- SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
- SingleDouble = 23.5,
- SingleFixed32 = 23,
- SingleFixed64 = 1234567890123,
- SingleFloat = 12.25f,
- SingleForeignEnum = ForeignEnum.ForeignBar,
- SingleForeignMessage = new ForeignMessage { C = 10 },
- SingleImportEnum = ImportEnum.ImportBaz,
- SingleImportMessage = new ImportMessage { D = 20 },
- SingleInt32 = 100,
- SingleInt64 = 3210987654321,
- SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
- SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
- SinglePublicImportMessage = new PublicImportMessage { E = 54 },
- SingleSfixed32 = -123,
- SingleSfixed64 = -12345678901234,
- SingleSint32 = -456,
- SingleSint64 = -12345678901235,
- SingleString = "test\twith\ttabs",
- SingleUint32 = uint.MaxValue,
- SingleUint64 = ulong.MaxValue,
- };
- var actualText = JsonFormatter.Default.Format(message);
-
- // Fields in numeric order
- var expectedText = "{ " +
- "'singleInt32': 100, " +
- "'singleInt64': '3210987654321', " +
- "'singleUint32': 4294967295, " +
- "'singleUint64': '18446744073709551615', " +
- "'singleSint32': -456, " +
- "'singleSint64': '-12345678901235', " +
- "'singleFixed32': 23, " +
- "'singleFixed64': '1234567890123', " +
- "'singleSfixed32': -123, " +
- "'singleSfixed64': '-12345678901234', " +
- "'singleFloat': 12.25, " +
- "'singleDouble': 23.5, " +
- "'singleBool': true, " +
- "'singleString': 'test\\twith\\ttabs', " +
- "'singleBytes': 'AQIDBA==', " +
- "'singleNestedMessage': { 'bb': 35 }, " +
- "'singleForeignMessage': { 'c': 10 }, " +
- "'singleImportMessage': { 'd': 20 }, " +
- "'singleNestedEnum': 'FOO', " +
- "'singleForeignEnum': 'FOREIGN_BAR', " +
- "'singleImportEnum': 'IMPORT_BAZ', " +
- "'singlePublicImportMessage': { 'e': 54 }" +
- " }";
- AssertJson(expectedText, actualText);
- }
-
- [Test]
- public void WithFormatDefaultValues_DoesNotAffectMessageFields()
- {
- var message = new TestAllTypes();
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var json = formatter.Format(message);
- Assert.IsFalse(json.Contains("\"singleNestedMessage\""));
- Assert.IsFalse(json.Contains("\"singleForeignMessage\""));
- Assert.IsFalse(json.Contains("\"singleImportMessage\""));
- }
-
- [Test]
- public void WithFormatDefaultValues_DoesNotAffectProto3OptionalFields()
- {
- var message = new TestProto3Optional();
- message.OptionalInt32 = 0;
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var json = formatter.Format(message);
- // The non-optional proto3 fields are formatted, as is the optional-but-specified field.
- AssertJson("{ 'optionalInt32': 0, 'singularInt32': 0, 'singularInt64': '0' }", json);
- }
-
- [Test]
- public void WithFormatDefaultValues_DoesNotAffectProto2Fields()
- {
- var message = new TestProtos.Proto2.ForeignMessage();
- message.C = 0;
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var json = formatter.Format(message);
- // The specified field is formatted, but the non-specified field (d) is not.
- AssertJson("{ 'c': 0 }", json);
- }
-
- [Test]
- public void WithFormatDefaultValues_DoesNotAffectOneofFields()
- {
- var message = new TestOneof();
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var json = formatter.Format(message);
- AssertJson("{ }", json);
- }
-
- [Test]
- public void RepeatedField()
- {
- AssertJson("{ 'repeatedInt32': [ 1, 2, 3, 4, 5 ] }",
- JsonFormatter.Default.Format(new TestAllTypes { RepeatedInt32 = { 1, 2, 3, 4, 5 } }));
- }
-
- [Test]
- public void MapField_StringString()
- {
- AssertJson("{ 'mapStringString': { 'with spaces': 'bar', 'a': 'b' } }",
- JsonFormatter.Default.Format(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } }));
- }
-
- [Test]
- public void MapField_Int32Int32()
- {
- // The keys are quoted, but the values aren't.
- AssertJson("{ 'mapInt32Int32': { '0': 1, '2': 3 } }",
- JsonFormatter.Default.Format(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } }));
- }
-
- [Test]
- public void MapField_BoolBool()
- {
- // The keys are quoted, but the values aren't.
- AssertJson("{ 'mapBoolBool': { 'false': true, 'true': false } }",
- JsonFormatter.Default.Format(new TestMap { MapBoolBool = { { false, true }, { true, false } } }));
- }
-
- [Test]
- public void NullValueOutsideStruct()
- {
- var message = new NullValueOutsideStruct { NullValue = NullValue.NullValue };
- AssertJson("{ 'nullValue': null }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void NullValueNotInOneof()
- {
- var message = new NullValueNotInOneof();
- AssertJson("{ }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void NullValueNotInOneof_FormatDefaults()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var message = new NullValueNotInOneof();
- AssertJson("{ 'nullValue': null }", formatter.Format(message));
- }
-
- [TestCase(1.0, "1")]
- [TestCase(double.NaN, "'NaN'")]
- [TestCase(double.PositiveInfinity, "'Infinity'")]
- [TestCase(double.NegativeInfinity, "'-Infinity'")]
- public void DoubleRepresentations(double value, string expectedValueText)
- {
- var message = new TestAllTypes { SingleDouble = value };
- string actualText = JsonFormatter.Default.Format(message);
- string expectedText = "{ 'singleDouble': " + expectedValueText + " }";
- AssertJson(expectedText, actualText);
- }
-
- [Test]
- public void UnknownEnumValueNumeric_SingleField()
- {
- var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };
- AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void UnknownEnumValueNumeric_RepeatedField()
- {
- var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo } };
- AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void UnknownEnumValueNumeric_MapField()
- {
- var message = new TestMap { MapInt32Enum = { { 1, MapEnum.Foo }, { 2, (MapEnum) 100 }, { 3, MapEnum.Bar } } };
- AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()
- {
- var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };
- AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- [TestCase("a\u17b4b", "a\\u17b4b")] // Explicit
- [TestCase("a\u0601b", "a\\u0601b")] // Ranged
- [TestCase("a\u0605b", "a\u0605b")] // Passthrough (note lack of double backslash...)
- public void SimpleNonAscii(string text, string encoded)
- {
- var message = new TestAllTypes { SingleString = text };
- AssertJson("{ 'singleString': '" + encoded + "' }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void SurrogatePairEscaping()
- {
- var message = new TestAllTypes { SingleString = "a\uD801\uDC01b" };
- AssertJson("{ 'singleString': 'a\\ud801\\udc01b' }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void InvalidSurrogatePairsFail()
- {
- // Note: don't use TestCase for these, as the strings can't be reliably represented
- // See http://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/
-
- // Lone low surrogate
- var message = new TestAllTypes { SingleString = "a\uDC01b" };
- Assert.Throws(() => JsonFormatter.Default.Format(message));
-
- // Lone high surrogate
- message = new TestAllTypes { SingleString = "a\uD801b" };
- Assert.Throws(() => JsonFormatter.Default.Format(message));
- }
-
- [Test]
- [TestCase("foo_bar", "fooBar")]
- [TestCase("bananaBanana", "bananaBanana")]
- [TestCase("BANANABanana", "BANANABanana")]
- [TestCase("simple", "simple")]
- [TestCase("ACTION_AND_ADVENTURE", "ACTIONANDADVENTURE")]
- [TestCase("action_and_adventure", "actionAndAdventure")]
- [TestCase("kFoo", "kFoo")]
- [TestCase("HTTPServer", "HTTPServer")]
- [TestCase("CLIENT", "CLIENT")]
- public void ToJsonName(string original, string expected)
- {
- Assert.AreEqual(expected, JsonFormatter.ToJsonName(original));
- }
-
- [Test]
- [TestCase(null, "{ }")]
- [TestCase("x", "{ 'fooString': 'x' }")]
- [TestCase("", "{ 'fooString': '' }")]
- public void Oneof(string fooStringValue, string expectedJson)
- {
- var message = new TestOneof();
- if (fooStringValue != null)
- {
- message.FooString = fooStringValue;
- }
-
- // We should get the same result both with and without "format default values".
- var formatter = JsonFormatter.Default;
- AssertJson(expectedJson, formatter.Format(message));
- formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- AssertJson(expectedJson, formatter.Format(message));
- }
-
- [Test]
- public void WrapperFormatting_Single()
- {
- // Just a few examples, handling both classes and value types, and
- // default vs non-default values
- var message = new TestWellKnownTypes
- {
- Int64Field = 10,
- Int32Field = 0,
- BytesField = ByteString.FromBase64("ABCD"),
- StringField = ""
- };
- var expectedJson = "{ 'int64Field': '10', 'int32Field': 0, 'stringField': '', 'bytesField': 'ABCD' }";
- AssertJson(expectedJson, JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void WrapperFormatting_Message()
- {
- Assert.AreEqual("\"\"", JsonFormatter.Default.Format(new StringValue()));
- Assert.AreEqual("0", JsonFormatter.Default.Format(new Int32Value()));
- }
-
- [Test]
- public void WrapperFormatting_FormatDefaultValuesDoesNotFormatNull()
- {
- // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
- var message = new TestWellKnownTypes { Int32Field = 10 };
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var actualJson = formatter.Format(message);
- // This *used* to include "int64Field": null, but that was a bug.
- // WithDefaultValues should not affect message fields, including wrapper types.
- Assert.IsFalse(actualJson.Contains("\"int64Field\": null"));
- Assert.IsTrue(actualJson.Contains("\"int32Field\": 10"));
- }
-
- [Test]
- public void OutputIsInNumericFieldOrder_NoDefaults()
- {
- var formatter = JsonFormatter.Default;
- var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };
- AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));
- message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
- AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
- message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
- AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
- }
-
- [Test]
- public void OutputIsInNumericFieldOrder_WithDefaults()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
- var message = new TestJsonFieldOrdering();
- AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));
- message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
- AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
- message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
- AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
- }
-
- [Test]
- [TestCase("1970-01-01T00:00:00Z", 0)]
- [TestCase("1970-01-01T00:00:00.000000001Z", 1)]
- [TestCase("1970-01-01T00:00:00.000000010Z", 10)]
- [TestCase("1970-01-01T00:00:00.000000100Z", 100)]
- [TestCase("1970-01-01T00:00:00.000001Z", 1000)]
- [TestCase("1970-01-01T00:00:00.000010Z", 10000)]
- [TestCase("1970-01-01T00:00:00.000100Z", 100000)]
- [TestCase("1970-01-01T00:00:00.001Z", 1000000)]
- [TestCase("1970-01-01T00:00:00.010Z", 10000000)]
- [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
- [TestCase("1970-01-01T00:00:00.120Z", 120000000)]
- [TestCase("1970-01-01T00:00:00.123Z", 123000000)]
- [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]
- [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]
- [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]
- [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]
- [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]
- [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]
- public void TimestampStandalone(string expected, int nanos)
- {
- Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());
- }
-
- [Test]
- public void TimestampStandalone_FromDateTime()
- {
- // One before and one after the Unix epoch, more easily represented via DateTime.
- Assert.AreEqual("\"1673-06-19T12:34:56Z\"",
- new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());
- Assert.AreEqual("\"2015-07-31T10:29:34Z\"",
- new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());
- }
-
- [Test]
- [TestCase(-1, -1)] // Would be valid as duration
- [TestCase(1, Timestamp.MaxNanos + 1)]
- [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
- [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]
- public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)
- {
- var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
- Assert.Throws(() => JsonFormatter.Default.Format(timestamp));
- }
-
- [Test]
- public void TimestampField()
- {
- var message = new TestWellKnownTypes { TimestampField = new Timestamp() };
- AssertJson("{ 'timestampField': '1970-01-01T00:00:00Z' }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- [TestCase(0, 0, "0s")]
- [TestCase(1, 0, "1s")]
- [TestCase(-1, 0, "-1s")]
- [TestCase(0, 1, "0.000000001s")]
- [TestCase(0, 10, "0.000000010s")]
- [TestCase(0, 100, "0.000000100s")]
- [TestCase(0, 1000, "0.000001s")]
- [TestCase(0, 10000, "0.000010s")]
- [TestCase(0, 100000, "0.000100s")]
- [TestCase(0, 1000000, "0.001s")]
- [TestCase(0, 10000000, "0.010s")]
- [TestCase(0, 100000000, "0.100s")]
- [TestCase(0, 120000000, "0.120s")]
- [TestCase(0, 123000000, "0.123s")]
- [TestCase(0, 123400000, "0.123400s")]
- [TestCase(0, 123450000, "0.123450s")]
- [TestCase(0, 123456000, "0.123456s")]
- [TestCase(0, 123456700, "0.123456700s")]
- [TestCase(0, 123456780, "0.123456780s")]
- [TestCase(0, 123456789, "0.123456789s")]
- [TestCase(0, -100000000, "-0.100s")]
- [TestCase(1, 100000000, "1.100s")]
- [TestCase(-1, -100000000, "-1.100s")]
- public void DurationStandalone(long seconds, int nanoseconds, string expected)
- {
- var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });
- Assert.AreEqual(WrapInQuotes(expected), json);
- }
-
- [Test]
- [TestCase(1, 2123456789)]
- [TestCase(1, -100000000)]
- public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)
- {
- var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
- Assert.Throws(() => JsonFormatter.Default.Format(duration));
- }
-
- [Test]
- public void DurationField()
- {
- var message = new TestWellKnownTypes { DurationField = new Duration() };
- AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void StructSample()
- {
- var message = new Struct
- {
- Fields =
- {
- { "a", Value.ForNull() },
- { "b", Value.ForBool(false) },
- { "c", Value.ForNumber(10.5) },
- { "d", Value.ForString("text") },
- { "e", Value.ForList(Value.ForString("t1"), Value.ForNumber(5)) },
- { "f", Value.ForStruct(new Struct { Fields = { { "nested", Value.ForString("value") } } }) }
- }
- };
- AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());
- }
-
- [Test]
- [TestCase("foo__bar")]
- [TestCase("foo_3_ar")]
- [TestCase("fooBar")]
- public void FieldMaskInvalid(string input)
- {
- var mask = new FieldMask { Paths = { input } };
- Assert.Throws(() => JsonFormatter.Default.Format(mask));
- }
-
- [Test]
- public void FieldMaskStandalone()
- {
- var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };
- Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());
-
- // Invalid, but we shouldn't create broken JSON...
- fieldMask = new FieldMask { Paths = { "x\\y" } };
- Assert.AreEqual(@"""x\\y""", fieldMask.ToString());
- }
-
- [Test]
- public void FieldMaskField()
- {
- var message = new TestWellKnownTypes { FieldMaskField = new FieldMask { Paths = { "user.display_name", "photo" } } };
- AssertJson("{ 'fieldMaskField': 'user.displayName,photo' }", JsonFormatter.Default.Format(message));
- }
-
- // SourceContext is an example of a well-known type with no special JSON handling
- [Test]
- public void SourceContextStandalone()
- {
- var message = new SourceContext { FileName = "foo.proto" };
- AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void AnyWellKnownType()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(Timestamp.Descriptor)));
- var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
- var any = Any.Pack(timestamp);
- AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
- }
-
- [Test]
- public void AnyMessageType()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
- var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
- var any = Any.Pack(message);
- AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
- }
-
- [Test]
- public void AnyMessageType_CustomPrefix()
- {
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
- var message = new TestAllTypes { SingleInt32 = 10 };
- var any = Any.Pack(message, "foo.bar/baz");
- AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest3.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));
- }
-
- [Test]
- public void AnyNested()
- {
- var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
- var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(registry));
-
- // Nest an Any as the value of an Any.
- var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
- var nestedMessage = Any.Pack(doubleNestedMessage);
- var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
- AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 20 } } }",
- formatter.Format(message));
- }
-
- [Test]
- public void AnyUnknownType()
- {
- // The default type registry doesn't have any types in it.
- var message = new TestAllTypes();
- var any = Any.Pack(message);
- Assert.Throws(() => JsonFormatter.Default.Format(any));
- }
-
- [Test]
- [TestCase(typeof(BoolValue), true, "true")]
- [TestCase(typeof(Int32Value), 32, "32")]
- [TestCase(typeof(Int64Value), 32L, "\"32\"")]
- [TestCase(typeof(UInt32Value), 32U, "32")]
- [TestCase(typeof(UInt64Value), 32UL, "\"32\"")]
- [TestCase(typeof(StringValue), "foo", "\"foo\"")]
- [TestCase(typeof(FloatValue), 1.5f, "1.5")]
- [TestCase(typeof(DoubleValue), 1.5d, "1.5")]
- public void Wrappers_Standalone(System.Type wrapperType, object value, string expectedJson)
- {
- IMessage populated = (IMessage)Activator.CreateInstance(wrapperType);
- populated.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(populated, value);
- Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(populated));
- }
-
- // Sanity tests for WriteValue. Not particularly comprehensive, as it's all covered above already,
- // as FormatMessage uses WriteValue.
-
- [TestCase(null, "null")]
- [TestCase(1, "1")]
- [TestCase(1L, "'1'")]
- [TestCase(0.5f, "0.5")]
- [TestCase(0.5d, "0.5")]
- [TestCase("text", "'text'")]
- [TestCase("x\ny", @"'x\ny'")]
- [TestCase(ForeignEnum.ForeignBar, "'FOREIGN_BAR'")]
- public void WriteValue_Constant(object value, string expectedJson)
- {
- AssertWriteValue(value, expectedJson);
- }
-
- [Test]
- public void WriteValue_Timestamp()
- {
- var value = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
- AssertWriteValue(value, "'1673-06-19T12:34:56Z'");
- }
-
- [Test]
- public void WriteValue_Message()
- {
- var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
- AssertWriteValue(value, "{ 'singleInt32': 100, 'singleInt64': '3210987654321' }");
- }
-
- [Test]
- public void WriteValue_Message_PreserveNames()
- {
- var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
- AssertWriteValue(value, "{ 'single_int32': 100, 'single_int64': '3210987654321' }", JsonFormatter.Settings.Default.WithPreserveProtoFieldNames(true));
- }
-
- [Test]
- public void WriteValue_List()
- {
- var value = new RepeatedField { 1, 2, 3 };
- AssertWriteValue(value, "[ 1, 2, 3 ]");
- }
-
- [Test]
- public void Proto2_DefaultValuesWritten()
- {
- var value = new ProtobufTestMessages.Proto2.TestAllTypesProto2() { FieldName13 = 0 };
- AssertWriteValue(value, "{ 'FieldName13': 0 }");
- }
-
- private static void AssertWriteValue(object value, string expectedJson, JsonFormatter.Settings settings = null)
- {
- var writer = new StringWriter();
- new JsonFormatter(settings ?? JsonFormatter.Settings.Default).WriteValue(writer, value);
- string actual = writer.ToString();
- AssertJson(expectedJson, actual);
- }
-
- ///
- /// Checks that the actual JSON is the same as the expected JSON - but after replacing
- /// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier
- /// to read.
- ///
- private static void AssertJson(string expectedJsonWithApostrophes, string actualJson)
- {
- var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"");
- Assert.AreEqual(expectedJson, actualJson);
- }
- }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 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;
+using Google.Protobuf.TestProtos;
+using NUnit.Framework;
+using UnitTest.Issues.TestProtos;
+using Google.Protobuf.WellKnownTypes;
+using Google.Protobuf.Reflection;
+
+using static Google.Protobuf.JsonParserTest; // For WrapInQuotes
+using System.IO;
+using Google.Protobuf.Collections;
+using ProtobufUnittest;
+
+namespace Google.Protobuf
+{
+ ///
+ /// Tests for the JSON formatter. Note that in these tests, double quotes are replaced with apostrophes
+ /// for the sake of readability (embedding \" everywhere is painful). See the AssertJson method for details.
+ ///
+ public class JsonFormatterTest
+ {
+ [Test]
+ public void DefaultValues_WhenOmitted()
+ {
+ var formatter = JsonFormatter.Default;
+
+ AssertJson("{ }", formatter.Format(new ForeignMessage()));
+ AssertJson("{ }", formatter.Format(new TestAllTypes()));
+ AssertJson("{ }", formatter.Format(new TestMap()));
+ }
+
+ [Test]
+ public void DefaultValues_WhenIncluded()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));
+ }
+
+ [Test]
+ public void EnumAllowAlias()
+ {
+ var message = new TestEnumAllowAlias
+ {
+ Value = TestEnumWithDupValue.Foo2,
+ };
+ var actualText = JsonFormatter.Default.Format(message);
+ var expectedText = "{ 'value': 'FOO1' }";
+ AssertJson(expectedText, actualText);
+ }
+
+ [Test]
+ public void EnumAsInt()
+ {
+ var message = new TestAllTypes
+ {
+ SingleForeignEnum = ForeignEnum.ForeignBar,
+ RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo }
+ };
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true));
+ var actualText = formatter.Format(message);
+ var expectedText = "{ " +
+ "'singleForeignEnum': 5, " +
+ "'repeatedForeignEnum': [ 6, 100, 4 ]" +
+ " }";
+ AssertJson(expectedText, actualText);
+ }
+
+ [Test]
+ public void AllSingleFields()
+ {
+ var message = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
+ SingleDouble = 23.5,
+ SingleFixed32 = 23,
+ SingleFixed64 = 1234567890123,
+ SingleFloat = 12.25f,
+ SingleForeignEnum = ForeignEnum.ForeignBar,
+ SingleForeignMessage = new ForeignMessage { C = 10 },
+ SingleImportEnum = ImportEnum.ImportBaz,
+ SingleImportMessage = new ImportMessage { D = 20 },
+ SingleInt32 = 100,
+ SingleInt64 = 3210987654321,
+ SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
+ SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
+ SinglePublicImportMessage = new PublicImportMessage { E = 54 },
+ SingleSfixed32 = -123,
+ SingleSfixed64 = -12345678901234,
+ SingleSint32 = -456,
+ SingleSint64 = -12345678901235,
+ SingleString = "test\twith\ttabs",
+ SingleUint32 = uint.MaxValue,
+ SingleUint64 = ulong.MaxValue,
+ };
+ var actualText = JsonFormatter.Default.Format(message);
+
+ // Fields in numeric order
+ var expectedText = "{ " +
+ "'singleInt32': 100, " +
+ "'singleInt64': '3210987654321', " +
+ "'singleUint32': 4294967295, " +
+ "'singleUint64': '18446744073709551615', " +
+ "'singleSint32': -456, " +
+ "'singleSint64': '-12345678901235', " +
+ "'singleFixed32': 23, " +
+ "'singleFixed64': '1234567890123', " +
+ "'singleSfixed32': -123, " +
+ "'singleSfixed64': '-12345678901234', " +
+ "'singleFloat': 12.25, " +
+ "'singleDouble': 23.5, " +
+ "'singleBool': true, " +
+ "'singleString': 'test\\twith\\ttabs', " +
+ "'singleBytes': 'AQIDBA==', " +
+ "'singleNestedMessage': { 'bb': 35 }, " +
+ "'singleForeignMessage': { 'c': 10 }, " +
+ "'singleImportMessage': { 'd': 20 }, " +
+ "'singleNestedEnum': 'FOO', " +
+ "'singleForeignEnum': 'FOREIGN_BAR', " +
+ "'singleImportEnum': 'IMPORT_BAZ', " +
+ "'singlePublicImportMessage': { 'e': 54 }" +
+ " }";
+ AssertJson(expectedText, actualText);
+ }
+
+ [Test]
+ public void WithFormatDefaultValues_DoesNotAffectMessageFields()
+ {
+ var message = new TestAllTypes();
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var json = formatter.Format(message);
+ Assert.IsFalse(json.Contains("\"singleNestedMessage\""));
+ Assert.IsFalse(json.Contains("\"singleForeignMessage\""));
+ Assert.IsFalse(json.Contains("\"singleImportMessage\""));
+ }
+
+ [Test]
+ public void WithFormatDefaultValues_DoesNotAffectProto3OptionalFields()
+ {
+ var message = new TestProto3Optional();
+ message.OptionalInt32 = 0;
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var json = formatter.Format(message);
+ // The non-optional proto3 fields are formatted, as is the optional-but-specified field.
+ AssertJson("{ 'optionalInt32': 0, 'singularInt32': 0, 'singularInt64': '0' }", json);
+ }
+
+ [Test]
+ public void WithFormatDefaultValues_DoesNotAffectProto2Fields()
+ {
+ var message = new TestProtos.Proto2.ForeignMessage();
+ message.C = 0;
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var json = formatter.Format(message);
+ // The specified field is formatted, but the non-specified field (d) is not.
+ AssertJson("{ 'c': 0 }", json);
+ }
+
+ [Test]
+ public void WithFormatDefaultValues_DoesNotAffectOneofFields()
+ {
+ var message = new TestOneof();
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var json = formatter.Format(message);
+ AssertJson("{ }", json);
+ }
+
+ [Test]
+ public void RepeatedField()
+ {
+ AssertJson("{ 'repeatedInt32': [ 1, 2, 3, 4, 5 ] }",
+ JsonFormatter.Default.Format(new TestAllTypes { RepeatedInt32 = { 1, 2, 3, 4, 5 } }));
+ }
+
+ [Test]
+ public void MapField_StringString()
+ {
+ AssertJson("{ 'mapStringString': { 'with spaces': 'bar', 'a': 'b' } }",
+ JsonFormatter.Default.Format(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } }));
+ }
+
+ [Test]
+ public void MapField_Int32Int32()
+ {
+ // The keys are quoted, but the values aren't.
+ AssertJson("{ 'mapInt32Int32': { '0': 1, '2': 3 } }",
+ JsonFormatter.Default.Format(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } }));
+ }
+
+ [Test]
+ public void MapField_BoolBool()
+ {
+ // The keys are quoted, but the values aren't.
+ AssertJson("{ 'mapBoolBool': { 'false': true, 'true': false } }",
+ JsonFormatter.Default.Format(new TestMap { MapBoolBool = { { false, true }, { true, false } } }));
+ }
+
+ [Test]
+ public void NullValueOutsideStruct()
+ {
+ var message = new NullValueOutsideStruct { NullValue = NullValue.NullValue };
+ AssertJson("{ 'nullValue': null }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void NullValueNotInOneof()
+ {
+ var message = new NullValueNotInOneof();
+ AssertJson("{ }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void NullValueNotInOneof_FormatDefaults()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var message = new NullValueNotInOneof();
+ AssertJson("{ 'nullValue': null }", formatter.Format(message));
+ }
+
+ [TestCase(1.0, "1")]
+ [TestCase(double.NaN, "'NaN'")]
+ [TestCase(double.PositiveInfinity, "'Infinity'")]
+ [TestCase(double.NegativeInfinity, "'-Infinity'")]
+ public void DoubleRepresentations(double value, string expectedValueText)
+ {
+ var message = new TestAllTypes { SingleDouble = value };
+ string actualText = JsonFormatter.Default.Format(message);
+ string expectedText = "{ 'singleDouble': " + expectedValueText + " }";
+ AssertJson(expectedText, actualText);
+ }
+
+ [Test]
+ public void UnknownEnumValueNumeric_SingleField()
+ {
+ var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };
+ AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void UnknownEnumValueNumeric_RepeatedField()
+ {
+ var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo } };
+ AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void UnknownEnumValueNumeric_MapField()
+ {
+ var message = new TestMap { MapInt32Enum = { { 1, MapEnum.Foo }, { 2, (MapEnum) 100 }, { 3, MapEnum.Bar } } };
+ AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()
+ {
+ var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };
+ AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ [TestCase("a\u17b4b", "a\\u17b4b")] // Explicit
+ [TestCase("a\u0601b", "a\\u0601b")] // Ranged
+ [TestCase("a\u0605b", "a\u0605b")] // Passthrough (note lack of double backslash...)
+ public void SimpleNonAscii(string text, string encoded)
+ {
+ var message = new TestAllTypes { SingleString = text };
+ AssertJson("{ 'singleString': '" + encoded + "' }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void SurrogatePairEscaping()
+ {
+ var message = new TestAllTypes { SingleString = "a\uD801\uDC01b" };
+ AssertJson("{ 'singleString': 'a\\ud801\\udc01b' }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void InvalidSurrogatePairsFail()
+ {
+ // Note: don't use TestCase for these, as the strings can't be reliably represented
+ // See http://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/
+
+ // Lone low surrogate
+ var message = new TestAllTypes { SingleString = "a\uDC01b" };
+ Assert.Throws(() => JsonFormatter.Default.Format(message));
+
+ // Lone high surrogate
+ message = new TestAllTypes { SingleString = "a\uD801b" };
+ Assert.Throws(() => JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ [TestCase("foo_bar", "fooBar")]
+ [TestCase("bananaBanana", "bananaBanana")]
+ [TestCase("BANANABanana", "BANANABanana")]
+ [TestCase("simple", "simple")]
+ [TestCase("ACTION_AND_ADVENTURE", "ACTIONANDADVENTURE")]
+ [TestCase("action_and_adventure", "actionAndAdventure")]
+ [TestCase("kFoo", "kFoo")]
+ [TestCase("HTTPServer", "HTTPServer")]
+ [TestCase("CLIENT", "CLIENT")]
+ public void ToJsonName(string original, string expected)
+ {
+ Assert.AreEqual(expected, JsonFormatter.ToJsonName(original));
+ }
+
+ [Test]
+ [TestCase(null, "{ }")]
+ [TestCase("x", "{ 'fooString': 'x' }")]
+ [TestCase("", "{ 'fooString': '' }")]
+ public void Oneof(string fooStringValue, string expectedJson)
+ {
+ var message = new TestOneof();
+ if (fooStringValue != null)
+ {
+ message.FooString = fooStringValue;
+ }
+
+ // We should get the same result both with and without "format default values".
+ var formatter = JsonFormatter.Default;
+ AssertJson(expectedJson, formatter.Format(message));
+ formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ AssertJson(expectedJson, formatter.Format(message));
+ }
+
+ [Test]
+ public void WrapperFormatting_Single()
+ {
+ // Just a few examples, handling both classes and value types, and
+ // default vs non-default values
+ var message = new TestWellKnownTypes
+ {
+ Int64Field = 10,
+ Int32Field = 0,
+ BytesField = ByteString.FromBase64("ABCD"),
+ StringField = ""
+ };
+ var expectedJson = "{ 'int64Field': '10', 'int32Field': 0, 'stringField': '', 'bytesField': 'ABCD' }";
+ AssertJson(expectedJson, JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void WrapperFormatting_Message()
+ {
+ Assert.AreEqual("\"\"", JsonFormatter.Default.Format(new StringValue()));
+ Assert.AreEqual("0", JsonFormatter.Default.Format(new Int32Value()));
+ }
+
+ [Test]
+ public void WrapperFormatting_FormatDefaultValuesDoesNotFormatNull()
+ {
+ // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
+ var message = new TestWellKnownTypes { Int32Field = 10 };
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var actualJson = formatter.Format(message);
+ // This *used* to include "int64Field": null, but that was a bug.
+ // WithDefaultValues should not affect message fields, including wrapper types.
+ Assert.IsFalse(actualJson.Contains("\"int64Field\": null"));
+ Assert.IsTrue(actualJson.Contains("\"int32Field\": 10"));
+ }
+
+ [Test]
+ public void OutputIsInNumericFieldOrder_NoDefaults()
+ {
+ var formatter = JsonFormatter.Default;
+ var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };
+ AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));
+ message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
+ AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
+ message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
+ AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
+ }
+
+ [Test]
+ public void OutputIsInNumericFieldOrder_WithDefaults()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
+ var message = new TestJsonFieldOrdering();
+ AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));
+ message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
+ AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
+ message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
+ AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
+ }
+
+ [Test]
+ [TestCase("1970-01-01T00:00:00Z", 0)]
+ [TestCase("1970-01-01T00:00:00.000000001Z", 1)]
+ [TestCase("1970-01-01T00:00:00.000000010Z", 10)]
+ [TestCase("1970-01-01T00:00:00.000000100Z", 100)]
+ [TestCase("1970-01-01T00:00:00.000001Z", 1000)]
+ [TestCase("1970-01-01T00:00:00.000010Z", 10000)]
+ [TestCase("1970-01-01T00:00:00.000100Z", 100000)]
+ [TestCase("1970-01-01T00:00:00.001Z", 1000000)]
+ [TestCase("1970-01-01T00:00:00.010Z", 10000000)]
+ [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
+ [TestCase("1970-01-01T00:00:00.120Z", 120000000)]
+ [TestCase("1970-01-01T00:00:00.123Z", 123000000)]
+ [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]
+ [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]
+ [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]
+ [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]
+ [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]
+ [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]
+ public void TimestampStandalone(string expected, int nanos)
+ {
+ Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());
+ }
+
+ [Test]
+ public void TimestampStandalone_FromDateTime()
+ {
+ // One before and one after the Unix epoch, more easily represented via DateTime.
+ Assert.AreEqual("\"1673-06-19T12:34:56Z\"",
+ new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());
+ Assert.AreEqual("\"2015-07-31T10:29:34Z\"",
+ new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());
+ }
+
+ [Test]
+ [TestCase(-1, -1)] // Would be valid as duration
+ [TestCase(1, Timestamp.MaxNanos + 1)]
+ [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
+ [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]
+ public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)
+ {
+ var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws(() => JsonFormatter.Default.Format(timestamp));
+ }
+
+ [Test]
+ public void TimestampField()
+ {
+ var message = new TestWellKnownTypes { TimestampField = new Timestamp() };
+ AssertJson("{ 'timestampField': '1970-01-01T00:00:00Z' }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ [TestCase(0, 0, "0s")]
+ [TestCase(1, 0, "1s")]
+ [TestCase(-1, 0, "-1s")]
+ [TestCase(0, 1, "0.000000001s")]
+ [TestCase(0, 10, "0.000000010s")]
+ [TestCase(0, 100, "0.000000100s")]
+ [TestCase(0, 1000, "0.000001s")]
+ [TestCase(0, 10000, "0.000010s")]
+ [TestCase(0, 100000, "0.000100s")]
+ [TestCase(0, 1000000, "0.001s")]
+ [TestCase(0, 10000000, "0.010s")]
+ [TestCase(0, 100000000, "0.100s")]
+ [TestCase(0, 120000000, "0.120s")]
+ [TestCase(0, 123000000, "0.123s")]
+ [TestCase(0, 123400000, "0.123400s")]
+ [TestCase(0, 123450000, "0.123450s")]
+ [TestCase(0, 123456000, "0.123456s")]
+ [TestCase(0, 123456700, "0.123456700s")]
+ [TestCase(0, 123456780, "0.123456780s")]
+ [TestCase(0, 123456789, "0.123456789s")]
+ [TestCase(0, -100000000, "-0.100s")]
+ [TestCase(1, 100000000, "1.100s")]
+ [TestCase(-1, -100000000, "-1.100s")]
+ public void DurationStandalone(long seconds, int nanoseconds, string expected)
+ {
+ var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });
+ Assert.AreEqual(WrapInQuotes(expected), json);
+ }
+
+ [Test]
+ [TestCase(1, 2123456789)]
+ [TestCase(1, -100000000)]
+ public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)
+ {
+ var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws(() => JsonFormatter.Default.Format(duration));
+ }
+
+ [Test]
+ public void DurationField()
+ {
+ var message = new TestWellKnownTypes { DurationField = new Duration() };
+ AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void StructSample()
+ {
+ var message = new Struct
+ {
+ Fields =
+ {
+ { "a", Value.ForNull() },
+ { "b", Value.ForBool(false) },
+ { "c", Value.ForNumber(10.5) },
+ { "d", Value.ForString("text") },
+ { "e", Value.ForList(Value.ForString("t1"), Value.ForNumber(5)) },
+ { "f", Value.ForStruct(new Struct { Fields = { { "nested", Value.ForString("value") } } }) }
+ }
+ };
+ AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());
+ }
+
+ [Test]
+ [TestCase("foo__bar")]
+ [TestCase("foo_3_ar")]
+ [TestCase("fooBar")]
+ public void FieldMaskInvalid(string input)
+ {
+ var mask = new FieldMask { Paths = { input } };
+ Assert.Throws(() => JsonFormatter.Default.Format(mask));
+ }
+
+ [Test]
+ public void FieldMaskStandalone()
+ {
+ var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };
+ Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());
+
+ // Invalid, but we shouldn't create broken JSON...
+ fieldMask = new FieldMask { Paths = { "x\\y" } };
+ Assert.AreEqual(@"""x\\y""", fieldMask.ToString());
+ }
+
+ [Test]
+ public void FieldMaskField()
+ {
+ var message = new TestWellKnownTypes { FieldMaskField = new FieldMask { Paths = { "user.display_name", "photo" } } };
+ AssertJson("{ 'fieldMaskField': 'user.displayName,photo' }", JsonFormatter.Default.Format(message));
+ }
+
+ // SourceContext is an example of a well-known type with no special JSON handling
+ [Test]
+ public void SourceContextStandalone()
+ {
+ var message = new SourceContext { FileName = "foo.proto" };
+ AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void AnyWellKnownType()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(Timestamp.Descriptor)));
+ var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+ var any = Any.Pack(timestamp);
+ AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
+ }
+
+ [Test]
+ public void AnyMessageType()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+ var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
+ var any = Any.Pack(message);
+ AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
+ }
+
+ [Test]
+ public void AnyMessageType_CustomPrefix()
+ {
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+ var message = new TestAllTypes { SingleInt32 = 10 };
+ var any = Any.Pack(message, "foo.bar/baz");
+ AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest3.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));
+ }
+
+ [Test]
+ public void AnyNested()
+ {
+ var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(registry));
+
+ // Nest an Any as the value of an Any.
+ var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
+ var nestedMessage = Any.Pack(doubleNestedMessage);
+ var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
+ AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest3.TestAllTypes', 'singleInt32': 20 } } }",
+ formatter.Format(message));
+ }
+
+ [Test]
+ public void AnyUnknownType()
+ {
+ // The default type registry doesn't have any types in it.
+ var message = new TestAllTypes();
+ var any = Any.Pack(message);
+ Assert.Throws(() => JsonFormatter.Default.Format(any));
+ }
+
+ [Test]
+ [TestCase(typeof(BoolValue), true, "true")]
+ [TestCase(typeof(Int32Value), 32, "32")]
+ [TestCase(typeof(Int64Value), 32L, "\"32\"")]
+ [TestCase(typeof(UInt32Value), 32U, "32")]
+ [TestCase(typeof(UInt64Value), 32UL, "\"32\"")]
+ [TestCase(typeof(StringValue), "foo", "\"foo\"")]
+ [TestCase(typeof(FloatValue), 1.5f, "1.5")]
+ [TestCase(typeof(DoubleValue), 1.5d, "1.5")]
+ public void Wrappers_Standalone(System.Type wrapperType, object value, string expectedJson)
+ {
+ IMessage populated = (IMessage)Activator.CreateInstance(wrapperType);
+ populated.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(populated, value);
+ Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(populated));
+ }
+
+ // Sanity tests for WriteValue. Not particularly comprehensive, as it's all covered above already,
+ // as FormatMessage uses WriteValue.
+
+ [TestCase(null, "null")]
+ [TestCase(1, "1")]
+ [TestCase(1L, "'1'")]
+ [TestCase(0.5f, "0.5")]
+ [TestCase(0.5d, "0.5")]
+ [TestCase("text", "'text'")]
+ [TestCase("x\ny", @"'x\ny'")]
+ [TestCase(ForeignEnum.ForeignBar, "'FOREIGN_BAR'")]
+ public void WriteValue_Constant(object value, string expectedJson)
+ {
+ AssertWriteValue(value, expectedJson);
+ }
+
+ [Test]
+ public void WriteValue_Timestamp()
+ {
+ var value = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+ AssertWriteValue(value, "'1673-06-19T12:34:56Z'");
+ }
+
+ [Test]
+ public void WriteValue_Message()
+ {
+ var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
+ AssertWriteValue(value, "{ 'singleInt32': 100, 'singleInt64': '3210987654321' }");
+ }
+
+ [Test]
+ public void WriteValue_Message_PreserveNames()
+ {
+ var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
+ AssertWriteValue(value, "{ 'single_int32': 100, 'single_int64': '3210987654321' }", JsonFormatter.Settings.Default.WithPreserveProtoFieldNames(true));
+ }
+
+ [Test]
+ public void WriteValue_List()
+ {
+ var value = new RepeatedField