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

GH3530: Add locale specific tests to check for invalid type conversions. #3548

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f3fe3d2
GH3530: Add locale specific tests to check for invalid type conversions.
May 15, 2024
2c8adc1
GH-3530: Change id type in preparation for manually creating the schema.
May 15, 2024
1a29463
GH-3530: Attempt to manually create the schema using the current dial…
May 15, 2024
af82e96
GH-3530: Add missing comma and space after Id field.
May 15, 2024
c62a7e8
GH-3530: Use the same syntax as Table.SqlCreateString for the identit…
May 15, 2024
1394492
GH-3530: Break up the tests by type so we can see which types fail wh…
May 15, 2024
72367b2
GH-3530: Fix incorrect test for Double types.
May 15, 2024
e66e1c6
GH-3530: Add a meaningful message when TestDouble fails.
May 15, 2024
a9b0910
GH-3530: Attempt to switch to a guid Id as some databases do not supp…
May 15, 2024
5282ab3
GH-3530: Guid key's are not identity columns and are defined like all…
May 15, 2024
11d645e
GH-3530: Correctly add the primary key constraint for the Id column.
May 15, 2024
aa7cd99
GH-3530: Allow all properties of the LocaleEntity to be nullable.
May 15, 2024
2666a44
GH-3530: Separate the types into different tables to avoid failure on…
May 15, 2024
d10c4d8
Generate async files
github-actions[bot] May 15, 2024
fd4f95c
GH-3530: Remove trailing comma and space from last column in table ge…
May 15, 2024
956219f
Generate async files
github-actions[bot] May 15, 2024
7ec4e58
GH-3530: Rename the Value column to DataValue to avoid conflicts with…
May 15, 2024
6461ddd
Generate async files
github-actions[bot] May 15, 2024
e09ffe6
GH-3530: Add additional test that uses the DateTime type with the
May 16, 2024
c2c7bd5
Generate async files
github-actions[bot] May 16, 2024
ea81464
GH-3530: Let the SchemaExporter drop and create tables before attempt…
May 16, 2024
06edc33
Generate async files
github-actions[bot] May 16, 2024
91f2599
GH-3530: Add exception handler to handle cases when the table already…
May 16, 2024
f9575cb
Generate async files
github-actions[bot] May 16, 2024
a2cf98c
GH-5350: Rename Entity<T> to DataEntity<T> to address Deepsource issue.
May 16, 2024
f1661eb
GH-5350: Use StringBuilder.Append(char) for appending a single charac…
May 16, 2024
36b951e
GH-3530: Switch to simple using statements per Deepsource.
May 16, 2024
2e3206f
GH-3530: Switch to file scoped namespacing per Deepsource.
May 16, 2024
8625f25
GH-3530: Replace explicit type with var to avoid type duplication.
May 16, 2024
58fe6c7
Generate async files
github-actions[bot] May 16, 2024
2be793d
GH-3530: Switch to file scoped namespace per DeepSource.
May 16, 2024
ac708f6
GH-5350: Add global setting for specifying the locale of the database.
May 29, 2024
bb38501
GH-3530: Use type specific methods of DbDataReader where possible and
May 29, 2024
6dbd4f0
GH-3530: Fix failing BooleanType test due to changed behavior of the …
May 29, 2024
97a516d
GH-3530: Fix failing CharBooleanType test due to change in behavior o…
May 29, 2024
f8eb2a6
GH-3530: Add tests for float type.
May 30, 2024
445d5a9
GH-3530: Add a mechanism to Drivers to execute a command and return a…
Jun 6, 2024
fa84e8a
GH-3530: Add a class for wrapping a DbDataReader.
Jun 6, 2024
f135115
GH-3530: Add a DbDataReader for performing direct casts of values ret…
May 30, 2024
4da33b7
GH-3530: Add TypeOfConnection and TypeOfCommand properties to the Ref…
Jun 7, 2024
b6b55f4
GH-3530: The Oracle driver does not support DbDataReader.GetChar and …
Jun 6, 2024
ec49d5f
GH-3530: The MySql driver doesn't properly convert strings from the d…
Jun 10, 2024
4b010ce
GH-3530: Firebird incorrectly uses the current culture to convert dat…
Jun 10, 2024
de669f2
GH-3530: Many drivers lack support for the DbDataReader.GetChar metho…
Jun 11, 2024
adf1f01
GH-3530: The Sql Server 2008 driver lacks support for the DbDataReade…
Jun 11, 2024
6e49aac
GH-3530: The MicrosoftDataSqlClientDriver does not support the DbData…
Jun 6, 2024
6610fe2
GH-3530: The SAP SQL Anywhere driver does not support the DbDataReade…
Jun 11, 2024
b99474d
GH-3530: The Sql Server CE driver does not support the DbDataReader.G…
Jun 11, 2024
f8637c7
GH-3530: Ensure all DbDataReader.TryGet extention methods catch all t…
Jun 11, 2024
b94e807
GH-3530: Add a warning when a driver throws an exception while retrie…
Jun 14, 2024
2b8c35e
GH-3530: The SAP SqlAnywhere driver does not implement DbDataReader.G…
Jun 14, 2024
dff6329
GH-3530: The Sybase SqlAnywhere DotNet4 driver does not implement DbD…
Jun 14, 2024
5c022a5
GH-3530: The Sybase SqlAnywhere driver does not implement DbDataReade…
Jun 14, 2024
0563713
GH-3530: Add an extension method to specify the return types when cal…
Jun 14, 2024
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
230 changes: 230 additions & 0 deletions src/NHibernate.Test/Async/NHSpecificTest/GH3530/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System;
using System.Data;
using System.Globalization;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Text;
using NHibernate.SqlTypes;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH3530;
using System.Threading.Tasks;
using System.Threading;

