Skip to content

Add BLite.NetStandard21.Tests: coverage for netstandard2.1 conditional code paths#9

Merged
mrdevrobot merged 2 commits intomainfrom
copilot/add-blite-netstandard21-tests
Mar 4, 2026
Merged

Add BLite.NetStandard21.Tests: coverage for netstandard2.1 conditional code paths#9
mrdevrobot merged 2 commits intomainfrom
copilot/add-blite-netstandard21-tests

Conversation

Copy link
Contributor

Copilot AI commented Mar 2, 2026

BLite.Core and BLite.Bson multi-target net10.0;netstandard2.1 with numerous #if branches, but the only test project targets net10.0 exclusively — leaving every #else path untested.

New project

tests/BLite.NetStandard21.Tests/ — targets netstandard2.1 only, compiles against the netstandard2.1 builds of BLite.Core and BLite.Bson (no BLite.Shared, which is net10.0-only).

InternalsVisibleTo

Added BLite.NetStandard21.Tests to BLite.Core.csproj to expose internal types (PageFile, StorageEngine, SlottedPageHeader, TimeSeriesPage, PriorityQueue polyfill).

Test coverage — one file per conditional path

File Branch exercised
PageFileNetStandardTests ReadPageAsync #else: Seek + SemaphoreSlim lock
StorageEngineNetStandardTests ReadPageAsync WAL fast paths returning default instead of ValueTask.CompletedTask
ObjectIdNetStandardTests NewObjectId() random via new Random(), ToString() via BitConverter.ToString(...).Replace("-","")
BsonWriterNetStandardTests WriteDoubleInternal via WriteInt64LittleEndian(BitConverter.DoubleToInt64Bits(v))
PageHeaderNetStandardTests WriteTo via MemoryMarshal.Write(destination, ref copy) on both PageHeader and SlottedPageHeader
TimeSeriesPageNetStandardTests SetLastTimestamp/SetEntryCount via MemoryMarshal.Write(..., ref value)
IndexKeyNetStandardTests ComputeHashCode via foreach (var b in data) hash.Add(b)
PolyfillsNetStandardTests Internal PriorityQueue<TElement,TPriority> min-heap polyfill (guarded #if NETSTANDARD2_1)

All test code uses only netstandard2.1-compatible APIs — no Random.Shared, Convert.ToHexString, RandomAccess, or ValueTask.CompletedTask.

Original prompt

Problema

Le librerie di produzione (BLite, BLite.Core, BLite.Bson) fanno multi-targeting su net10.0;netstandard2.1, ma l'unico progetto di test esistente — BLite.Tests — target solo net10.0. Questo significa che nessun test viene mai eseguito contro il runtime netstandard2.1, nonostante esistano numerosi path di codice condizionali (#if NETSTANDARD2_1, #if NET6_0_OR_GREATER, #if NET5_0_OR_GREATER, #if NET8_0_OR_GREATER, ecc.) che attivano implementazioni alternative.

Obiettivo

Creare un nuovo progetto di test dedicato tests/BLite.NetStandard21.Tests/ che:

  1. Target esclusivamente netstandard2.1
  2. Copra solo i path critici specifici di quel runtime (non duplicare i test già coperti da BLite.Tests)
  3. Sia eseguibile con dotnet test -f netstandard2.1 (tramite un runtime compatibile, es. .NET 5/6/8/9)

Path critici da coprire (tutti con codice condizionale nel sorgente)

1. PageFile.ReadPageAsync — path #else (Seek + SemaphoreSlim)

File: src/BLite.Core/Storage/PageFile.cs

Su net10.0 usa RandomAccess.ReadAsync (lock-free). Su netstandard2.1 usa il path #else:

// netstandard2.1: FileStream.Seek + ReadAsync — not lock-free, serialize under _lock.
await _lock.WaitAsync(cancellationToken).ConfigureAwait(false);
_fileStream.Seek(offset, SeekOrigin.Begin);
bytesRead = await _fileStream.ReadAsync(slice, cancellationToken).ConfigureAwait(false);

Test da scrivere:

  • ReadPageAsync_SingleRead_ReturnsCorrectData — scrive una pagina, la rilegge async e verifica i dati
  • ReadPageAsync_ConcurrentReads_DoNotDeadlock — 20 reader concorrenti sulla stessa pagina, verifica che non ci sia deadlock o corruzione (il SemaphoreSlim serializza, non deve bloccarsi)
  • ReadPageAsync_WithCancellation_ThrowsOperationCanceledException — cancellation token già cancellato deve lanciare OperationCanceledException

2. StorageEngine.ReadPageAsyncValueTask.CompletedTask polyfill

File: src/BLite.Core/Storage/StorageEngine.Pages.cs

Su netstandard2.1 non esiste ValueTask.CompletedTask, quindi usa return default;:

#if NET5_0_OR_GREATER
    return ValueTask.CompletedTask;
#else
    return default; // netstandard2.1
#endif

Test da scrivere:

  • ReadPageAsync_FromWalCache_ReturnsCorrectly_OnNetStandard — scrive in WAL cache e legge async; il ValueTask restituito dal path default deve essere awaitable correttamente
  • ReadPageAsync_FromWalIndex_ReturnsCorrectly_OnNetStandard — stessa verifica per il path WAL index

3. ObjectId.NewObjectId() — generazione random

File: src/BLite.Bson/ObjectId.cs

Su net6.0+ usa Random.Shared.NextInt64(). Su netstandard2.1:

var random = (long)((ulong)new Random().Next() << 32 | (ulong)(uint)new Random().Next());

Test da scrivere:

  • ObjectId_NewObjectId_IsUnique — genera 1000 ObjectId e verifica che siano tutti distinti
  • ObjectId_NewObjectId_HasCurrentTimestamp — verifica che il timestamp dell'ObjectId sia vicino a DateTimeOffset.UtcNow
  • ObjectId_ToString_ReturnsHexString — verifica il path #else di ToString() (usa BitConverter.ToString + Replace("-", "") invece di Convert.ToHexString)

4. ObjectId.ToString()Convert.ToHexString polyfill

File: src/BLite.Bson/ObjectId.cs

#if NET5_0_OR_GREATER
    return Convert.ToHexString(bytes).ToLowerInvariant();
#else
    return BitConverter.ToString(bytes.ToArray()).Replace("-", "").ToLowerInvariant();
#endif

Test da scrivere:

  • ObjectId_RoundTrip_ToString_Parse — serializza un ObjectId a stringa e verifica che il formato sia 24 caratteri hex lowercase
  • ObjectId_KnownValue_ToString_MatchesExpected — crea un ObjectId con bytes noti e verifica che ToString() produca il valore hex atteso

5. BsonBufferWriter.WriteDoubleInternalBinaryPrimitives.WriteDoubleLittleEndian

File: src/BLite.Bson/BsonBufferWriter.cs

Su netstandard2.1 usa:

BinaryPrimitives.WriteInt64LittleEndian(span, BitConverter.DoubleToInt64Bits(value));

Test da scrivere:

  • BsonWriter_WriteDouble_RoundTrip — scrive un double nel BSON e lo rilegge, verificando identità di valore
  • BsonWriter_WriteDouble_SpecialValues — testa double.NaN, double.PositiveInfinity, double.NegativeInfinity, double.MaxValue, double.MinValue

6. PageHeader.WriteTo e SlottedPageHeader.WriteToMemoryMarshal.Write con ref vs in

File: src/BLite.Core/Storage/PageHeader.cs, src/BLite.Core/Storage/SlottedPage.cs

Su netstandard2.1 usa ref copy invece di in this:

#if NET5_0_OR_GREATER
    MemoryMarshal.Write(destination, in this);
#else
    var copy = this;
    MemoryMarshal.Write(destination, ref copy);
#endif

Test da scrivere:

  • PageHeader_WriteTo_ReadFrom_RoundTrip — crea un PageHeader con valori noti, lo scrive su uno span e lo rilegge con ReadFrom, verificando ogni campo
  • `SlottedPageHeader_WriteTo_ReadFrom...

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…code path coverage

Co-authored-by: mrdevrobot <12503462+mrdevrobot@users.noreply.github.com>
Copilot AI changed the title [WIP] Add new test project for netstandard2.1 support Add BLite.NetStandard21.Tests: coverage for netstandard2.1 conditional code paths Mar 2, 2026
@mrdevrobot mrdevrobot requested a review from Copilot March 4, 2026 20:25
@mrdevrobot mrdevrobot marked this pull request as ready for review March 4, 2026 20:27
@mrdevrobot mrdevrobot review requested due to automatic review settings March 4, 2026 20:28
@mrdevrobot mrdevrobot merged commit d30c8ff into main Mar 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants