Skip to content

Commit

Permalink
Merge pull request #11 from Archomeda/rework-enums-and-flags
Browse files Browse the repository at this point in the history
Improve ApiEnum and ApiFlags
  • Loading branch information
Archomeda authored Aug 10, 2019
2 parents 889ec14 + 35aa8ff commit d9c9e84
Show file tree
Hide file tree
Showing 14 changed files with 446 additions and 274 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Gw2Sharp History

## 0.6.1
### Endpoints
- Change property `Emblem` in `Gw2Sharp.WebApi.V2.Models.Guild` to be nullable because the API might leave this property out ([#10](https://github.com/Archomeda/Gw2Sharp/issues/10))

### Fixes
- Fix default instantiations of `ApiEnum` and `ApiFlags` that might cause `InvalidCastException`s when requesting data by removing the non-generic `ApiEnum` and `ApiFlags` variants (they were only used internally for easy casting when deserializing) ([#10](https://github.com/Archomeda/Gw2Sharp/issues/10))

## 0.6.0
### Endpoints
- **Breaking:** `Gw2Sharp.WebApi.V2.Models.GuildTeam` has had the type of its property `Ladders` changed from `PvpStatsLadders` to `IReadOnlyDictionary<string, PvpStatsAggregate>`
Expand Down
2 changes: 2 additions & 0 deletions Gw2Sharp.Tests/Gw2Sharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
<None Remove="TestFiles\Gliders\Gliders.single.json" />
<None Remove="TestFiles\Guild\GuildId.1.json" />
<None Remove="TestFiles\Guild\GuildId.2.json" />
<None Remove="TestFiles\Guild\GuildId.3.json" />
<None Remove="TestFiles\Guild\GuildIdLog.json" />
<None Remove="TestFiles\Guild\GuildIdMembers.json" />
<None Remove="TestFiles\Guild\GuildIdRanks.json" />
Expand Down Expand Up @@ -369,6 +370,7 @@
<EmbeddedResource Include="TestFiles\Gliders\Gliders.single.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildId.1.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildId.2.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildId.3.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildIdLog.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildIdMembers.json" />
<EmbeddedResource Include="TestFiles\Guild\GuildIdRanks.json" />
Expand Down
2 changes: 1 addition & 1 deletion Gw2Sharp.Tests/Json/Converters/ApiEnumConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void NoWriteTest()
public void CanConvertTest()
{
var converter = new ApiEnumConverter();
Assert.True(converter.CanConvert(typeof(ApiEnum)));
Assert.True(converter.CanConvert(typeof(ApiEnum<>)));
}
}
}
5 changes: 5 additions & 0 deletions Gw2Sharp.Tests/TestFiles/Guild/GuildId.3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"id": "12345678-90AB-CDEF-FEDC-BA0987654321",
"name": "abc",
"tag": "ABC"
}
65 changes: 39 additions & 26 deletions Gw2Sharp.Tests/WebApi/V2/Clients/BaseEndpointClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ protected void AssertJsonObject(JArray expected, object actual)

protected void AssertJsonObject(JValue expected, object actual)
{
bool switched = true;
switch (actual)
{
case Guid guid:
Expand All @@ -341,40 +342,52 @@ protected void AssertJsonObject(JValue expected, object actual)
case TimeSpan timeSpan:
Assert.Equal(TimeSpan.FromSeconds(expected.Value<int>()), timeSpan);
break;
case ApiEnum @enum:
Assert.Equal(expected.Value<string>(), @enum.RawValue);
var typeInfo = @enum.GetType().GetTypeInfo();
if (typeInfo.IsGenericType && typeInfo.GenericTypeArguments.Length > 0)
{
var enumType = typeInfo.GenericTypeArguments[0];
if (@enum.IsUnknown)
{
var enumNames = Enum.GetNames(enumType).Select(x => x.Replace("_", ""));
Assert.True(enumNames.Contains(@enum.RawValue, StringComparer.OrdinalIgnoreCase), $"Expected '{expected}' to be a value in enumerator {@enum.Value.GetType().FullName}; detected value '{@enum.Value}'");
}
Assert.Equal(expected.Value<string>().ParseEnum(enumType), @enum.Value);
}
else
throw new InvalidOperationException("Expected a generic ApiEnum");
break;
case int @int:
Assert.Equal(Convert.ToInt32(expected.Value), @int);
Assert.Equal(expected.Value<int>(), @int);
break;
case long @long:
Assert.Equal(expected.Value<long>(), @long);
break;
case double @double:
Assert.Equal(Convert.ToDouble(expected.Value), @double, 10);
Assert.Equal(expected.Value<double>(), @double, 10);
break;
case bool @bool:
Assert.Equal(expected.Value<bool>(), @bool);
break;
case string @string:
Assert.Equal(expected.Value, @string);
break;
case null:
if (expected.Type == JTokenType.String)
Assert.Equal(expected.Value, string.Empty);
break;
default:
if (((actual != null && !(actual is string)) || actual == null) &&
expected.Type == JTokenType.String)
switched = false;
break;
}

if (!switched)
{
var typeInfo = actual!.GetType().GetTypeInfo();
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(ApiEnum<>))
{
var enumType = typeInfo.GenericTypeArguments[0];
dynamic @enum = actual; // Just for easiness

Assert.Equal(expected.Value<string>(), @enum.RawValue);
if (@enum.IsUnknown)
{
// Special case where the resulting value has been auto deserialized into something else than a string,
// while the original is a string.
Assert.Equal(expected.Value, actual?.ToString() ?? string.Empty);
var enumNames = Enum.GetNames(enumType).Select(x => x.Replace("_", ""));
Assert.True(enumNames.Contains((string)@enum.RawValue, StringComparer.OrdinalIgnoreCase), $"Expected '{expected}' to be a value in enumerator {@enum.Value.GetType().FullName}; detected value '{@enum.Value}'");
}
else
Assert.Equal(expected.Value, actual);
break;
Assert.Equal(expected.Value<string>().ParseEnum(enumType), @enum.Value);

switched = true;
}
}

if (!switched)
Assert.Equal(expected.Value, actual!.ToString());
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ protected override IGuildIdClient CreateClient(IGw2Client gw2Client) =>
[Theory]
[InlineData("TestFiles.Guild.GuildId.1.json")]
[InlineData("TestFiles.Guild.GuildId.2.json")]
[InlineData("TestFiles.Guild.GuildId.3.json")]
public Task BlobTest(string file) => this.AssertBlobDataAsync(this.Client, file);
}
}
86 changes: 41 additions & 45 deletions Gw2Sharp.Tests/WebApi/V2/Models/ApiEnumTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.ComponentModel;
using Gw2Sharp.Tests.Helpers;
using Gw2Sharp.WebApi.V2.Models;
using Newtonsoft.Json;
using Xunit;
Expand Down Expand Up @@ -35,12 +33,19 @@ public void ConstructorTest()
[Theory]
[InlineData("{\"Enum\":\"EnumValue2\"}", "EnumValue2", TestEnum.EnumValue2)]
[InlineData("{\"Enum\":\"SomeRandomValue\"}", "SomeRandomValue", TestEnum.EnumValue3)]
[InlineData("{\"Enum\":undefined}", "EnumValue3", TestEnum.EnumValue3)]
public void DeserializeTest(string json, string expectedRaw, TestEnum expected)
[InlineData("{\"Enum\":\"\"}", "", TestEnum.EnumValue3)]
[InlineData("{\"Enum\":undefined}", null, TestEnum.EnumValue3)]
[InlineData("{}", null, null)]
public void DeserializeTest(string json, string expectedRaw, TestEnum? expected)
{
var obj = JsonConvert.DeserializeObject<JsonObject>(json);
Assert.Equal(expectedRaw, obj.Enum.RawValue);
Assert.Equal(expected, obj.Enum.Value);
if (expected == null)
Assert.Null(obj.Enum);
else
{
Assert.Equal(expectedRaw, obj.Enum.RawValue);
Assert.Equal(expected, obj.Enum.Value);
}
}