[TestFixture]
public class FixtureAsync : BugTestCase
{
private CultureInfo initialCulture;

[OneTimeSetUp]
public void FixtureSetup()
{
initialCulture = CurrentCulture;
}

[OneTimeTearDown]
public void FixtureTearDown()
{
CurrentCulture = initialCulture;
}

protected override void OnTearDown()
{
using var session = OpenSession();
using var transaction = session.BeginTransaction();

session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}

protected override void CreateSchema()
{
CreateTable("Integer");
CreateTable("DateTime");
CreateTable("Double");
CreateTable("Decimal");
CreateTable("Float");

base.CreateSchema();
}

/// <summary>
/// This function creates the schema for our custom entities.
/// If the SchemaExporter provided a mechanism to override the database
/// type, this method would not be required.
/// </summary>
/// <param name="name"></param>
private void CreateTable(string name)
{
var sb = new StringBuilder();
var guidType = Dialect.GetTypeName(SqlTypeFactory.Guid);
var stringType = Dialect.GetTypeName(SqlTypeFactory.GetAnsiString(255));

var catalog = GetQuotedDefaultCatalog();
var schema = GetQuotedDefaultSchema();
var table = GetQualifiedName(catalog, schema, $"{name}Entity");

sb.Append($"{Dialect.CreateTableString} {table} (");

// Generate columns
sb.Append($"Id {guidType}, ");
sb.Append($"DataValue {stringType}");

// Add the primary key contraint for the identity column
sb.Append($", {Dialect.PrimaryKeyString} ( Id )");
sb.Append(')');

using var cn = Sfi.ConnectionProvider.GetConnection();
try
{
using var cmd = cn.CreateCommand();

cmd.CommandText = sb.ToString();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Assert.Warn($"Creating the schema failed, assuming it already exists. {ex}");
}
finally
{
Sfi.ConnectionProvider.CloseConnection(cn);
}
}

private string GetQuotedDefaultCatalog()
{
var t = cfg.GetType();
var getQuotedDefaultCatalog = t.GetMethod("GetQuotedDefaultCatalog", BindingFlags.Instance | BindingFlags.NonPublic);

return (string) getQuotedDefaultCatalog.Invoke(cfg, [Dialect]);
}

private string GetQuotedDefaultSchema()
{
var t = cfg.GetType();
var getQuotedDefaultSchema = t.GetMethod("GetQuotedDefaultSchema", BindingFlags.Instance | BindingFlags.NonPublic);

return (string) getQuotedDefaultSchema.Invoke(cfg, [Dialect]);
}

private string GetQualifiedName(string catalog, string schema, string name)
{
return Dialect.Qualify(catalog, schema, name);
}

private async Task PerformTestAsync<T, U>(CultureInfo from, CultureInfo to, T expectedValue, Action<T, T> assert, CancellationToken cancellationToken = default(CancellationToken))
where T : struct
where U : DataEntity<T>, new()
{
object id;

CurrentCulture = from;
using (var session = OpenSession())
using (var tx = session.BeginTransaction())
{
var entity = new U()
{
DataValue = expectedValue
};

id = await (session.SaveAsync(entity, cancellationToken));
await (tx.CommitAsync(cancellationToken));
}

CurrentCulture = to;
using (var session = OpenSession())
using (var tx = session.BeginTransaction())
{
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
var entity = await (session.GetAsync<U>(id, cancellationToken));
AppDomain.CurrentDomain.FirstChanceException -= OnFirstChanceException;

assert(expectedValue, entity.DataValue);
}
}

private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
Assert.Warn($"Driver threw a {e.Exception.GetType().Name} exception while retrieving the value.");
}

