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 codedOutput.Flush(); 115 stream.Position = 0; 116 var codedInput = new CodedInputStream(stream); 117 Assert.AreEqual(sampleValue, codec.Read(codedInput)); 118 Assert.IsTrue(codedInput.IsAtEnd); 119 } 120 TestRoundTripWithTag()121 public void TestRoundTripWithTag() 122 { 123 var stream = new MemoryStream(); 124 var codedOutput = new CodedOutputStream(stream); 125 codec.WriteTagAndValue(codedOutput, sampleValue); 126 codedOutput.Flush(); 127 stream.Position = 0; 128 var codedInput = new CodedInputStream(stream); 129 codedInput.AssertNextTag(codec.Tag); 130 Assert.AreEqual(sampleValue, codec.Read(codedInput)); 131 Assert.IsTrue(codedInput.IsAtEnd); 132 } 133 TestCalculateSizeWithTag()134 public void TestCalculateSizeWithTag() 135 { 136 var stream = new MemoryStream(); 137 var codedOutput = new CodedOutputStream(stream); 138 codec.WriteTagAndValue(codedOutput, sampleValue); 139 codedOutput.Flush(); 140 Assert.AreEqual(stream.Position, codec.CalculateSizeWithTag(sampleValue)); 141 } 142 TestDefaultValue()143 public void TestDefaultValue() 144 { 145 // WriteTagAndValue ignores default values 146 var stream = new MemoryStream(); 147 var codedOutput = new CodedOutputStream(stream); 148 codec.WriteTagAndValue(codedOutput, codec.DefaultValue); 149 codedOutput.Flush(); 150 Assert.AreEqual(0, stream.Position); 151 Assert.AreEqual(0, codec.CalculateSizeWithTag(codec.DefaultValue)); 152 if (typeof(T).GetTypeInfo().IsValueType) 153 { 154 Assert.AreEqual(default(T), codec.DefaultValue); 155 } 156 157 // The plain ValueWriter/ValueReader delegates don't. 158 if (codec.DefaultValue != null) // This part isn't appropriate for message types. 159 { 160 codedOutput = new CodedOutputStream(stream); 161 WriteContext.Initialize(codedOutput, out WriteContext ctx); 162 try 163 { 164 // only write the value using the codec 165 codec.ValueWriter(ref ctx, codec.DefaultValue); 166 } 167 finally 168 { 169 ctx.CopyStateTo(codedOutput); 170 } 171 codedOutput.Flush(); 172 Assert.AreNotEqual(0, stream.Position); 173 Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue)); 174 stream.Position = 0; 175 var codedInput = new CodedInputStream(stream); 176 Assert.AreEqual(codec.DefaultValue, codec.Read(codedInput)); 177 } 178 } 179 TestFixedSize()180 public void TestFixedSize() 181 { 182 Assert.AreEqual(name.Contains("Fixed"), codec.FixedSize != 0); 183 } 184 ToString()185 public override string ToString() 186 { 187 return name; 188 } 189 } 190 } 191 } 192