Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CP-51694: Add date deserialization unit tests for C#/Java/Go #6027

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/generate-and-build-sdks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
path: |
_build/install/default/share/go/*
!_build/install/default/share/go/dune
!_build/install/default/share/go/**/*_test.go

- name: Store Java SDK source
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -138,6 +139,13 @@ jobs:
name: SDK_Source_CSharp
path: source/

- name: Test C# SDK
shell: pwsh
run: |
dotnet test source/XenServerTest `
--disable-build-servers `
--verbosity=normal

- name: Build C# SDK
shell: pwsh
run: |
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/go-ci/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ runs:
working-directory: ${{ github.workspace }}/_build/install/default/share/go/src
args: --config=${{ github.workspace }}/.golangci.yml

- name: Run Go Tests
danilo-delbusso marked this conversation as resolved.
Show resolved Hide resolved
shell: bash
working-directory: ${{ github.workspace }}/_build/install/default/share/go/src
run: go test -v

- name: Run CI for Go SDK
shell: bash
run: |
Expand Down
139 changes: 139 additions & 0 deletions ocaml/sdk-gen/csharp/autogen/XenServerTest/DateTimeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (c) Cloud Software Group, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2) 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.
*
* 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 HOLDER 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.
*/

using System.Reflection;
using Newtonsoft.Json;
using XenAPI;
using Console = System.Console;

namespace XenServerTest;

internal class DateTimeObject
{
[JsonConverter(typeof(XenDateTimeConverter))]
public DateTime Date { get; set; }
}

[TestClass]
public class DateTimeTests
{
private readonly JsonSerializerSettings _settings = new()
{
Converters = new List<JsonConverter> { new XenDateTimeConverter() }
};

[TestMethod]
[DynamicData(nameof(GetTestData), DynamicDataSourceType.Method,
DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]
public void TestXenDateTimeConverter(string dateString, DateTime expectedDateTime)
{
try
{
var jsonDateString = "{ \"Date\" : \"" + dateString + "\" }";
var actualDateTime = JsonConvert.DeserializeObject<DateTimeObject>(jsonDateString, _settings);

Assert.IsNotNull(actualDateTime, $"Failed to convert '{dateString}'");
Assert.IsTrue(expectedDateTime.Equals(actualDateTime.Date),
$"Conversion of '{dateString}' resulted in an incorrect DateTime value");
}
catch (Exception ex)
{
// Log the error or mark this specific data entry as failed
Console.WriteLine($@"Error processing dateString '{dateString}': {ex.Message}");
Assert.Fail($"An error occurred while processing '{dateString}'");
}
}

public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
{
return $"{methodInfo.Name}: '{data[0] as string}'";
}

public static IEnumerable<object[]> GetTestData()
{
// no dashes, no colons
yield return new object[] { "20220101T123045", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Unspecified) };
yield return new object[] { "20220101T123045Z", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Utc) };
yield return new object[] { "20220101T123045+03", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[] { "20220101T123045+0300", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[] { "20220101T123045+03:00", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };

yield return new object[]
{ "20220101T123045.123", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Unspecified) };
yield return new object[]
{ "20220101T123045.123Z", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Utc) };
yield return new object[]
{ "20220101T123045.123+03", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "20220101T123045.123+0300", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "20220101T123045.123+03:00", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };

// no dashes, with colons
yield return new object[]
{ "20220101T12:30:45", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Unspecified) };
yield return new object[] { "20220101T12:30:45Z", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Utc) };
yield return new object[] { "20220101T12:30:45+03", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[] { "20220101T12:30:45+0300", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[]
{ "20220101T12:30:45+03:00", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };

yield return new object[]
{ "20220101T12:30:45.123", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Unspecified) };
yield return new object[]
{ "20220101T12:30:45.123Z", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Utc) };
yield return new object[]
{ "20220101T12:30:45.123+03", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "20220101T12:30:45.123+0300", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "20220101T12:30:45.123+03:00", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };

// dashes and colons
yield return new object[]
{ "2022-01-01T12:30:45", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Unspecified) };
yield return new object[] { "2022-01-01T12:30:45Z", new DateTime(2022, 1, 1, 12, 30, 45, DateTimeKind.Utc) };
yield return new object[] { "2022-01-01T12:30:45+03", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[]
{ "2022-01-01T12:30:45+0300", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };
yield return new object[]
{ "2022-01-01T12:30:45+03:00", new DateTime(2022, 1, 1, 9, 30, 45, DateTimeKind.Local) };

yield return new object[]
{ "2022-01-01T12:30:45.123", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Unspecified) };
yield return new object[]
{ "2022-01-01T12:30:45.123Z", new DateTime(2022, 1, 1, 12, 30, 45, 123, DateTimeKind.Utc) };
yield return new object[]
{ "2022-01-01T12:30:45.123+03", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "2022-01-01T12:30:45.123+0300", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
yield return new object[]
{ "2022-01-01T12:30:45.123+03:00", new DateTime(2022, 1, 1, 9, 30, 45, 123, DateTimeKind.Local) };
}
}
27 changes: 27 additions & 0 deletions ocaml/sdk-gen/csharp/autogen/XenServerTest/XenServerTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\XenServer.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>

