Strengthen connection pooling and locking tests

This commit is contained in:
2026-02-27 19:04:37 +01:00
parent aedb97e879
commit d534311b9a
2 changed files with 169 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
namespace DynamORM.Tests.Helpers
{
@@ -23,11 +24,16 @@ namespace DynamORM.Tests.Helpers
public int MaxConcurrentExecutions;
public int CurrentConcurrentExecutions;
public object LastTransactionSeen;
public readonly List<object> SeenTransactions = new List<object>();
public readonly List<FakeProviderConnection> Connections = new List<FakeProviderConnection>();
public ManualResetEventSlim FirstExecutionEntered = new ManualResetEventSlim(false);
public ManualResetEventSlim AllowExecution = new ManualResetEventSlim(true);
public bool BlockFirstExecution;
public bool BlockFirstReader;
public ManualResetEventSlim FirstReaderEntered = new ManualResetEventSlim(false);
public ManualResetEventSlim AllowReader = new ManualResetEventSlim(true);
private int _blocked;
private int _readerBlocked;
public void RecordExecution(IDbTransaction transaction)
{
@@ -39,6 +45,8 @@ namespace DynamORM.Tests.Helpers
Interlocked.Increment(ref ExecuteNonQueryCalls);
if (transaction != null)
LastTransactionSeen = transaction;
lock (SeenTransactions)
SeenTransactions.Add(transaction);
if (BlockFirstExecution && Interlocked.CompareExchange(ref _blocked, 1, 0) == 0)
{
@@ -51,6 +59,25 @@ namespace DynamORM.Tests.Helpers
{
Interlocked.Decrement(ref CurrentConcurrentExecutions);
}
public void RecordReaderOpen(IDbTransaction transaction)
{
int current = Interlocked.Increment(ref CurrentConcurrentExecutions);
int snapshot;
while ((snapshot = MaxConcurrentExecutions) < current)
Interlocked.CompareExchange(ref MaxConcurrentExecutions, current, snapshot);
if (transaction != null)
LastTransactionSeen = transaction;
lock (SeenTransactions)
SeenTransactions.Add(transaction);
if (BlockFirstReader && Interlocked.CompareExchange(ref _readerBlocked, 1, 0) == 0)
{
FirstReaderEntered.Set();
AllowReader.Wait(TimeSpan.FromSeconds(5));
}
}
}
internal sealed class FakeProviderFactory : DbProviderFactory
@@ -192,7 +219,67 @@ namespace DynamORM.Tests.Helpers
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
throw new NotSupportedException();
_state.RecordReaderOpen(DbTransaction);
return new FakeProviderDataReader(_state);
}
}
internal sealed class FakeProviderDataReader : DbDataReader
{
private readonly FakeProviderState _state;
private bool _closed;
public FakeProviderDataReader(FakeProviderState state)
{
_state = state;
}
public override object this[int ordinal] { get { return 1; } }
public override object this[string name] { get { return 1; } }
public override int Depth { get { return 0; } }
public override int FieldCount { get { return 1; } }
public override bool HasRows { get { return true; } }
public override bool IsClosed { get { return _closed; } }
public override int RecordsAffected { get { return 0; } }
public override bool GetBoolean(int ordinal) { return true; }
public override byte GetByte(int ordinal) { return 1; }
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) { return 0; }
public override char GetChar(int ordinal) { return '1'; }
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) { return 0; }
public override string GetDataTypeName(int ordinal) { return "Int32"; }
public override DateTime GetDateTime(int ordinal) { return DateTime.UtcNow; }
public override decimal GetDecimal(int ordinal) { return 1m; }
public override double GetDouble(int ordinal) { return 1d; }
public override System.Collections.IEnumerator GetEnumerator() { yield break; }
public override Type GetFieldType(int ordinal) { return typeof(int); }
public override float GetFloat(int ordinal) { return 1f; }
public override Guid GetGuid(int ordinal) { return Guid.Empty; }
public override short GetInt16(int ordinal) { return 1; }
public override int GetInt32(int ordinal) { return 1; }
public override long GetInt64(int ordinal) { return 1L; }
public override string GetName(int ordinal) { return "Value"; }
public override int GetOrdinal(string name) { return 0; }
public override string GetString(int ordinal) { return "1"; }
public override object GetValue(int ordinal) { return 1; }
public override int GetValues(object[] values) { values[0] = 1; return 1; }
public override bool IsDBNull(int ordinal) { return false; }
public override bool NextResult() { return false; }
public override bool Read() { return false; }
public override void Close()
{
if (!_closed)
{
_closed = true;
_state.ExitExecution();
}
}
protected override void Dispose(bool disposing)
{
Close();
base.Dispose(disposing);
}
}