// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Xunit; namespace Microsoft.Net.Http.Headers { public class NameValueHeaderValueTest { [Fact] public void Ctor_NameNull_Throw() { Assert.Throws(() => new NameValueHeaderValue(null)); // null and empty should be treated the same. So we also throw for empty strings. Assert.Throws(() => new NameValueHeaderValue(string.Empty)); } [Fact] public void Ctor_NameInvalidFormat_ThrowFormatException() { // When adding values using strongly typed objects, no leading/trailing LWS (whitespaces) are allowed. AssertFormatException(" text ", null); AssertFormatException("text ", null); AssertFormatException(" text", null); AssertFormatException("te xt", null); AssertFormatException("te=xt", null); // The ctor takes a name which must not contain '='. AssertFormatException("teäxt", null); } [Fact] public void Ctor_NameValidFormat_SuccessfullyCreated() { var nameValue = new NameValueHeaderValue("text", null); Assert.Equal("text", nameValue.Name); } [Fact] public void Ctor_ValueInvalidFormat_ThrowFormatException() { // When adding values using strongly typed objects, no leading/trailing LWS (whitespaces) are allowed. AssertFormatException("text", " token "); AssertFormatException("text", "token "); AssertFormatException("text", " token"); AssertFormatException("text", "token string"); AssertFormatException("text", "\"quoted string with \" quotes\""); AssertFormatException("text", "\"quoted string with \"two\" quotes\""); } [Fact] public void Ctor_ValueValidFormat_SuccessfullyCreated() { CheckValue(null); CheckValue(string.Empty); CheckValue("token_string"); CheckValue("\"quoted string\""); CheckValue("\"quoted string with quoted \\\" quote-pair\""); } [Fact] public void Value_CallSetterWithInvalidValues_Throw() { // Just verify that the setter calls the same validation the ctor invokes. Assert.Throws(() => { var x = new NameValueHeaderValue("name"); x.Value = " x "; }); Assert.Throws(() => { var x = new NameValueHeaderValue("name"); x.Value = "x y"; }); } [Fact] public void ToString_UseNoValueAndTokenAndQuotedStringValues_SerializedCorrectly() { var nameValue = new NameValueHeaderValue("text", "token"); Assert.Equal("text=token", nameValue.ToString()); nameValue.Value = "\"quoted string\""; Assert.Equal("text=\"quoted string\"", nameValue.ToString()); nameValue.Value = null; Assert.Equal("text", nameValue.ToString()); nameValue.Value = string.Empty; Assert.Equal("text", nameValue.ToString()); } [Fact] public void GetHashCode_ValuesUseDifferentValues_HashDiffersAccordingToRfc() { var nameValue1 = new NameValueHeaderValue("text"); var nameValue2 = new NameValueHeaderValue("text"); nameValue1.Value = null; nameValue2.Value = null; Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "token"; nameValue2.Value = null; Assert.NotEqual(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "token"; nameValue2.Value = string.Empty; Assert.NotEqual(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = null; nameValue2.Value = string.Empty; Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "token"; nameValue2.Value = "TOKEN"; Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "token"; nameValue2.Value = "token"; Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "\"quoted string\""; nameValue2.Value = "\"QUOTED STRING\""; Assert.NotEqual(nameValue1.GetHashCode(), nameValue2.GetHashCode()); nameValue1.Value = "\"quoted string\""; nameValue2.Value = "\"quoted string\""; Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); } [Fact] public void GetHashCode_NameUseDifferentCasing_HashDiffersAccordingToRfc() { var nameValue1 = new NameValueHeaderValue("text"); var nameValue2 = new NameValueHeaderValue("TEXT"); Assert.Equal(nameValue1.GetHashCode(), nameValue2.GetHashCode()); } [Fact] public void Equals_ValuesUseDifferentValues_ValuesAreEqualOrDifferentAccordingToRfc() { var nameValue1 = new NameValueHeaderValue("text"); var nameValue2 = new NameValueHeaderValue("text"); nameValue1.Value = null; nameValue2.Value = null; Assert.True(nameValue1.Equals(nameValue2), " vs. ."); nameValue1.Value = "token"; nameValue2.Value = null; Assert.False(nameValue1.Equals(nameValue2), "token vs. ."); nameValue1.Value = null; nameValue2.Value = "token"; Assert.False(nameValue1.Equals(nameValue2), " vs. token."); nameValue1.Value = string.Empty; nameValue2.Value = "token"; Assert.False(nameValue1.Equals(nameValue2), "string.Empty vs. token."); nameValue1.Value = null; nameValue2.Value = string.Empty; Assert.True(nameValue1.Equals(nameValue2), " vs. string.Empty."); nameValue1.Value = "token"; nameValue2.Value = "TOKEN"; Assert.True(nameValue1.Equals(nameValue2), "token vs. TOKEN."); nameValue1.Value = "token"; nameValue2.Value = "token"; Assert.True(nameValue1.Equals(nameValue2), "token vs. token."); nameValue1.Value = "\"quoted string\""; nameValue2.Value = "\"QUOTED STRING\""; Assert.False(nameValue1.Equals(nameValue2), "\"quoted string\" vs. \"QUOTED STRING\"."); nameValue1.Value = "\"quoted string\""; nameValue2.Value = "\"quoted string\""; Assert.True(nameValue1.Equals(nameValue2), "\"quoted string\" vs. \"quoted string\"."); Assert.False(nameValue1.Equals(null), "\"quoted string\" vs. ."); } [Fact] public void Equals_NameUseDifferentCasing_ConsideredEqual() { var nameValue1 = new NameValueHeaderValue("text"); var nameValue2 = new NameValueHeaderValue("TEXT"); Assert.True(nameValue1.Equals(nameValue2), "text vs. TEXT."); } [Fact] public void Parse_SetOfValidValueStrings_ParsedCorrectly() { CheckValidParse(" name = value ", new NameValueHeaderValue("name", "value")); CheckValidParse(" name", new NameValueHeaderValue("name")); CheckValidParse(" name ", new NameValueHeaderValue("name")); CheckValidParse(" name=\"value\"", new NameValueHeaderValue("name", "\"value\"")); CheckValidParse("name=value", new NameValueHeaderValue("name", "value")); CheckValidParse("name=\"quoted str\"", new NameValueHeaderValue("name", "\"quoted str\"")); CheckValidParse("name\t =va1ue", new NameValueHeaderValue("name", "va1ue")); CheckValidParse("name= va*ue ", new NameValueHeaderValue("name", "va*ue")); CheckValidParse("name=", new NameValueHeaderValue("name", "")); } [Fact] public void Parse_SetOfInvalidValueStrings_Throws() { CheckInvalidParse("name[value"); CheckInvalidParse("name=value="); CheckInvalidParse("name=会"); CheckInvalidParse("name==value"); CheckInvalidParse("name= va:ue"); CheckInvalidParse("=value"); CheckInvalidParse("name value"); CheckInvalidParse("name=,value"); CheckInvalidParse("会"); CheckInvalidParse(null); CheckInvalidParse(string.Empty); CheckInvalidParse(" "); CheckInvalidParse(" ,,"); CheckInvalidParse(" , , name = value , "); CheckInvalidParse(" name,"); CheckInvalidParse(" ,name=\"value\""); } [Fact] public void TryParse_SetOfValidValueStrings_ParsedCorrectly() { CheckValidTryParse(" name = value ", new NameValueHeaderValue("name", "value")); CheckValidTryParse(" name", new NameValueHeaderValue("name")); CheckValidTryParse(" name=\"value\"", new NameValueHeaderValue("name", "\"value\"")); CheckValidTryParse("name=value", new NameValueHeaderValue("name", "value")); } [Fact] public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() { CheckInvalidTryParse("name[value"); CheckInvalidTryParse("name=value="); CheckInvalidTryParse("name=会"); CheckInvalidTryParse("name==value"); CheckInvalidTryParse("=value"); CheckInvalidTryParse("name value"); CheckInvalidTryParse("name=,value"); CheckInvalidTryParse("会"); CheckInvalidTryParse(null); CheckInvalidTryParse(string.Empty); CheckInvalidTryParse(" "); CheckInvalidTryParse(" ,,"); CheckInvalidTryParse(" , , name = value , "); CheckInvalidTryParse(" name,"); CheckInvalidTryParse(" ,name=\"value\""); } [Fact] public void ParseList_SetOfValidValueStrings_ParsedCorrectly() { var inputs = new[] { "", "name=value1", "", " name = value2 ", "\r\n name =value3\r\n ", "name=\"value 4\"", "name=\"value会5\"", "name=value6,name=value7", "name=\"value 8\", name= \"value 9\"", }; var results = NameValueHeaderValue.ParseList(inputs); var expectedResults = new[] { new NameValueHeaderValue("name", "value1"), new NameValueHeaderValue("name", "value2"), new NameValueHeaderValue("name", "value3"), new NameValueHeaderValue("name", "\"value 4\""), new NameValueHeaderValue("name", "\"value会5\""), new NameValueHeaderValue("name", "value6"), new NameValueHeaderValue("name", "value7"), new NameValueHeaderValue("name", "\"value 8\""), new NameValueHeaderValue("name", "\"value 9\""), }.ToList(); Assert.Equal(expectedResults, results); } [Fact] public void TryParseList_SetOfValidValueStrings_ParsedCorrectly() { var inputs = new[] { "", "name=value1", "", " name = value2 ", "\r\n name =value3\r\n ", "name=\"value 4\"", "name=\"value会5\"", "name=value6,name=value7", "name=\"value 8\", name= \"value 9\"", }; IList results; Assert.True(NameValueHeaderValue.TryParseList(inputs, out results)); var expectedResults = new[] { new NameValueHeaderValue("name", "value1"), new NameValueHeaderValue("name", "value2"), new NameValueHeaderValue("name", "value3"), new NameValueHeaderValue("name", "\"value 4\""), new NameValueHeaderValue("name", "\"value会5\""), new NameValueHeaderValue("name", "value6"), new NameValueHeaderValue("name", "value7"), new NameValueHeaderValue("name", "\"value 8\""), new NameValueHeaderValue("name", "\"value 9\""), }.ToList(); Assert.Equal(expectedResults, results); } [Fact] public void ParseList_WithSomeInvlaidValues_Throws() { var inputs = new[] { "", "name1=value1", "name2", " name3 = 3, value a", "name4 =value4, name5 = value5 b", "name6=\"value 6", "name7=\"value会7\"", "name8=value8,name9=value9", "name10=\"value 10\", name11= \"value 11\"", }; Assert.Throws(() => NameValueHeaderValue.ParseList(inputs)); } [Fact] public void TryParseList_WithSomeInvlaidValues_ReturnsFalse() { var inputs = new[] { "", "name1=value1", "name2", " name3 = 3, value a", "name4 =value4, name5 = value5 b", "name6=\"value 6", "name7=\"value会7\"", "name8=value8,name9=value9", "name10=\"value 10\", name11= \"value 11\"", }; IList results; Assert.False(NameValueHeaderValue.TryParseList(inputs, out results)); } #region Helper methods private void CheckValidParse(string input, NameValueHeaderValue expectedResult) { var result = NameValueHeaderValue.Parse(input); Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => NameValueHeaderValue.Parse(input)); } private void CheckValidTryParse(string input, NameValueHeaderValue expectedResult) { NameValueHeaderValue result = null; Assert.True(NameValueHeaderValue.TryParse(input, out result)); Assert.Equal(expectedResult, result); } private void CheckInvalidTryParse(string input) { NameValueHeaderValue result = null; Assert.False(NameValueHeaderValue.TryParse(input, out result)); Assert.Null(result); } private static void CheckValue(string value) { var nameValue = new NameValueHeaderValue("text", value); Assert.Equal(value, nameValue.Value); } private static void AssertFormatException(string name, string value) { Assert.Throws(() => new NameValueHeaderValue(name, value)); } #endregion } }