1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2015 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using System.Collections.Generic; 11 using System.IO; 12 using System.Reflection; 13 using Google.Protobuf.TestProtos; 14 using NUnit.Framework; 15 16 namespace Google.Protobuf 17 { 18 public class FieldCodecTest 19 { 20 #pragma warning disable 0414 // Used by tests via reflection - do not remove! 21 private static readonly List<ICodecTestData> Codecs = new List<ICodecTestData> 22 { 23 new FieldCodecTestData<bool>(FieldCodec.ForBool(100), true, "FixedBool"), 24 new FieldCodecTestData<string>(FieldCodec.ForString(100), "sample", "String"), 25 new FieldCodecTestData<ByteString>(FieldCodec.ForBytes(100), ByteString.CopyFrom(1, 2, 3), "Bytes"), 26 new FieldCodecTestData<int>(FieldCodec.ForInt32(100), -1000, "Int32"), 27 new FieldCodecTestData<int>(FieldCodec.ForSInt32(100), -1000, "SInt32"), 28 new FieldCodecTestData<int>(FieldCodec.ForSFixed32(100), -1000, "SFixed32"), 29 new FieldCodecTestData<uint>(FieldCodec.ForUInt32(100), 1234, "UInt32"), 30 new FieldCodecTestData<uint>(FieldCodec.ForFixed32(100), 1234, "Fixed32"), 31 new FieldCodecTestData<long>(FieldCodec.ForInt64(100), -1000, "Int64"), 32 new FieldCodecTestData<long>(FieldCodec.ForSInt64(100), -1000, "SInt64"), 33 new FieldCodecTestData<long>(FieldCodec.ForSFixed64(100), -1000, "SFixed64"), 34 new FieldCodecTestData<ulong>(FieldCodec.ForUInt64(100), 1234, "UInt64"), 35 new FieldCodecTestData<ulong>(FieldCodec.ForFixed64(100), 1234, "Fixed64"), 36 new FieldCodecTestData<float>(FieldCodec.ForFloat(100), 1234.5f, "FixedFloat"), 37 new FieldCodecTestData<double>(FieldCodec.ForDouble(100), 1234567890.5d, "FixedDouble"), 38 new FieldCodecTestData<ForeignEnum>( 39 FieldCodec.ForEnum(100, t => (int) t, t => (ForeignEnum) t), ForeignEnum.ForeignBaz, "Enum"), 40 new FieldCodecTestData<ForeignMessage>( 41 FieldCodec.ForMessage(100, ForeignMessage.Parser), new ForeignMessage { C = 10 }, "Message"), 42 }; 43 #pragma warning restore 0414 44 45 [Test, TestCaseSource("Codecs")] RoundTripWithTag(ICodecTestData codec)46 public void RoundTripWithTag(ICodecTestData codec) 47 { 48 codec.TestRoundTripWithTag(); 49 } 50 51 [Test, TestCaseSource("Codecs")] RoundTripRaw(ICodecTestData codec)52 public void RoundTripRaw(ICodecTestData codec) 53 { 54 codec.TestRoundTripRaw(); 55 } 56 57 [Test, TestCaseSource("Codecs")] CalculateSize(ICodecTestData codec)58 public void CalculateSize(ICodecTestData codec) 59 { 60 codec.TestCalculateSizeWithTag(); 61 } 62 63 [Test, TestCaseSource("Codecs")] DefaultValue(ICodecTestData codec)64 public void DefaultValue(ICodecTestData codec) 65 { 66 codec.TestDefaultValue(); 67 } 68 69 [Test, TestCaseSource("Codecs")] FixedSize(ICodecTestData codec)70 public void FixedSize(ICodecTestData codec) 71 { 72 codec.TestFixedSize(); 73 } 74 75 // This is ugly, but it means we can have a non-generic interface. 76 // It feels like NUnit should support this better, but I don't know 77 // of any better ways right now. 78 public interface ICodecTestData 79 { TestRoundTripRaw()80 void TestRoundTripRaw(); TestRoundTripWithTag()81 void TestRoundTripWithTag(); TestCalculateSizeWithTag()82 void TestCalculateSizeWithTag(); TestDefaultValue()83 void TestDefaultValue(); TestFixedSize()84 void TestFixedSize(); 85 } 86 87 public class FieldCodecTestData<T> : ICodecTestData 88 { 89 private readonly FieldCodec<T> codec; 90 private readonly T sampleValue; 91 private readonly string name; 92 FieldCodecTestData(FieldCodec<T> codec, T sampleValue, string name)93 public FieldCodecTestData(FieldCodec<T> codec, T sampleValue, string name) 94 { 95 this.codec = codec; 96 this.sampleValue = sampleValue; 97 this.name = name; 98 } 99 TestRoundTripRaw()100 public void TestRoundTripRaw() 101 { 102 var stream = new MemoryStream(); 103 var codedOutput = new CodedOutputStream(stream); 104 WriteContext.Initialize(codedOutput, out WriteContext ctx); 105 try 106 { 107 // only write the value using the codec 108 codec.ValueWriter(ref ctx, sampleValue); 109 } 110 finally 111 { 112 ctx.CopyStateTo(codedOutput); 113 } 114 115 codedOutput.Flush(); 116 stream.Position = 0; 117 var codedInput = new CodedInputStream(stream); 118 Assert.AreEqual(sampleValue, codec.Read(codedInput)); 119 Assert.IsTrue(codedInput.IsAtEnd); 120 } 121 TestRoundTripWithTag()122 public void TestRoundTripWithTag() 123 { 124 var stream = new MemoryStream(); 125 var codedOutput = new CodedOutputStream(stream); 126 codec.WriteTagAndValue(codedOutput, sampleValue); 127 codedOutput.Flush(); 128 stream.Position = 0; 129 var codedInput = new CodedInputStream(stream); 130 codedInput.AssertNextTag(codec.Tag); 131 Assert.AreEqual(sampleValue, codec.Read(codedInput)); 132 Assert.IsTrue(codedInput.IsAtEnd); 133 } 134 TestCalculateSizeWithTag()135 public void TestCalculateSizeWithTag() 136 { 137 var stream = new MemoryStream(); 138 var codedOutput = new CodedOutputStream(stream); 139 codec.WriteTagAndValue(codedOutput, sampleValue); 140 codedOutput.Flush(); 141 Assert.AreEqual(stream.Position, codec.CalculateSizeWithTag(sampleValue)); 142 } 143 TestDefaultValue()144 public void TestDefaultValue() 145 { 146 // WriteTagAndValue ignores default values 147 var stream = new MemoryStream(); 148 CodedOutputStream codedOutput; 149 codedOutput = new CodedOutputStream(stream); 150 codec.WriteTagAndValue(codedOutput, codec.DefaultValue); 151 codedOutput.Flush(); 152 Assert.AreEqual(0, stream.Position); 153 Assert.AreEqual(0, codec.CalculateSizeWithTag(codec.DefaultValue)); 154 if (typeof(T).GetTypeInfo().IsValueType) 155 { 156 Assert.AreEqual(default(T), codec.DefaultValue); 157 } 158 159 // The plain ValueWriter/ValueReader delegates don't. 160 if (codec.DefaultValue != null) // This part isn't appropriate for message types. 161 { 162 codedOutput = new CodedOutputStream(stream); 163 WriteContext.Initialize(codedOutput, out WriteContext ctx); 164 try 165 { 166 // only write the value using the codec 167 codec.ValueWriter(ref ctx, codec.DefaultValue); 168 } 169 finally 170 { 171 ctx.CopyStateTo(codedOutput); 172 } 173 174 codedOutput.Flush(); 175 Assert.AreNotEqual(0, stream.Position); 176 Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue)); 177 stream.Position = 0; 178 var codedInput = new CodedInputStream(stream); 179 Assert.AreEqual(codec.DefaultValue, codec.Read(codedInput)); 180 } 181 } 182 TestFixedSize()183 public void TestFixedSize() 184 { 185 Assert.AreEqual(name.Contains("Fixed"), codec.FixedSize != 0); 186 } 187 ToString()188 public override string ToString() 189 { 190 return name; 191 } 192 } 193 } 194 } 195