[Fact]
Expand Down Expand Up @@ -75,6 +80,14 @@ public void ImplicitConversionToEnumTest()
Assert.Equal(expected, actual);
}

[Fact]
public void ImplicitConversionFromStringTest()
{
var expected = new ApiEnum<TestEnum>(TestEnum.EnumValue2);
ApiEnum<TestEnum> actual = "EnumValue2";
Assert.Equal(expected, actual);
}

[Fact]
public void ImplicitConversionToStringTest()
{
Expand All @@ -94,81 +107,64 @@ public void ToStringTest()
[Fact]
public void EqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
Assert.True(item.Equals(item2));
Assert.True(item1.Equals(item2));
}

[Fact]
public void NotEqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue2.ToString());
var item3 = new ApiEnum<TestEnum>(TestEnum.EnumValue2, TestEnum.EnumValue2.ToString());
Assert.False(item.Equals(item2));
Assert.False(item.Equals(item3));
Assert.False(item1.Equals(item2));
Assert.False(item1.Equals(item3));
Assert.False(item2.Equals(item3));
Assert.False(item.Equals(new object()));
Assert.False(item.Equals(null!));
Assert.False(item1.Equals(new object()));
Assert.False(item1.Equals(null!));
}

[Fact]
public void HashCodeEqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
Assert.Equal(item.GetHashCode(), item2.GetHashCode());
Assert.Equal(item1.GetHashCode(), item2.GetHashCode());
}