[Test, TestCaseSource(nameof(GetTestCases))]
public async Task TestNHDateTimeAsync(CultureInfo from, CultureInfo to)
{
var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));

await (PerformTestAsync<DateTime, NHDateTimeEntity>(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual)));
}

[Test, TestCaseSource(nameof(GetTestCases))]
public async Task TestDateTimeAsync(CultureInfo from, CultureInfo to)
{
var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));

await (PerformTestAsync<DateTime, DateTimeEntity>(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual)));
}

[Test, TestCaseSource(nameof(GetTestCases))]
public async Task TestDecimalAsync(CultureInfo from, CultureInfo to)
{
decimal decimalValue = 12.3m;

await (PerformTestAsync<decimal, DecimalEntity>(from, to, decimalValue, (expected, actual) => Assert.AreEqual(expected, actual)));
}

[Test, TestCaseSource(nameof(GetTestCases))]
public async Task TestDoubleAsync(CultureInfo from, CultureInfo to)
{
double doubleValue = 12.3d;

await (PerformTestAsync<double, DoubleEntity>(from, to, doubleValue,
(expected, actual) => Assert.True(Math.Abs(expected - actual) < double.Epsilon, $"Expected {expected} but was {actual}\n")
));
}

[Test, TestCaseSource(nameof(GetTestCases))]

public async Task TestIntegerAsync(CultureInfo from, CultureInfo to)
{
int integerValue = 123;

await (PerformTestAsync<int, IntegerEntity>(from, to, integerValue, (expected, actual) => Assert.AreEqual(expected, actual)));
}

[Test, TestCaseSource(nameof(GetTestCases))]
public async Task TestFloatAsync(CultureInfo from, CultureInfo to)
{
float floatValue = 12.3f;

await (PerformTestAsync<float, FloatEntity>(from, to, floatValue,
(expected, actual) => Assert.True(Math.Abs(expected - actual) < float.Epsilon, $"Expected {expected} but was {actual}\n")
));
}

private CultureInfo CurrentCulture
{
get => CultureInfo.CurrentCulture;
set => CultureInfo.CurrentCulture = value;
}

public static object[][] GetTestCases()
{
return [
[new CultureInfo("en-US"), new CultureInfo("de-DE")],
[new CultureInfo("en-US"), new CultureInfo("ar-SA", false)],
[new CultureInfo("en-US"), new CultureInfo("th-TH", false)],
];
}
}
21 changes: 21 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH3530/Entities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace NHibernate.Test.NHSpecificTest.GH3530;

public abstract class Entity
{
public virtual Guid Id { get; set; }
}

public abstract class DataEntity<T>:Entity where T : struct
{
public virtual T DataValue { get; set; }
}

public class IntegerEntity : DataEntity<int> { }
public class DateTimeEntity : DataEntity<DateTime> { }

public class DoubleEntity : DataEntity<double> { }
public class DecimalEntity : DataEntity<decimal> { }
public class FloatEntity : DataEntity<float> { }
public class NHDateTimeEntity : DataEntity<DateTime> { }
Loading
Loading