</Project>
15 changes: 10 additions & 5 deletions ocaml/sdk-gen/csharp/autogen/src/Converters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

[assembly: InternalsVisibleTo("XenServerTest")]

namespace XenAPI
{
Expand Down Expand Up @@ -437,12 +439,16 @@ internal class XenDateTimeConverter : IsoDateTimeConverter

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string str = JToken.Load(reader).ToString();
// JsonReader may have already parsed the date for us
if (reader.ValueType != null && reader.ValueType == typeof(DateTime))
{
return reader.Value;
}

DateTime result;
var str = JToken.Load(reader).ToString();

if (DateTime.TryParseExact(str, DateFormatsUtc, CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result))
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result))
return result;

if (DateTime.TryParseExact(str, DateFormatsLocal, CultureInfo.InvariantCulture,
Expand All @@ -454,9 +460,8 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is DateTime)
if (value is DateTime dateTime)
{
var dateTime = (DateTime)value;
dateTime = dateTime.ToUniversalTime();
var text = dateTime.ToString(DateFormatsUtc[0], CultureInfo.InvariantCulture);
writer.WriteValue(text);
Expand Down
91 changes: 91 additions & 0 deletions ocaml/sdk-gen/go/autogen/src/convert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) Cloud Software Group, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2) 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.
*
* 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 HOLDER 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.
*/

package xenapi_test

import (
"testing"
"time"

"go/xenapi"
)

func TestDateDeseralization(t *testing.T) {
dates := map[string]time.Time{
// no dashes, no colons
"20220101T123045": time.Date(2022, 1, 1, 12, 30, 45, 0, time.Local),
"20220101T123045Z": time.Date(2022, 1, 1, 12, 30, 45, 0, time.UTC),
"20220101T123045+03": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)), // +03 timezone
"20220101T123045+0300": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),
"20220101T123045+03:00": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),

"20220101T123045.123": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.Local),
"20220101T123045.123Z": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.UTC),
"20220101T123045.123+03": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),
"20220101T123045.123+0300": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),
"20220101T123045.123+03:00": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),

// no dashes, with colons
"20220101T12:30:45": time.Date(2022, 1, 1, 12, 30, 45, 0, time.Local),
"20220101T12:30:45Z": time.Date(2022, 1, 1, 12, 30, 45, 0, time.UTC),
"20220101T12:30:45+03": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),
"20220101T12:30:45+0300": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),
"20220101T12:30:45+03:00": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),

"20220101T12:30:45.123": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.Local),
"20220101T12:30:45.123Z": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.UTC),
"20220101T12:30:45.123+03": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),
"20220101T12:30:45.123+0300": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),
"20220101T12:30:45.123+03:00": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),

// dashes and colons
"2022-01-01T12:30:45": time.Date(2022, 1, 1, 12, 30, 45, 0, time.Local),
"2022-01-01T12:30:45Z": time.Date(2022, 1, 1, 12, 30, 45, 0, time.UTC),
"2022-01-01T12:30:45+03": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),
"2022-01-01T12:30:45+0300": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),
"2022-01-01T12:30:45+03:00": time.Date(2022, 1, 1, 12, 30, 45, 0, time.FixedZone("", 3*60*60)),

"2022-01-01T12:30:45.123": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.Local),
"2022-01-01T12:30:45.123Z": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.UTC),
"2022-01-01T12:30:45.123+03": time.Date(2022, 1, 1, 12, 30, 45, 123000000, time.FixedZone("", 3*60*60)),
}
for input, expected := range dates {
t.Run("Input:"+input, func(t *testing.T) {
result, err := xenapi.DeserializeTime("", input)
if err == nil {
matching := expected.Equal(result)
if !matching {
t.Fatalf(`Failed to find match for '%s'`, input)
}
} else {
t.Fatalf(`Failed to find match for '%s'`, input)
}
})
}
}
37 changes: 37 additions & 0 deletions ocaml/sdk-gen/go/autogen/src/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) Cloud Software Group, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2) 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.
*
* 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 HOLDER 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.
*/

// This file contains exports of private functions specifically for testing purposes.
// It allows test code to access and verify the behavior of internal functions within the `xenapi` package.

package xenapi

// DeserializeTime is a private function that deserializes a time value.
// It is exported for testing to allow verification of its functionality.
var DeserializeTime = deserializeTime
Loading
Loading