[Fact]
public void HashCodeNotEqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue2, TestEnum.EnumValue2.ToString());
Assert.NotEqual(item.GetHashCode(), item2.GetHashCode());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue2.ToString());
var item3 = new ApiEnum<TestEnum>(TestEnum.EnumValue2, TestEnum.EnumValue2.ToString());
Assert.NotEqual(item1.GetHashCode(), item2.GetHashCode());
Assert.NotEqual(item2.GetHashCode(), item3.GetHashCode());
Assert.NotEqual(item1.GetHashCode(), item3.GetHashCode());
}

[Fact]
public void OperatorEqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
Assert.True(item == item2);
#pragma warning disable CS1718 // Comparison made to same variable
Assert.True(item == (ApiEnum)item);
#pragma warning restore CS1718 // Comparison made to same variable
Assert.True(item1 == item2);
}

[Fact]
public void OperatorNotEqualsTest()
{
var item = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item1 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString());
var item2 = new ApiEnum<TestEnum>(TestEnum.EnumValue1, TestEnum.EnumValue2.ToString());
var item3 = new ApiEnum<TestEnum>(TestEnum.EnumValue2, TestEnum.EnumValue2.ToString());
Assert.True(item != item2);
Assert.True(item != item3);
Assert.True(item1 != item2);
Assert.True(item1 != item3);
Assert.True(item2 != item3);
#pragma warning disable CS0253 // Possible unintended reference comparison; right hand side needs cast
Assert.True(item != new object());
Assert.True(item1 != new object());
#pragma warning restore CS0253 // Possible unintended reference comparison; right hand side needs cast
Assert.True(item != null!);

Assert.True((ApiEnum)item != item2);
Assert.True(item1 != null!);
}

#region ArgumentNullException tests

[Fact]
public void ArgumentNullConstructorTest()
{
AssertArguments.ThrowsWhenNull(
() => new ApiEnum<Enum>(TestEnum.EnumValue1),
new[] { true });
AssertArguments.ThrowsWhenNull(
() => new ApiEnum<Enum>(TestEnum.EnumValue1, TestEnum.EnumValue1.ToString()),
new[] { true, false });
}

#endregion
}
}
Loading

0 comments on commit d9c9e84

Please sign in to comment.