1
0
mirror of https://github.com/oliverbooth/X10D synced 2024-11-22 19:48:46 +00:00

Merge pull request #68 from oliverbooth/develop

3.2.0 update
This commit is contained in:
Oliver Booth 2023-04-03 17:51:01 +01:00 committed by GitHub
commit 7ff391a52e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
325 changed files with 27930 additions and 1586 deletions

View File

@ -211,3 +211,4 @@ dotnet_diagnostic.SA1633.severity = silent
# https://github.com/JosefPihrt/Roslynator/blob/master/docs/analyzers/RCS1090.md # https://github.com/JosefPihrt/Roslynator/blob/master/docs/analyzers/RCS1090.md
dotnet_diagnostic.CA2007.severity = silent dotnet_diagnostic.CA2007.severity = silent
dotnet_diagnostic.RCS1090.severity = silent dotnet_diagnostic.RCS1090.severity = silent
dotnet_diagnostic.CA1805.severity = none

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Publish Documentation name: Publish Documentation
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
- uses: nikeee/docfx-action@v1.0.0 - uses: nikeee/docfx-action@v1.0.0
name: Build Documentation name: Build Documentation
with: with:

View File

@ -16,9 +16,12 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: |
3.1.x
6.0.x
7.0.x
- name: Add NuGet source - name: Add NuGet source
run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json" run: dotnet nuget add source --username oliverbooth --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/oliverbooth/index.json"
@ -29,5 +32,11 @@ jobs:
- name: Build - name: Build
run: dotnet build --no-restore --configuration Release run: dotnet build --no-restore --configuration Release
- name: Test - name: Test .NET Core 3.1
run: dotnet test --no-build --verbosity normal --configuration Release run: dotnet test --no-build --verbosity normal --configuration Release --framework netcoreapp3.1
- name: Test .NET 6
run: dotnet test --no-build --verbosity normal --configuration Release --framework net6.0
- name: Test .NET 7
run: dotnet test --no-build --verbosity normal --configuration Release --framework net7.0

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: 7.0.x
@ -26,16 +26,63 @@ jobs:
run: dotnet restore run: dotnet restore
- name: Build - name: Build
run: dotnet build -c Debug run: dotnet build --configuration Debug --no-restore -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }}
- name: Build NuGet package - name: Build NuGet package
run: | run: |
mkdir build mkdir build
dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D --configuration Debug --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D.DSharpPlus --configuration Debug --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Hosting --configuration Debug --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Unity --configuration Debug --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }}
- name: Push NuGet Package to GitHub - name: Push NuGet Package to GitHub
run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
- name: Push NuGet Package to nuget.org - name: Push NuGet Package to nuget.org
run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: build/
- name: Checkout upm branch
uses: actions/checkout@v3
with:
ref: upm
path: upm
- name: Build package.json
run: |
dotnet run --project ./X10D.UpmPackageGenerator/X10D.UpmPackageGenerator.csproj "./X10D/bin/Debug/netstandard2.1/X10D.dll"
cp package.json upm/package.json
- name: Copy built artifacts to upm
run: |
cd upm
cp ../X10D/bin/Debug/netstandard2.1/X10D.dll ./X10D.dll
cp ../X10D/bin/Debug/netstandard2.1/X10D.xml ./X10D.xml
cp ../X10D.Unity/bin/Debug/netstandard2.1/X10D.Unity.dll ./X10D.Unity.dll
cp ../X10D.Unity/bin/Debug/netstandard2.1/X10D.Unity.xml ./X10D.Unity.xml
- name: Check for changes
run: |
cd upm
git diff --quiet
continue-on-error: true
- name: Commit update
if: ${{ success() }}
run: |
cd upm
git config user.name github-actions
git config user.email github-actions@github.com
git add X10D.dll
git add X10D.Unity.dll
git add X10D.xml
git add X10D.Unity.xml
git add package.json
git commit -m "Update upm branch ($GITHUB_SHA)"
git push

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: 7.0.x
@ -26,13 +26,15 @@ jobs:
run: dotnet restore run: dotnet restore
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build --configuration Release --no-restore -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }}
- name: Build NuGet package - name: Build NuGet package
run: | run: |
mkdir build mkdir build
dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D.DSharpPlus --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Hosting --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }}
dotnet pack X10D.Unity --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }}
- name: Push NuGet Package to GitHub - name: Push NuGet Package to GitHub
run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
@ -40,8 +42,53 @@ jobs:
- name: Push NuGet Package to nuget.org - name: Push NuGet Package to nuget.org
run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: build/
- name: Create Release - name: Create Release
uses: "marvinpinto/action-automatic-releases@latest" uses: "marvinpinto/action-automatic-releases@latest"
with: with:
repo_token: "${{ secrets.GITHUB_TOKEN }}" repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: true prerelease: true
- name: Checkout upm branch
uses: actions/checkout@v3
with:
ref: upm
path: upm
- name: Build package.json
run: |
dotnet run --project ./X10D.UpmPackageGenerator/X10D.UpmPackageGenerator.csproj "./X10D/bin/Release/netstandard2.1/X10D.dll"
cp package.json upm/package.json
- name: Copy built artifacts to upm
run: |
cd upm
cp ../X10D/bin/Release/netstandard2.1/X10D.dll ./X10D.dll
cp ../X10D/bin/Release/netstandard2.1/X10D.xml ./X10D.xml
cp ../X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.dll ./X10D.Unity.dll
cp ../X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.xml ./X10D.Unity.xml
- name: Check for changes
run: |
cd upm
git diff --quiet
continue-on-error: true
- name: Commit update
if: ${{ success() }}
run: |
cd upm
git config user.name github-actions
git config user.email github-actions@github.com
git add X10D.dll
git add X10D.Unity.dll
git add X10D.xml
git add X10D.Unity.xml
git add package.json
git commit -m "Update upm branch ($GITHUB_SHA)"
git push

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: 7.0.x
@ -26,13 +26,15 @@ jobs:
run: dotnet restore run: dotnet restore
- name: Build - name: Build
run: dotnet build -c Release run: dotnet build --configuration Release --no-restore
- name: Build NuGet package - name: Build NuGet package
run: | run: |
mkdir build mkdir build
dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build dotnet pack X10D --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build
dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build dotnet pack X10D.DSharpPlus --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build
dotnet pack X10D.Hosting --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build
dotnet pack X10D.Unity --configuration Release --no-build -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build
- name: Push NuGet Package to GitHub - name: Push NuGet Package to GitHub
run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate run: dotnet nuget push "build/*" --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
@ -40,8 +42,53 @@ jobs:
- name: Push NuGet Package to nuget.org - name: Push NuGet Package to nuget.org
run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate run: dotnet nuget push "build/*" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: build/
- name: Create Release - name: Create Release
uses: "marvinpinto/action-automatic-releases@latest" uses: "marvinpinto/action-automatic-releases@latest"
with: with:
repo_token: "${{ secrets.GITHUB_TOKEN }}" repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false prerelease: false
- name: Checkout upm branch
uses: actions/checkout@v3
with:
ref: upm
path: upm
- name: Build package.json
run: |
dotnet run --project ./X10D.UpmPackageGenerator/X10D.UpmPackageGenerator.csproj "./X10D/bin/Release/netstandard2.1/X10D.dll"
cp package.json upm/package.json
- name: Copy built artifacts to upm
run: |
cd upm
cp ../X10D/bin/Release/netstandard2.1/X10D.dll ./X10D.dll
cp ../X10D/bin/Release/netstandard2.1/X10D.xml ./X10D.xml
cp ../X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.dll ./X10D.Unity.dll
cp ../X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.xml ./X10D.Unity.xml
- name: Check for changes
run: |
cd upm
git diff --quiet
continue-on-error: true
- name: Commit update
if: ${{ success() }}
run: |
cd upm
git config user.name github-actions
git config user.email github-actions@github.com
git add X10D.dll
git add X10D.Unity.dll
git add X10D.xml
git add X10D.Unity.xml
git add package.json
git commit -m "Update upm branch ($GITHUB_SHA)"
git push

View File

@ -14,7 +14,7 @@ jobs:
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 1.11 java-version: 1.11
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Cache SonarCloud packages - name: Cache SonarCloud packages

View File

@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: 7.0.x

View File

@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x dotnet-version: 7.0.x
@ -40,7 +40,7 @@ jobs:
cp -r ./X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.dll ./X10D.Unity.Tests/Assets/Libraries/X10D.Unity.dll cp -r ./X10D.Unity/bin/Release/netstandard2.1/X10D.Unity.dll ./X10D.Unity.Tests/Assets/Libraries/X10D.Unity.dll
- name: Unity - Test runner - name: Unity - Test runner
uses: game-ci/unity-test-runner@v2.0.2 uses: game-ci/unity-test-runner@v2.1.0
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}

View File

@ -1,205 +1,392 @@
# Changelog # Changelog
## [3.1.0] All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 3.2.0 - [Unreleased]
### Added ### Added
- Added new library X10D.DSharpPlus.
- Added new library X10D.Hosting.
- Added .NET 7 target.
- X10D: Added `IntrinsicExtensions` and `IntrinsicUtility` which offer methods for vectors in `System.Runtime.Instrinsics`. (#70)
- X10D: Added `MathUtility.Bias(float, float)` and `MathUtility.Bias(double, double)`.
- X10D: Added `MathUtility.ExponentialDecay(float, float, float)` and `MathUtility.ExponentialDecay(double, double, double)`.
- X10D: Added `MathUtility.InverseLerp(float, float, float)` and `MathUtility.InverseLerp(double, double, double)`.
- X10D: Added `MathUtility.Pulse(float, float, float)` and `MathUtility.Pulse(double, double, double)`.
- X10D: Added `MathUtility.Sawtooth(float)` and `MathUtility.Sawtooth(double)`.
- X10D: Added `MathUtility.ScaleRange(float, float, float, float, float)`
and `MathUtility.ScaleRange(double, double, double, double, double)`
- X10D: Added `MathUtility.Sigmoid(float)` and `MathUtility.Sigmoid(double)`.
- X10D: Added `MathUtility.SmoothStep(float, float, float)` and `MathUtility.SmoothStep(double, double, double)`.
- X10D: Added `Circle`, `CircleF`, `Cuboid`, `Ellipse`, `EllipseF`, `Line3D`, `Line`, `LineF`, `Polygon`, `PolygonF`, `Polyhedron`,
and `Sphere`, to complement System.Drawing structs such as `Point` and `Rectangle`.
- X10D: Added `Color.Deconstruct()` - with optional alpha parameter.
- X10D: Added `Color.GetClosestConsoleColor()`.
- X10D: Added `DateTime.GetIso8601WeekOfYear()` and `DateTimeOffset.GetIso8601WeekOfYear()`.
- X10D: Added `DirectoryInfo.Clear()`.
- X10D: Added `double.LinearToGamma([gamma])` and `float.LinearToGamma([gamma])`. (#60)
- X10D: Added `double.GammaToLinear([gamma])` and `float.GammaToLinear([gamma])`. (#60)
- X10D: Added `GreatestCommonFactor` for built-in integer types.
- X10D: Added `IEnumerable<T>.ConcatOne(T)`.
- X10D: Added `IEnumerable<T>.CountWhereNot(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.FirstWhereNot(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.FirstWhereNotOrDefault(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.LastWhereNot(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.LastWhereNotOrDefault(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.MinMax()` and `IEnumerable<T>.MinMaxBy()`. (#72)
- X10D: Added `IEnumerable<T>.WhereNot(Func<T, bool>)`.
- X10D: Added `IEnumerable<T>.WhereNotNull()`.
- X10D: Added `IEnumerable<string>.Grep(string[, bool])`.
- X10D: Added `IList<T>.RemoveRange(Range)`.
- X10D: Added `IList<T>.Swap(IList<T>)`. (#62)
- X10D: Added `IReadOnlyList<T>.IndexOf(T[, int[, int]])`.
- X10D: Added `IReadOnlyList<T>.Slice(int[, int]])`.
- X10D: Added `LowestCommonMultiple` for built-in integer types.
- X10D: Added `Wrap(T[, T])` for built-in numeric types. (#60)
- X10D: Added `Nullable<T>.TryGetValue(out T)`. (#61)
- X10D: Added `Point.IsOnLine(LineF)`, `Point.IsOnLine(PointF, PointF)`, and `Point.IsOnLine(Vector2, Vector2)`.
- X10D: Added `PointF.IsOnLine(LineF)`, `PointF.IsOnLine(PointF, PointF)`, and `PointF.IsOnLine(Vector2, Vector2)`.
- X10D: Added `Point.ToSize()`.
- X10D: Added `Point.ToSizeF()`.
- X10D: Added `Point.ToVector2()`.
- X10D: Added `PointF.Round([float])`.
- X10D: Added `PointF.ToSizeF()`.
- X10D: Added `PointF.ToVector2()` for .NET < 6.
- X10D: Added `PopCount()` for built-in integer types.
- X10D: Added `Quaternion.ToAxisAngle(out float, out float)`.
- X10D: Added `Quaternion.ToVector3()`.
- X10D: Added `Random.NextFrom(Span<T>)` and `Random.NextFrom(ReadOnlySpan<T>)`.
- X10D: Added `ReadOnlySpan<char>.CountSubstring(char)`.
- X10D: Added `ReadOnlySpan<char>.CountSubstring(ReadOnlySpan<char>[, StringComparison])`.
- X10D: Added `ReadOnlySpan<char>.ToTimeSpan()`.
- X10D: Added `ReadOnlySpan<T>.Split(T)`.
- X10D: Added `ReadOnlySpan<T>.Split(ReadOnlySpan<T>)`.
- X10D: Added `RoundUpToPowerOf2()` for built-in integer types.
- X10D: Added `Saturate()` for built-in floating-point types.
- X10D: Added `Size.ToPoint()`.
- X10D: Added `Size.ToPointF()`.
- X10D: Added `Size.ToVector2()`.
- X10D: Added `Span<char>.CountSubstring(char)`.
- X10D: Added `Span<char>.CountSubstring(Span<char>[, StringComparison])`.
- X10D: Added `Span<T>.Split(T)`.
- X10D: Added `Span<T>.Split(Span<T>)`.
- X10D: Added `string.CountSubstring(char)`.
- X10D: Added `string.CountSubstring(string[, StringComparison])`.
- X10D: Added `string.EnsureStartsWith()` and `string.EnsureEndsWith()`.
- X10D: Added `string.IsEmpty()`.
- X10D: Added `string.IsWhiteSpace()`.
- X10D: Added `string.IsNullOrEmpty()`.
- X10D: Added `string.IsNullOrWhiteSpace()`.
- X10D: Added `TextReader.EnumerateLines()` and `TextReader.EnumerateLinesAsync()`.
- X10D: Added `TimeSpan.TryParse(ReadOnlySpan<char>, out TimeSpan)`.
- X10D: Added `Quaternion.Multiply(Vector3)` - this functions as an equivalent to Unity's `Quaternion * Vector3` operator.
- X10D: Added `Vector2.Deconstruct()`.
- X10D: Added `Vector2.IsOnLine(LineF)`, `Vector2.IsOnLine(PointF, PointF)`, and `Vector2.IsOnLine(Vector2, Vector2)`.
- X10D: Added `Vector2.Round([float])`.
- X10D: Added `Vector2.ToPointF()`.
- X10D: Added `Vector2.ToSizeF()`.
- X10D: Added `Vector3.Deconstruct()`.
- X10D: Added `Vector3.Round([float])`.
- X10D: Added `Vector4.Deconstruct()`.
- X10D: Added `Vector4.Round([float])`.
- X10D: `ComplexSqrt` is now exposed for all frameworks.
- X10D.Unity: Added `DebugUtility`, which mimics `UnityEngine.Debug` while offering more useful primitive drawing methods.
- X10D.Unity: Added `System.Drawing.Color.ToUnityColor()`.
- X10D.Unity: Added `System.Drawing.Color.ToUnityColor32()`.
- X10D.Unity: Added `Color.Deconstruct()` - with optional alpha parameter.
- X10D.Unity: Added `Color.GetClosestConsoleColor()`.
- X10D.Unity: Added `Color.ToSystemDrawingColor()`.
- X10D.Unity: Added `Color32.Deconstruct()` - with optional alpha parameter.
- X10D.Unity: Added `Color32.GetClosestConsoleColor()`.
- X10D.Unity: Added `Color32.ToSystemDrawingColor()`.
- X10D.Unity: Added `Point.ToUnityVector2()`.
- X10D.Unity: Added `Point.ToUnityVector2Int()`.
- X10D.Unity: Added `PointF.ToUnityVector2()`.
- X10D.Unity: Added `Rect.ToSystemRectangleF()`.
- X10D.Unity: Added `RectInt.ToSystemRectangle()`.
- X10D.Unity: Added `RectInt.ToSystemRectangleF()`.
- X10D.Unity: Added `Rectangle.ToUnityRect()`.
- X10D.Unity: Added `Rectangle.ToUnityRectInt()`.
- X10D.Unity: Added `RectangleF.ToUnityRect()`.
- X10D.Unity: Added `Size.ToUnityVector2()`.
- X10D.Unity: Added `Size.ToUnityVector2Int()`.
- X10D.Unity: Added `SizeF.ToUnityVector2()`.
- X10D.Unity: Added `Vector2.Deconstruct()`.
- X10D.Unity: Added `Vector2.IsOnLine(LineF)`, `Vector2.IsOnLine(PointF, PointF)`, and `Vector2.IsOnLine(Vector2, Vector2)`.
- X10D.Unity: Added `Vector2Int.IsOnLine(LineF)`, `Vector2Int.IsOnLine(PointF, PointF)`, `Vector2Int.IsOnLine(Vector2, Vector2)`,
and `Vector2Int.IsOnLine(Vector2Int, Vector2Int)`.
- X10D.Unity: Added `Vector2.Round([float])`.
- X10D.Unity: Added `Vector2.ToSystemPointF()`.
- X10D.Unity: Added `Vector2.ToSystemSizeF()`.
- X10D.Unity: Added `Vector2Int.Deconstruct()`.
- X10D.Unity: Added `Vector2Int.ToSystemPoint()`.
- X10D.Unity: Added `Vector2Int.ToSystemSize()`.
- X10D.Unity: Added `Vector2Int.ToSystemVector()`.
- X10D.Unity: Added `Vector2Int.WithX()`.
- X10D.Unity: Added `Vector2Int.WithY()`.
- X10D.Unity: Added `Vector3.Deconstruct()`.
- X10D.Unity: Added `Vector3.Round([float])`.
- X10D.Unity: Added `Vector3Int.Deconstruct()`.
- X10D.Unity: Added `Vector3Int.ToSystemVector()`.
- X10D.Unity: Added `Vector3Int.WithX()`.
- X10D.Unity: Added `Vector3Int.WithY()`.
- X10D.Unity: Added `Vector3Int.WithZ()`.
- X10D.Unity: Added `Vector4.Deconstruct()`.
- X10D.Unity: Added `Vector4.Round([float])`
- X10D.Unity: Added `WaitForFrames` yield instruction.
- X10D.Unity: Added `WaitForKeyDown` yield instruction.
- X10D.Unity: Added `WaitForKeyUp` yield instruction.
- X10D.Unity: Added `WaitForSecondsNoAlloc` yield instruction.
- X10D.Unity: Added `WaitForSecondsRealtimeNoAlloc` yield instruction.
- X10D.Unity: Added `WaitForTimeSpanNoAlloc` yield instruction.
- X10D.Unity: Added `WaitForTimeSpanRealtimeNoAlloc` yield instruction.
### Fixed
- X10D: `T[].Clear` will now correctly clear the specified range of elements by
using the [GetOffsetAndLength](https://learn.microsoft.com/en-us/dotnet/api/system.range.getoffsetandlength?view=net-7.0)
method.
- X10D: `Stream.ReadSingle(Endianness)` now returns a float instead of a double.
- X10D: Fixed `Stream.ReadDouble(Endianness)`, `Stream.ReadSingle(Endianness)`, `Stream.WriteDouble(double, Endianness)`,
`Stream.WriteSingle(float, Endianness)` writing and reading the wrong endianness.
### Changed
- X10D: Performance for integer `Unpack` has been dramatically improved using vector parallelization. (#70)
- X10D: Performance for `MemberInfo.HasCustomAttribute` has been improved. (#70)
- X10D: Performance for `Rune.Repeat` has been improved. (#70)
- X10D: `TimeSpanParser.TryParse` now accepts a nullable string, and returns false if this input is null or empty.
- X10D.Unity: `Singleton<T>` now caches instance where possible.
- X10D.Unity: Conversions between equivalent structures in `System.Numerics` and `UnityEngine` now make use of "unsafe"
reinterpretation, rather than constructing new instances. (#70)
## [3.1.0] - 2022-05-13
### Added
- Reintroduced Unity support - Reintroduced Unity support
- X10D: Added `Color.Inverted()` (#54) - X10D: Added `Color.Inverted()`. (#54)
- X10D: Added `Color.WithA()` (#55) - X10D: Added `Color.WithA()`. (#55)
- X10D: Added `Color.WithB()` (#55) - X10D: Added `Color.WithB()`. (#55)
- X10D: Added `Color.WithG()` (#55) - X10D: Added `Color.WithG()`. (#55)
- X10D: Added `Color.WithR()` (#55) - X10D: Added `Color.WithR()`. (#55)
- X10D: Added `ICollection<T>.ClearAndDisposeAll()` - X10D: Added `ICollection<T>.ClearAndDisposeAll()`.
- X10D: Added `ICollection<T>.ClearAndDisposeAllAsync()` - X10D: Added `ICollection<T>.ClearAndDisposeAllAsync()`.
- X10D: Added `IEnumerable<T>.For()` (#50) - X10D: Added `IEnumerable<T>.For()`. (#50)
- X10D: Added `IEnumerable<T>.ForEach()` (#50) - X10D: Added `IEnumerable<T>.ForEach()`. (#50)
- X10D: Added `IEnumerable<T>.DisposeAll()` - X10D: Added `IEnumerable<T>.DisposeAll()`.
- X10D: Added `IEnumerable<T>.DisposeAllAsync()` - X10D: Added `IEnumerable<T>.DisposeAllAsync()`.
- X10D: Added `char.IsEmoji` - X10D: Added `char.IsEmoji`.
- X10D: Added `ReadOnlySpan<T>.Count(Predicate<T>)` - X10D: Added `ReadOnlySpan<T>.Count(Predicate<T>)`.
- X10D: Added `Rune.IsEmoji` - X10D: Added `Rune.IsEmoji`.
- X10D: Added `Span<T>.Count(Predicate<T>)` - X10D: Added `Span<T>.Count(Predicate<T>)`.
- X10D: Added `string.IsEmoji` - X10D: Added `string.IsEmoji`.
- X10D: Added `Vector2.WithX()` (#56) - X10D: Added `Vector2.WithX()`. (#56)
- X10D: Added `Vector2.WithY()` (#56) - X10D: Added `Vector2.WithY()`. (#56)
- X10D: Added `Vector3.WithX()` (#56) - X10D: Added `Vector3.WithX()`. (#56)
- X10D: Added `Vector3.WithY()` (#56) - X10D: Added `Vector3.WithY()`. (#56)
- X10D: Added `Vector3.WithZ()` (#56) - X10D: Added `Vector3.WithZ()`. (#56)
- X10D: Added `Vector4.WithX()` (#56) - X10D: Added `Vector4.WithX()`. (#56)
- X10D: Added `Vector4.WithY()` (#56) - X10D: Added `Vector4.WithY()`. (#56)
- X10D: Added `Vector4.WithZ()` (#56) - X10D: Added `Vector4.WithZ()`. (#56)
- X10D: Added `Vector4.WithW()` (#56) - X10D: Added `Vector4.WithW()`. (#56)
- X10D.Unity: Added `Singleton<T>` class - X10D.Unity: Added `Singleton<T>` class.
- X10D.Unity: Added `Color.Inverted()` (#54) - X10D.Unity: Added `Color.Inverted()`. (#54)
- X10D.Unity: Added `Color.WithA()` (#55) - X10D.Unity: Added `Color.WithA()`. (#55)
- X10D.Unity: Added `Color.WithB()` (#55) - X10D.Unity: Added `Color.WithB()`. (#55)
- X10D.Unity: Added `Color.WithG()` (#55) - X10D.Unity: Added `Color.WithG()`. (#55)
- X10D.Unity: Added `Color.WithR()` (#55) - X10D.Unity: Added `Color.WithR()`. (#55)
- X10D.Unity: Added `Color32.Inverted()` (#54) - X10D.Unity: Added `Color32.Inverted()`. (#54)
- X10D.Unity: Added `Color32.WithA()` (#55) - X10D.Unity: Added `Color32.WithA()`. (#55)
- X10D.Unity: Added `Color32.WithB()` (#55) - X10D.Unity: Added `Color32.WithB()`. (#55)
- X10D.Unity: Added `Color32.WithG()` (#55) - X10D.Unity: Added `Color32.WithG()`. (#55)
- X10D.Unity: Added `Color32.WithR()` (#55) - X10D.Unity: Added `Color32.WithR()`. (#55)
- X10D.Unity: Added `Component.GetComponentsInChildrenOnly<T>()` - X10D.Unity: Added `Component.GetComponentsInChildrenOnly<T>()`.
- X10D.Unity: Added `GameObject.GetComponentsInChildrenOnly<T>()` - X10D.Unity: Added `GameObject.GetComponentsInChildrenOnly<T>()`.
- X10D.Unity: Added `GameObject.LookAt(GameObject[, Vector3])` - X10D.Unity: Added `GameObject.LookAt(GameObject[, Vector3])`.
- X10D.Unity: Added `GameObject.LookAt(Transform[, Vector3])` - X10D.Unity: Added `GameObject.LookAt(Transform[, Vector3])`.
- X10D.Unity: Added `GameObject.LookAt(Vector3[, Vector3])` - X10D.Unity: Added `GameObject.LookAt(Vector3[, Vector3])`.
- X10D.Unity: Added `GameObject.SetLayerRecursively(int)` (#57) - X10D.Unity: Added `GameObject.SetLayerRecursively(int)`. (#57)
- X10D.Unity: Added `GameObject.SetParent(GameObject[, bool])` - X10D.Unity: Added `GameObject.SetParent(GameObject[, bool])`.
- X10D.Unity: Added `GameObject.SetParent(Transform[, bool])` - X10D.Unity: Added `GameObject.SetParent(Transform[, bool])`.
- X10D.Unity: Added `System.Numerics.Quaternion.ToUnityQuaternion()` - X10D.Unity: Added `System.Numerics.Quaternion.ToUnityQuaternion()`.
- X10D.Unity: Added `Quaternion.ToSystemQuaternion()` - X10D.Unity: Added `Quaternion.ToSystemQuaternion()`.
- X10D.Unity: Added `Random.NextColorArgb()` - X10D.Unity: Added `Random.NextColorArgb()`.
- X10D.Unity: Added `Random.NextColor32Argb()` - X10D.Unity: Added `Random.NextColor32Argb()`.
- X10D.Unity: Added `Random.NextColorRgb()` - X10D.Unity: Added `Random.NextColorRgb()`.
- X10D.Unity: Added `Random.NextColor32Rgb()` - X10D.Unity: Added `Random.NextColor32Rgb()`.
- X10D.Unity: Added `Random.NextRotation()` - X10D.Unity: Added `Random.NextRotation()`.
- X10D.Unity: Added `Random.NextRotationUniform()` - X10D.Unity: Added `Random.NextRotationUniform()`.
- X10D.Unity: Added `Random.NextUnitVector2()` - X10D.Unity: Added `Random.NextUnitVector2()`.
- X10D.Unity: Added `Random.NextUnitVector3()` - X10D.Unity: Added `Random.NextUnitVector3()`.
- X10D.Unity: Added `Transform.LookAt(GameObject[, Vector3])` - X10D.Unity: Added `Transform.LookAt(GameObject[, Vector3])`.
- X10D.Unity: Added `Transform.SetParent(GameObject[, bool])` - X10D.Unity: Added `Transform.SetParent(GameObject[, bool])`.
- X10D.Unity: Added `System.Numerics.Vector2.ToUnityVector()` - X10D.Unity: Added `System.Numerics.Vector2.ToUnityVector()`.
- X10D.Unity: Added `System.Numerics.Vector3.ToUnityVector()` - X10D.Unity: Added `System.Numerics.Vector3.ToUnityVector()`.
- X10D.Unity: Added `System.Numerics.Vector4.ToUnityVector()` - X10D.Unity: Added `System.Numerics.Vector4.ToUnityVector()`.
- X10D.Unity: Added `Vector2.ToSystemVector()` - X10D.Unity: Added `Vector2.ToSystemVector()`.
- X10D.Unity: Added `Vector3.ToSystemVector()` - X10D.Unity: Added `Vector3.ToSystemVector()`.
- X10D.Unity: Added `Vector4.ToSystemVector()` - X10D.Unity: Added `Vector4.ToSystemVector()`.
- X10D.Unity: Added `Vector2.WithX()` (#56) - X10D.Unity: Added `Vector2.WithX()`. (#56)
- X10D.Unity: Added `Vector2.WithY()` (#56) - X10D.Unity: Added `Vector2.WithY()`. (#56)
- X10D.Unity: Added `Vector3.WithX()` (#56) - X10D.Unity: Added `Vector3.WithX()`. (#56)
- X10D.Unity: Added `Vector3.WithY()` (#56) - X10D.Unity: Added `Vector3.WithY()`. (#56)
- X10D.Unity: Added `Vector3.WithZ()` (#56) - X10D.Unity: Added `Vector3.WithZ()`. (#56)
- X10D.Unity: Added `Vector4.WithX()` (#56) - X10D.Unity: Added `Vector4.WithX()`. (#56)
- X10D.Unity: Added `Vector4.WithY()` (#56) - X10D.Unity: Added `Vector4.WithY()`. (#56)
- X10D.Unity: Added `Vector4.WithZ()` (#56) - X10D.Unity: Added `Vector4.WithZ()`. (#56)
- X10D.Unity: Added `Vector4.WithW()` (#56) - X10D.Unity: Added `Vector4.WithW()`. (#56)
## [3.0.0] ## [3.0.0] - 2022-04-30
In the midst of writing these release notes, I may have missed some important changes. If you notice an API change that is not documented here, In the midst of writing these release notes, I may have missed some important changes. If you notice an API change that is not
documented here,
please [open an issue](https://github.com/oliverbooth/X10D/issues)! please [open an issue](https://github.com/oliverbooth/X10D/issues)!
### Added ### Added
- Added `T.AsArrayValue()`
- Added `T.AsEnumerableValue()` - Added `T.AsArrayValue()`.
- Added `T.RepeatValue(int)` - Added `T.AsEnumerableValue()`.
- Added `T.ToJson([JsonSerializerOptions])` - Added `T.RepeatValue(int)`.
- Added `string.FromJson([JsonSerializerOptions])` - Added `T.ToJson([JsonSerializerOptions])`.
- Added `T[].AsReadOnly()` - Added `string.FromJson([JsonSerializerOptions])`.
- Added `T[].Clear([Range])` and `T[].Clear(int, int)` - Added `T[].AsReadOnly()`.
- Added `DateTime.IsLeapYear()` - Added `T[].Clear([Range])` and `T[].Clear(int, int)`.
- Added `DateTimeOffset.IsLeapYear()` - Added `DateTime.IsLeapYear()`.
- Added `Endianness` enum - Added `DateTimeOffset.IsLeapYear()`.
- Added `FileInfo.GetHash<T>()` - Added `Endianness` enum.
- Added `FileInfo.TryWriteHash<T>(Span<byte>, out int)` - Added `FileInfo.GetHash<T>()`.
- Added `IComparable<T>.Clamp(T, T)` - this supersedes `Clamp` defined for hard-coded numeric types (#24) - Added `FileInfo.TryWriteHash<T>(Span<byte>, out int)`.
- Added `IComparable<T1>.GreaterThan(T2)` (#22) - Added `IComparable<T>.Clamp(T, T)` - this supersedes `Clamp` defined for hard-coded numeric types. (#24)
- Added `IComparable<T1>.GreaterThanOrEqualTo(T2)` (#22) - Added `IComparable<T1>.GreaterThan(T2)`. (#22)
- Added `IComparable<T1>.LessThan(T2)` (#22) - Added `IComparable<T1>.GreaterThanOrEqualTo(T2)`. (#22)
- Added `IComparable<T1>.LessThanOrEqualTo(T2)` (#22) - Added `IComparable<T1>.LessThan(T2)`. (#22)
- Added `IComparable<T1>.Max(T)` (#23) - Added `IComparable<T1>.LessThanOrEqualTo(T2)`. (#22)
- Added `IComparable<T1>.Min(T)` (#23) - Added `IComparable<T1>.Max(T)`. (#23)
- Added `IComparable<T1>.Min(T)`. (#23)
- Added `IDictionary<TKey, TValue>.AddOrUpdate()` - Added `IDictionary<TKey, TValue>.AddOrUpdate()`
- Added `IEnumerable<TSource>.Product()` and `IEnumerable<TSource>.Product<TResult>(Func<TSource>, TResult)` for all built-in numeric types, computing the product of all (optionally transformed) elements - Added `IEnumerable<TSource>.Product()` and `IEnumerable<TSource>.Product<TResult>(Func<TSource>, TResult)` for all built-in
- Added `IList<T>.Fill(T)` and `IList<T>.Fill(T, int, int)` numeric types, computing the product of all (optionally transformed) elements.
- Added `IPAddress.IsIPv4()` and `IPAddress.IsIPv6()` - Added `IList<T>.Fill(T)` and `IList<T>.Fill(T, int, int)`.
- Added `IReadOnlyList<byte>.Pack8Bit()` - Added `IPAddress.IsIPv4()` and `IPAddress.IsIPv6()`.
- Added `IReadOnlyList<byte>.Pack16Bit()` - Added `IReadOnlyList<byte>.Pack8Bit()`.
- Added `IReadOnlyList<byte>.Pack32Bit()` - Added `IReadOnlyList<byte>.Pack16Bit()`.
- Added `IReadOnlyList<byte>.Pack64Bit()` - Added `IReadOnlyList<byte>.Pack32Bit()`.
- Added `MemberInfo.HasCustomAttribute<T>()` and `MemberInfo.HasCustomAttribute(Type)` - Added `IReadOnlyList<byte>.Pack64Bit()`.
- Added `defaultValue` overload for `MemberInfo.SelectFromCustomAttribute<TAttr, TReturn>()` - Added `MemberInfo.HasCustomAttribute<T>()` and `MemberInfo.HasCustomAttribute(Type)`.
- Added `Random.Next<T>()` (returns a random field from a specified enum type) - Added `defaultValue` overload for `MemberInfo.SelectFromCustomAttribute<TAttr, TReturn>()`.
- Added `Random.NextByte([byte[, byte]])` - Added `Random.Next<T>()` (returns a random field from a specified enum type).
- Added `Random.NextColorArgb()`, returning a random color in RGB space, including random alpha - Added `Random.NextByte([byte[, byte]])`.
- Added `Random.NextColorRgb()`, returning a random color in RGB space with alpha 255 - Added `Random.NextColorArgb()`, returning a random color in RGB space, including random alpha.
- Added `Random.NextDouble(double[, double])` - Added `Random.NextColorRgb()`, returning a random color in RGB space with alpha 255.
- Added `Random.NextInt16([short[, short]])` - Added `Random.NextDouble(double[, double])`.
- Added `Random.NextSingle(float[, float])` (#34) - Added `Random.NextInt16([short[, short]])`.
- Added `Random.NextUnitVector2()` - Added `Random.NextSingle(float[, float])`. (#34)
- Added `Random.NextUnitVector3()` - Added `Random.NextUnitVector2()`.
- Added `Random.NextRotation()` - Added `Random.NextUnitVector3()`.
- Added `Rune.Repeat(int)` - Added `Random.NextRotation()`.
- Added `byte.IsEven()` - Added `Rune.Repeat(int)`.
- Added `byte.IsOdd()` - Added `byte.IsEven()`.
- Added `byte.IsPrime()` - Added `byte.IsOdd()`.
- Added `byte.UnpackBits()` - Added `byte.IsPrime()`.
- Added `byte.RangeTo(byte)`, `byte.RangeTo(short)`, `byte.RangeTo(int)`, and `byte.RangeTo(long)` - Added `byte.UnpackBits()`.
- Added `int.Mod(int)` - Added `byte.RangeTo(byte)`, `byte.RangeTo(short)`, `byte.RangeTo(int)`, and `byte.RangeTo(long)`.
- Added `int.RangeTo(int)`, and `int.RangeTo(long)` - Added `int.Mod(int)`.
- Added `int.UnpackBits()` - Added `int.RangeTo(int)`, and `int.RangeTo(long)`.
- Added `long.Mod(long)` - Added `int.UnpackBits()`.
- Added `long.RangeTo(long)` - Added `long.Mod(long)`.
- Added `long.UnpackBits()` - Added `long.RangeTo(long)`.
- Added `sbyte.IsEven()` - Added `long.UnpackBits()`.
- Added `sbyte.IsOdd()` - Added `sbyte.IsEven()`.
- Added `sbyte.IsPrime()` - Added `sbyte.IsOdd()`.
- Added `sbyte.Mod(sbyte)` - Added `sbyte.IsPrime()`.
- Added `short.IsEven()` - Added `sbyte.Mod(sbyte)`.
- Added `short.IsOdd()` - Added `short.IsEven()`.
- Added `short.Mod(short)` - Added `short.IsOdd()`.
- Added `short.RangeTo(short)`, `short.RangeTo(int)`, and `short.RangeTo(long)` - Added `short.Mod(short)`.
- Added `short.UnpackBits()` - Added `short.RangeTo(short)`, `short.RangeTo(int)`, and `short.RangeTo(long)`.
- Added `string.IsPalindrome()` - Added `short.UnpackBits()`.
- Added `Stream.GetHash<T>()` - Added `string.IsPalindrome()`.
- Added `Stream.TryWriteHash<T>(Span<byte>, out int)` - Added `Stream.GetHash<T>()`.
- Added `Stream.ReadInt16([Endian])` - Added `Stream.TryWriteHash<T>(Span<byte>, out int)`.
- Added `Stream.ReadInt32([Endian])` - Added `Stream.ReadInt16([Endian])`.
- Added `Stream.ReadInt64([Endian])` - Added `Stream.ReadInt32([Endian])`.
- Added `Stream.ReadUInt16([Endian])` - Added `Stream.ReadInt64([Endian])`.
- Added `Stream.ReadUInt32([Endian])` - Added `Stream.ReadUInt16([Endian])`.
- Added `Stream.ReadUInt64([Endian])` - Added `Stream.ReadUInt32([Endian])`.
- Added `Stream.Write(short, [Endian])` - Added `Stream.ReadUInt64([Endian])`.
- Added `Stream.Write(int, [Endian])` - Added `Stream.Write(short, [Endian])`.
- Added `Stream.Write(long, [Endian])` - Added `Stream.Write(int, [Endian])`.
- Added `Stream.Write(ushort, [Endian])` - Added `Stream.Write(long, [Endian])`.
- Added `Stream.Write(uint, [Endian])` - Added `Stream.Write(ushort, [Endian])`.
- Added `Stream.Write(ulong, [Endian])` - Added `Stream.Write(uint, [Endian])`.
- Added `TimeSpan.Ago()` - Added `Stream.Write(ulong, [Endian])`.
- Added `TimeSpan.FromNow()` - Added `TimeSpan.Ago()`.
- Added `TimeSpanParser.TryParse` which supersedes `TimeSpanParser.Parse` - Added `TimeSpan.FromNow()`.
- Added `Sqrt()` and `ComplexSqrt()` for all built-in decimal types - Added `TimeSpanParser.TryParse` which supersedes `TimeSpanParser.Parse`.
- Added `All()` and `Any()` for `Span<T>` and `ReadOnlySpan<T>`, mimicking the corresponding methods in `System.Linq` - Added `Sqrt()` and `ComplexSqrt()` for all built-in decimal types.
- Added `Sign()` for built-in numeric types. For unsigned types, this never returns -1 - Added `All()` and `Any()` for `Span<T>` and `ReadOnlySpan<T>`, mimicking the corresponding methods in `System.Linq`.
- Added `Type.Implements<T>()` and `Type.Implements(Type)` (#25) - Added `Sign()` for built-in numeric types. For unsigned types, this never returns -1.
- Added `Type.Inherits<T>()` and `Type.Inherits(Type)` (#25) - Added `Type.Implements<T>()` and `Type.Implements(Type)`. (#25)
- Added `DigitalRoot` function for built-in integer types - Added `Type.Inherits<T>()` and `Type.Inherits(Type)`. (#25)
- Added `Factorial` function for built-in integer types - Added `DigitalRoot` function for built-in integer types.
- Added `HostToNetworkOrder` and `NetworkToHostOrder` for `short`, `int`, and `long` - Added `Factorial` function for built-in integer types.
- Added `IsLeapYear` function for `DateTime` and `DateTimeOffset`, as well as built-in numeric types - Added `HostToNetworkOrder` and `NetworkToHostOrder` for `short`, `int`, and `long`.
- Added `MultiplicativePersistence` function for built-in integer types - Added `IsLeapYear` function for `DateTime` and `DateTimeOffset`, as well as built-in numeric types.
- Added `RotateLeft` and `RotateRight` for built-in integer types - Added `MultiplicativePersistence` function for built-in integer types.
- Added trigonometric functions for built-in numeric types, including `Acos()`, `Asin()`, `Atan()`, `Atan2()`, `Cos()`, `Sin()`, `Tan()` (#49) - Added `RotateLeft` and `RotateRight` for built-in integer types.
- Added time-related extension methods for built-in numeric types, including `Milliseconds()`, `Seconds()`, `Minutes()`, `Hours()`, `Days()`, and `Weeks()`. `Ticks()` is also available, but only for integers; not floating point - Added trigonometric functions for built-in numeric types,
- Added `StringBuilderReader` (inheriting `TextReader`) which allows reading from a `StringBuilder` without consuming the underlying string including `Acos()`, `Asin()`, `Atan()`, `Atan2()`, `Cos()`, `Sin()`, `Tan()` (#49)
- Added extension methods for `System.Decimal` - Added time-related extension methods for built-in numeric types,
including `Milliseconds()`, `Seconds()`, `Minutes()`, `Hours()`, `Days()`, and `Weeks()`. `Ticks()` is also available, but only
for integers; not floating point.
- Added `StringBuilderReader` (inheriting `TextReader`) which allows reading from a `StringBuilder` without consuming the
underlying string.
- Added extension methods for `System.Decimal`.
### Changed ### Changed
- Updated to .NET 6 (#45)
- Methods defined to accept `byte[]` have been changed accept `IReadOnlyList<byte>` - Updated to .NET 6. (#45)
- Extension methods are now defined in appropriate child namespaces to reduce the risk of name collisions (#7) - Methods defined to accept `byte[]` have been changed accept `IReadOnlyList<byte>`.
- `char[].Random(int)`, `char[].Random(int, int)`, `IEnumerable<char>.Random(int)`, and `IEnumerable<char>.Random(int, int)` have been redefined as `Random.NextString(IReadOnlyList<char>, int)` - Extension methods are now defined in appropriate child namespaces to reduce the risk of name collisions. (#7)
- `IComparable<T>.Between(T, T)` has been redefined as `IComparable<T1>.Between(T2, T3, [InclusiveOptions])` to allow comparison of disparate yet comparable types, and also offers inclusivity options - `char[].Random(int)`, `char[].Random(int, int)`, `IEnumerable<char>.Random(int)`, and `IEnumerable<char>.Random(int, int)` have
- `DateTime` extensions now wrap `DateTimeOffset` extensions been redefined as `Random.NextString(IReadOnlyList<char>, int)`.
- `DateTime.ToUnixTimestamp([bool])` has been redefined as `DateTime.ToUnixTimeMilliseconds()` and `DateTime.ToUnixTimeSeconds()` - `IComparable<T>.Between(T, T)` has been redefined as `IComparable<T1>.Between(T2, T3, [InclusiveOptions])` to allow comparison
- `Dictionary<T1, T2>.ToGetParameters()`, `IDictionary<T1, T2>.ToGetParameters()`, and `IReadOnlyDictionary<T1, T2>.ToGetParameters()`, has been redefined as `IEnumerable<KeyValuePair<T1, T2>>.ToGetParameters()` of disparate yet comparable types, and also offers inclusivity options.
- `Dictionary<T1, T2>.ToConnectionString()`, `IDictionary<T1, T2>.ToConnectionString()`, and `IReadOnlyDictionary<T1, T2>.ToConnectionString()`, has been redefined as `IEnumerable<KeyValuePair<T1, T2>>.ToConnectionString()` - `DateTime` extensions now wrap `DateTimeOffset` extensions.
- `IList<T>.OneOf([Random])` and `IEnumerable<T>.OneOf([Random])` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` to fall in line with the naming convention of `System.Random` (#21) - `DateTime.ToUnixTimestamp([bool])` has been redefined as `DateTime.ToUnixTimeMilliseconds()` and `DateTime.ToUnixTimeSeconds()`.
- `IList<T>.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation - `Dictionary<T1, T2>.ToGetParameters()`, `IDictionary<T1, T2>.ToGetParameters()`,
- `IList<T>.Shuffle([Random])` has been repurposed to shuffle the list in place, rather than returning a new enumerable and `IReadOnlyDictionary<T1, T2>.ToGetParameters()`, has been redefined as `IEnumerable<KeyValuePair<T1, T2>>.ToGetParameters()`.
- `IEnumerable<T>.Shuffle([Random])` has been renamed to `IEnumerable<T>.Shuffled([Random])` to avoid confusion with `IList<T>.Shuffle([Random])` - `Dictionary<T1, T2>.ToConnectionString()`, `IDictionary<T1, T2>.ToConnectionString()`,
- `Random.CoinToss()` has been redefined as `Random.NextBoolean()` to fall in line with the naming convention of `System.Random` and `IReadOnlyDictionary<T1, T2>.ToConnectionString()`, has been redefined
- `Random.OneOf<T>(T[])` and `Random.OneOf<T>(IList<T>)` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` to fall in line with the naming convention of `System.Random` as `IEnumerable<KeyValuePair<T1, T2>>.ToConnectionString()`.
- `Enum.Next([bool])` and `Enum.Previous([bool])` have been redefined as `Enum.Next()`, `Enum.Previous()`, `Enum.NextUnchecked()`, `Enum.PreviousUnchecked()`. The `Unchecked` variants of these methods do not perform index validation, and will throw `IndexOutOfRangeException` when attempting to access an invalid index. The checked variants will perform a modulo to wrap the index - `IList<T>.OneOf([Random])` and `IEnumerable<T>.OneOf([Random])` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` to
- Seperated `string.WithAlternative(string, [bool])` to `string.WithEmptyAlternative(string)` and `string.WithWhiteSpaceAlternative(string)` fall in line with the naming convention of `System.Random`. (#21)
- `string.AsNullIfEmpty()` and `string.AsNullIfWhiteSpace()` now accept a nullable `string` - `IList<T>.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation.
- `IList<T>.Shuffle([Random])` has been repurposed to shuffle the list in place, rather than returning a new enumerable.
- `IEnumerable<T>.Shuffle([Random])` has been renamed to `IEnumerable<T>.Shuffled([Random])` to avoid confusion
with `IList<T>.Shuffle([Random])`.
- `Random.CoinToss()` has been redefined as `Random.NextBoolean()` to fall in line with the naming convention of `System.Random`.
- `Random.OneOf<T>(T[])` and `Random.OneOf<T>(IList<T>)` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` to fall in
line with the naming convention of `System.Random`.
- `Enum.Next([bool])` and `Enum.Previous([bool])` have been redefined.
as `Enum.Next()`, `Enum.Previous()`, `Enum.NextUnchecked()`, `Enum.PreviousUnchecked()`. The `Unchecked` variants of these
methods do not perform index validation, and will throw `IndexOutOfRangeException` when attempting to access an invalid index.
The checked variants will perform a modulo to wrap the index.
- Seperated `string.WithAlternative(string, [bool])` to `string.WithEmptyAlternative(string)`
and `string.WithWhiteSpaceAlternative(string)`.
- `string.AsNullIfEmpty()` and `string.AsNullIfWhiteSpace()` now accept a nullable `string`.
- `IEnumerable<byte>.GetString([Encoding])` has been renamed to `IReadOnlyList<byte>.ToString` and its `Encoding` parameter has - `IEnumerable<byte>.GetString([Encoding])` has been renamed to `IReadOnlyList<byte>.ToString` and its `Encoding` parameter has
been made non-optional been made non-optional.
- Fixed a bug where `IEnumerable<KeyValuePair<K,V>>.ToConnectionString()` would not sanitize types with custom `ToString()` - Fixed a bug where `IEnumerable<KeyValuePair<K,V>>.ToConnectionString()` would not sanitize types with custom `ToString()`
implementation (#20) implementation. (#20)
Renamed `string.Random(int[, Random])` to `string.Randomize(int[, Random])` Renamed `string.Random(int[, Random])` to `string.Randomize(int[, Random])`.
- Redefined `char[].Random(int)`, `char[].Random(int, Random)`, `IEnumerable<char>.Random(int)`, and `IEnumerable<char>.Random(int, Random)`, as `Random.NextString(IReadOnlyList<char>, int)` - Redefined `char[].Random(int)`, `char[].Random(int, Random)`, `IEnumerable<char>.Random(int)`,
and `IEnumerable<char>.Random(int, Random)`, as `Random.NextString(IReadOnlyList<char>, int)`.
- Improved performance for: - Improved performance for:
- `string.IsLower()` - `string.IsLower()`
- `string.IsUpper()` - `string.IsUpper()`
@ -207,46 +394,52 @@ please [open an issue](https://github.com/oliverbooth/X10D/issues)!
- `TimeSpanParser` - `TimeSpanParser`
### Removed ### Removed
- Indefinitely suspended Unity support, until such a time that Unity can be updated to a newer version of .NET. See: https://forum.unity.com/threads/unity-future-net-development-status.1092205/
- Removed `bool.And(bool)` - Indefinitely suspended Unity support, until such a time that Unity can be updated to a newer version of .NET.
- Removed `bool.NAnd(bool)` See: https://forum.unity.com/threads/unity-future-net-development-status.1092205/
- Removed `bool.NOr(bool)` - Removed `bool.And(bool)`.
- Removed `bool.Not(bool)` - Removed `bool.NAnd(bool)`.
- Removed `bool.Or(bool)` - Removed `bool.NOr(bool)`.
- Removed `bool.ToByte()` - Removed `bool.Not(bool)`.
- Removed `bool.ToInt16()` - Removed `bool.Or(bool)`.
- Removed `bool.ToInt64()` - Removed `bool.ToByte()`.
- Removed `bool.XNOr()` - Removed `bool.ToInt16()`.
- Removed `bool.XOr()` - Removed `bool.ToInt64()`.
- Removed `IConvertible.To<T>([IFormatProvider])` (#13) - Removed `bool.XNOr()`.
- Removed `IConvertible.ToOrDefault<T>([IFormatProvider])` (#13) - Removed `bool.XOr()`.
- Removed `IConvertible.ToOrDefault<T>(out T, [IFormatProvider])` (#13) - Removed `IConvertible.To<T>([IFormatProvider])`. (#13)
- Removed `IConvertible.ToOrNull<T>([IFormatProvider])` (#13) - Removed `IConvertible.ToOrDefault<T>([IFormatProvider])`. (#13)
- Removed `IConvertible.ToOrNull<T>(out T, [IFormatProvider])` (#13) - Removed `IConvertible.ToOrDefault<T>(out T, [IFormatProvider])`. (#13)
- Removed `IConvertible.ToOrOther<T>(T, [IFormatProvider])` (#13) - Removed `IConvertible.ToOrNull<T>([IFormatProvider])`. (#13)
- Removed `IConvertible.ToOrOther<T>(out T, T, [IFormatProvider])` (#13) - Removed `IConvertible.ToOrNull<T>(out T, [IFormatProvider])`. (#13)
- Removed `IEnumerable<T>.Split(int)` - this functionality is now provided by .NET in the form of the `Chunk` method. See: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.chunk?view=net-6.0 - Removed `IConvertible.ToOrOther<T>(T, [IFormatProvider])`. (#13)
- Removed `MemberInfo.GetDefaultValue()` (use `SelectFromCustomAttribute()` instead) - Removed `IConvertible.ToOrOther<T>(out T, T, [IFormatProvider])`. (#13)
- Removed `MemberInfo.GetDescription()` (use `SelectFromCustomAttribute()` instead) - Removed `IEnumerable<T>.Split(int)` - this functionality is now provided by .NET in the form of the `Chunk` method.
- Removed `int.ToBoolean()` See: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.chunk?view=net-6.0.
- Removed `long.ToBoolean()` - Removed `MemberInfo.GetDefaultValue()` (use `SelectFromCustomAttribute()` instead).
- Removed `short.ToBoolean()` - Removed `MemberInfo.GetDescription()` (use `SelectFromCustomAttribute()` instead).
- Removed `uint.ToBoolean()` - Removed `int.ToBoolean()`.
- Removed `ushort.ToBoolean()` - Removed `long.ToBoolean()`.
- Removed `ulong.ToBoolean()` - Removed `short.ToBoolean()`.
- Removed `WaitHandle.WaitOneAsync()`. For suspensions of execution during asynchronous operations, favour `TaskCompletionSource` or `TaskCompletionSource<T>`. See: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource?view=net-6.0 and https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=net-6.0 - Removed `uint.ToBoolean()`.
- Removed `ushort.ToBoolean()`.
- Removed `ulong.ToBoolean()`.
- Removed `WaitHandle.WaitOneAsync()`. For suspensions of execution during asynchronous operations, favour `TaskCompletionSource`
or `TaskCompletionSource<T>`.
See: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource?view=net-6.0
and https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=net-6.0
## [2.6.0] - 2020-10-20 ## [2.6.0] - 2020-10-20
### Added ### Added
- Add `string.AsNullIfEmpty()` - Add `string.AsNullIfEmpty()`.
- Returns the current string, or `null` if the current string is null or empty. - Returns the current string, or `null` if the current string is null or empty.
- Add `string.AsNullIfWhiteSpace()` - Add `string.AsNullIfWhiteSpace()`.
- Returns the current string, or `null` if the current string is null, empty, or consists of only whitespace. - Returns the current string, or `null` if the current string is null, empty, or consists of only whitespace.
- Add `string.Reverse()` - Add `string.Reverse()`.
- Reverses the current string - Reverses the current string.
- Add `string.WithAlternative()` - Add `string.WithAlternative()`.
- Returns the current string, or an alternative value if the current string is null or empty, or optionally if the current - Returns the current string, or an alternative value if the current string is null or empty, or optionally if the current
string consists of only whitespace. string consists of only whitespace.
@ -262,30 +455,30 @@ please [open an issue](https://github.com/oliverbooth/X10D/issues)!
### Added ### Added
- `WaitHandle.WaitOneAsync()` - `WaitHandle.WaitOneAsync()`.
- Wraps `WaitHandle.WaitOne` as a `Task` - Wraps `WaitHandle.WaitOne` as a `Task`.
- Add support for Unity 2019.4.3f1 - Add support for Unity 2019.4.3f1.
- Add `GameObject.LookAt(GameObject)` - Add `GameObject.LookAt(GameObject)`.
- Rotates the Transform on the current GameObject so that it faces the Transform on another GameObject - Rotates the Transform on the current GameObject so that it faces the Transform on another GameObject.
- Add `GameObject.LookAt(Transform)` - Add `GameObject.LookAt(Transform)`.
- Rotates the Transform on the current GameObject so that it faces another transform - Rotates the Transform on the current GameObject so that it faces another transform.
- Add `Transform.LookAt(GameObject)` - Add `Transform.LookAt(GameObject)`
- Rotates the current Transform so that it faces the Transform on another GameObject - Rotates the current Transform so that it faces the Transform on another GameObject.
- Add `Vector3.Round([float])` - Add `Vector3.Round([float])`.
- Returns a rounded Vector3 by calling `float.Round()` on each component - Returns a rounded Vector3 by calling `float.Round()` on each component.
- Add `Vector3.WithX(float)` - Add `Vector3.WithX(float)`.
- Returns a Vector3 with a new X component value - Returns a Vector3 with a new X component value.
- Add `Vector3.WithY(float)` - Add `Vector3.WithY(float)`.
- Returns a Vector3 with a new Y component value - Returns a Vector3 with a new Y component value.
- Add `Vector3.WithZ(float)` - Add `Vector3.WithZ(float)`.
- Returns a Vector3 with a new Z component value - Returns a Vector3 with a new Z component value.
- Add `Vector3.WithXY(float, float)` - Add `Vector3.WithXY(float, float)`.
- Returns a Vector3 with new X and Y component values - Returns a Vector3 with new X and Y component values.
- Add `Vector3.WithXZ(float, float)` - Add `Vector3.WithXZ(float, float)`.
- Returns a Vector3 with new X and Z component values - Returns a Vector3 with new X and Z component values.
- Add `Vector3.WithYZ(float, float)` - Add `Vector3.WithYZ(float, float)`.
- Returns a Vector3 with new Y and Z component values - Returns a Vector3 with new Y and Z component values.
- Add `BetterBehavior` (experimental wrapper over `MonoBehaviour`) - Add `BetterBehavior` (experimental wrapper over `MonoBehaviour`).
### Changed ### Changed
@ -299,18 +492,18 @@ please [open an issue](https://github.com/oliverbooth/X10D/issues)!
### Added ### Added
- Add `string.ChangeEncoding(Encoding, Encoding)` - Add `string.ChangeEncoding(Encoding, Encoding)`.
- Converts this string from one encoding to another - Converts this string from one encoding to another.
- Add `string.IsLower()` - Add `string.IsLower()`.
- Determines if all alpha characters in this string are considered lowercase - Determines if all alpha characters in this string are considered lowercase.
- Add `string.IsUpper()` - Add `string.IsUpper()`.
- Determines if all alpha characters in this string are considered uppercase - Determines if all alpha characters in this string are considered uppercase.
- Various extension methods with regards to reflection: - Various extension methods with regards to reflection:
- `GetDefaultValue` and `GetDefaultValue<T>` - gets the value stored in the member's `DefaultValue` attribute - `GetDefaultValue` and `GetDefaultValue<T>` - gets the value stored in the member's `DefaultValue` attribute.
- `GetDescription`- gets the value stored in the member's `Description` attribute - `GetDescription`- gets the value stored in the member's `Description` attribute.
- `SelectFromCustomAttribute<T1, T2>` - Internally calls `GetCustomAttribute<T1>` and passes it to a `Func<T1, T2>` so that - `SelectFromCustomAttribute<T1, T2>` - Internally calls `GetCustomAttribute<T1>` and passes it to a `Func<T1, T2>` so that
specific members may be selected specific members may be selected.
### Changed ### Changed
@ -324,26 +517,26 @@ please [open an issue](https://github.com/oliverbooth/X10D/issues)!
### Added ### Added
- Add `bool bool.And(bool)` - Add `bool bool.And(bool)`.
- Performs logical AND - Performs logical AND.
- Add `bool bool.Or(bool)` - Add `bool bool.Or(bool)`.
- Performs logical OR - Performs logical OR.
- Add `bool bool.Not(bool)` - Add `bool bool.Not(bool)`.
- Performs logical NOT - Performs logical NOT.
- Add `bool bool.XOr(bool)` - Add `bool bool.XOr(bool)`.
- Performs Logical XOR - Performs Logical XOR.
- Add `bool bool.NAnd(bool)` - Add `bool bool.NAnd(bool)`.
- Performs logical NAND - Performs logical NAND.
- Add `bool bool.NOr(bool)` - Add `bool bool.NOr(bool)`.
- Performs logical NOR - Performs logical NOR.
- Add `bool bool.XNOr(bool)` - Add `bool bool.XNOr(bool)`.
- Performs logical XNOR - Performs logical XNOR.
- Add `byte bool.ToByte()` - Add `byte bool.ToByte()`.
- 1 if `true`, 0 otherwise - 1 if `true`, 0 otherwise.
- Add `short bool.ToInt16()` - Add `short bool.ToInt16()`.
- 1 if `true`, 0 otherwise - 1 if `true`, 0 otherwise.
- Add `long bool.ToInt64()` - Add `long bool.ToInt64()`.
- 1 if `true`, 0 otherwise - 1 if `true`, 0 otherwise.
### Changed ### Changed
@ -357,23 +550,24 @@ please [open an issue](https://github.com/oliverbooth/X10D/issues)!
### Added ### Added
- Add `IEnumerable<T>.Split(int)` - Add `IEnumerable<T>.Split(int)`.
- Performs the same operation as the previously defined `IEnumerable<byte>.Chunkify(int)`, except now accepts any type `T` - Performs the same operation as the previously defined `IEnumerable<byte>.Chunkify(int)`, except now accepts any type `T`.
### Changed ### Changed
- Fix `DateTime.Last(DayOfWeek)` implementation - Fix `DateTime.Last(DayOfWeek)` implementation.
- Now returns the correct date/time for a given day of the week - Now returns the correct date/time for a given day of the week.
### Removed ### Removed
- Remove `IEnumerable<byte>.Chunkify(int)` - Remove `IEnumerable<byte>.Chunkify(int)`.
- Replaced by a method of the same behaviour `IEnumerable<T>.Split(int)` - Replaced by a method of the same behaviour `IEnumerable<T>.Split(int)`.
## Earlier versions ## Earlier versions
### ***Not documented*** Earlier versions of this package are undocumented and unlisted from package results.
[unreleased]: https://github.com/oliverbooth/X10D/compare/v3.1.0...develop
[3.1.0]: https://github.com/oliverbooth/X10D/releases/tag/v3.1.0 [3.1.0]: https://github.com/oliverbooth/X10D/releases/tag/v3.1.0
[3.0.0]: https://github.com/oliverbooth/X10D/releases/tag/v3.0.0 [3.0.0]: https://github.com/oliverbooth/X10D/releases/tag/v3.0.0
[2.6.0]: https://github.com/oliverbooth/X10D/releases/tag/2.6.0 [2.6.0]: https://github.com/oliverbooth/X10D/releases/tag/2.6.0

View File

@ -5,7 +5,7 @@ or submit a pull request.
### Pull request guidelines ### Pull request guidelines
This project uses C# 10.0 language features, and adheres to StyleCop rules with some minor adjustments. This project uses C# 11.0 language features where feasible, and adheres to StyleCop rules with some minor adjustments.
There is an `.editorconfig` included in this repository. For quick and painless pull requests, ensure that the analyzer does not There is an `.editorconfig` included in this repository. For quick and painless pull requests, ensure that the analyzer does not
throw warnings. throw warnings.
@ -13,7 +13,7 @@ throw warnings.
Below are a few pointers to which you may refer, but keep in mind this is not an exhaustive list: Below are a few pointers to which you may refer, but keep in mind this is not an exhaustive list:
- Use C# 10.0 features where possible - Use C# 11.0 features where possible
- Try to ensure code is CLS-compliant. Where this is not possible, decorate methods with `CLSCompliantAttribute` and pass `false` - Try to ensure code is CLS-compliant. Where this is not possible, decorate methods with `CLSCompliantAttribute` and pass `false`
- Follow all .NET guidelines and coding conventions. - Follow all .NET guidelines and coding conventions.
See https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions See https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions
@ -33,11 +33,11 @@ When in doubt, follow .NET guidelines.
### Tests ### Tests
When introducing a new extension method, you must ensure that you have also defined a unit test that asserts its correct behavior. When introducing a new extension method, you must ensure that you have also defined a unit test that asserts its correct behavior.
The code style guidelines and code-analysis rules apply to the `X10D.Tests` equally as much as `X10D`, although documentation may The code style guidelines and code-analysis rules apply to the `X10D.Tests` as much as `X10D`, although documentation may
be briefer. Refer to existing tests as a guideline. be briefer. Refer to existing tests as a guideline.
### Disclaimer ### Disclaimer
In the event of a code style violation, a pull request may left open (or closed entirely) without merging. Keep in mind this does In the event of a code style violation, a pull request may be left open (or closed entirely) without merging. Keep in mind this does
not mean the theory or implementation of the method is inherently bad or rejected entirely (although if this is the case, it will not mean the theory or implementation of the method is inherently bad or rejected entirely (although if this is the case, it will
be outlined) be outlined)

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019-2022 Oliver Booth Copyright (c) 2019-2023 Oliver Booth
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,12 +1,12 @@
<h1 align="center"><img src="https://raw.githubusercontent.com/oliverbooth/X10D/develop/banner.png"></h1> <h1 align="center"><img src="branding_Banner.png"></h1>
<p align="center"> <p align="center">
<a href="https://github.com/oliverbooth/X10D/actions?query=workflow%3A%22.NET+Core%22"><img src="https://img.shields.io/github/workflow/status/oliverbooth/X10D/.NET%20Core" alt="GitHub Workflow Status" title="GitHub Workflow Status"></a> <a href="https://github.com/oliverbooth/X10D/actions/workflows/dotnet.yml"><img src="https://img.shields.io/github/actions/workflow/status/oliverbooth/X10D/dotnet.yml?style=flat-square" alt="GitHub Workflow Status" title="GitHub Workflow Status"></a>
<a href="https://github.com/oliverbooth/X10D/issues"><img src="https://img.shields.io/github/issues/oliverbooth/X10D" alt="GitHub Issues" title="GitHub Issues"></a> <a href="https://github.com/oliverbooth/X10D/issues"><img src="https://img.shields.io/github/issues/oliverbooth/X10D?style=flat-square" alt="GitHub Issues" title="GitHub Issues"></a>
<a href="https://sonarcloud.io/dashboard?id=oliverbooth_X10D"><img src="https://img.shields.io/sonar/coverage/oliverbooth_X10D?server=https%3A%2F%2Fsonarcloud.io" alt="Coverage"></a> <a href="https://sonarcloud.io/dashboard?id=oliverbooth_X10D"><img src="https://img.shields.io/sonar/coverage/oliverbooth_X10D?server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Coverage"></a>
<a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/dt/X10D" alt="NuGet Downloads" title="NuGet Downloads"></a> <a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/dt/X10D?style=flat-square" alt="NuGet Downloads" title="NuGet Downloads"></a>
<a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/v/X10D?label=stable" alt="Stable Version" title="Stable Version"></a> <a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/v/X10D?label=stable&style=flat-square" alt="Stable Version" title="Stable Version"></a>
<a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/vpre/X10D?label=nightly" alt="Nightly Version" title="Nightly Version"></a> <a href="https://www.nuget.org/packages/X10D/"><img src="https://img.shields.io/nuget/vpre/X10D?label=nightly&style=flat-square" alt="Nightly Version" title="Nightly Version"></a>
<a href="https://github.com/oliverbooth/X10D/blob/master/LICENSE.md"><img src="https://img.shields.io/github/license/oliverbooth/X10D" alt="MIT License" title="MIT License"></a> <a href="https://github.com/oliverbooth/X10D/blob/master/LICENSE.md"><img src="https://img.shields.io/github/license/oliverbooth/X10D?style=flat-square" alt="MIT License" title="MIT License"></a>
</p> </p>
### About ### About
@ -17,18 +17,14 @@ X10D (pronounced *extend*), is a .NET package that provides extension methods fo
## Installation ## Installation
### NuGet installation ### NuGet installation
```ps ```ps
Install-Package X10D -Version 3.1.0 Install-Package X10D -Version 3.2.0
``` ```
### Manual installation ### Manual installation
Download the [latest release](https://github.com/oliverbooth/X10D/releases/latest) from this repository and adding a direct assembly reference for your chosen platform. Download the [latest release](https://github.com/oliverbooth/X10D/releases/latest) from this repository and adding a direct assembly reference for your chosen platform.
### Unity installation ### Unity installation
Starting with Unity 2021.2, support for .NET Standard 2.1 has been added. With this change, I am confident providing support for this version for the time being, with only minimal feature-loss. For the Unity installation guide, refer to the [README.md in X10D.Unity](X10D.Unity/README.md).
To add X10D into your Unity project, goto the [Package Manager window](https://docs.unity3d.com/Manual/upm-ui.html), and choose to install from a Git URL, and use the URL https://github.com/oliverbooth/X10D.git#upm
Parity with the main branch of X10D, and full .NET 6 feature support, is planned - but a timeline is not yet available. Unity plan to add .NET 6 support in the near future.
For more information, see [this forum post](https://forum.unity.com/threads/unity-future-net-development-status.1092205/).
## Features ## Features
I'm planning on writing complete and extensive documentation in the near future. As of this time, feel free to browse the source or the API using your favourite IDE. I'm planning on writing complete and extensive documentation in the near future. As of this time, feel free to browse the source or the API using your favourite IDE.
@ -38,4 +34,4 @@ For those familiar with the 2.6.0 API, please read [CHANGELOG.md](CHANGELOG.md)
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
## License ## License
X10D is released under the MIT License. See [here](https://github.com/oliverbooth/X10D/blob/master/LICENSE.md) for more details. X10D is released under the MIT License. See [here](https://github.com/oliverbooth/X10D/blob/main/LICENSE.md) for more details.

View File

@ -0,0 +1,70 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net6.0;netstandard2.1</TargetFrameworks>
<LangVersion>11.0</LangVersion>
<Optimize>true</Optimize>
<ImplicitUsings>true</ImplicitUsings>
<Authors>Oliver Booth</Authors>
<NeutralLanguage>en</NeutralLanguage>
<RepositoryUrl>https://github.com/oliverbooth/X10D</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Description>Extension methods on crack.</Description>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageIcon>branding_Icon.png</PackageIcon>
<PackageIconUrl/>
<PackageTags>dotnet extension-methods</PackageTags>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../CHANGELOG.md"))</PackageReleaseNotes>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<VersionPrefix>3.2.0</VersionPrefix>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ExcludeFromCodeCoverage>true</ExcludeFromCodeCoverage>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' == ''">
<Version>$(VersionPrefix)-$(VersionSuffix)</Version>
<AssemblyVersion>$(VersionPrefix).0</AssemblyVersion>
<FileVersion>$(VersionPrefix).0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">
<Version>$(VersionPrefix)-$(VersionSuffix).$(BuildNumber)</Version>
<AssemblyVersion>$(VersionPrefix).$(BuildNumber)</AssemblyVersion>
<FileVersion>$(VersionPrefix).$(BuildNumber)</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' == ''">
<Version>$(VersionPrefix)</Version>
<AssemblyVersion>$(VersionPrefix).0</AssemblyVersion>
<FileVersion>$(VersionPrefix).0</FileVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DSharpPlus" Version="4.3.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<None Include="..\branding_Icon.png">
<Pack>True</Pack>
<PackagePath/>
</None>
<None Include="..\LICENSE.md">
<Pack>True</Pack>
<PackagePath/>
</None>
<None Include="..\CHANGELOG.md">
<Pack>True</Pack>
<PackagePath/>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
[assembly: CLSCompliant(false)]

View File

@ -0,0 +1,80 @@
using DSharpPlus;
using DSharpPlus.Entities;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordChannel" />.
/// </summary>
public static class DiscordChannelExtensions
{
/// <summary>
/// Gets the category of this channel.
/// </summary>
/// <param name="channel">The channel whose category to retrieve.</param>
/// <returns>
/// The category of <paramref name="channel" />, or <paramref name="channel" /> itself if it is already a category;
/// <see langword="null" /> if this channel is not defined in a category.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="channel" /> is <see langword="null" />.</exception>
public static DiscordChannel? GetCategory(this DiscordChannel channel)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(channel);
#else
if (channel is null)
{
throw new ArgumentNullException(nameof(channel));
}
#endif
while (true)
{
if (channel.IsCategory)
{
return channel;
}
if (channel.Parent is not { } parent)
{
return null;
}
channel = parent;
}
}
/// <summary>
/// Normalizes a <see cref="DiscordChannel" /> so that the internal client is assured to be a specified value.
/// </summary>
/// <param name="channel">The <see cref="DiscordChannel" /> to normalize.</param>
/// <param name="client">The target client.</param>
/// <returns>
/// A <see cref="DiscordChannel" /> whose public values will match <paramref name="channel" />, but whose internal client
/// is <paramref name="client" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="channel" /> is <see langword="null" /></para>
/// -or-
/// <para><paramref name="client" /> is <see langword="null" /></para>
/// </exception>
public static async Task<DiscordChannel> NormalizeClientAsync(this DiscordChannel channel, DiscordClient client)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(channel);
ArgumentNullException.ThrowIfNull(client);
#else
if (channel is null)
{
throw new ArgumentNullException(nameof(channel));
}
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
return await client.GetChannelAsync(channel.Id).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,79 @@
using DSharpPlus;
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordClient" />.
/// </summary>
public static class DiscordClientExtensions
{
/// <summary>
/// Instructs the client to automatically join all existing threads, and any newly-created threads.
/// </summary>
/// <param name="client">The <see cref="DiscordClient" /> whose events should be subscribed.</param>
/// <param name="rejoinIfRemoved">
/// <see langword="true" /> to automatically rejoin a thread if this client was removed; otherwise,
/// <see langword="false" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="client" /> is <see langword="null" />.</exception>
public static void AutoJoinThreads(this DiscordClient client, bool rejoinIfRemoved = true)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(client);
#else
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
client.GuildAvailable += (_, args) => args.Guild.JoinAllThreadsAsync();
client.ThreadCreated += (_, args) => args.Thread.JoinThreadAsync();
if (rejoinIfRemoved)
{
client.ThreadMembersUpdated += (_, args) =>
{
if (args.RemovedMembers.Any(m => m.Id == client.CurrentUser.Id))
return args.Thread.JoinThreadAsync();
return Task.CompletedTask;
};
}
}
/// <summary>
/// Gets a user by their ID. If the user is not found, <see langword="null" /> is returned instead of
/// <see cref="NotFoundException" /> being thrown.
/// </summary>
/// <param name="client">The Discord client.</param>
/// <param name="userId">The ID of the user to retrieve.</param>
/// <exception cref="ArgumentNullException"><paramref name="client" /> is <see langword="null" />.</exception>
public static async Task<DiscordUser?> GetUserOrNullAsync(this DiscordClient client, ulong userId)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(client);
#else
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
try
{
// we should never use exceptions for flow control but this is D#+ we're talking about.
// NotFoundException isn't even documented, and yet it gets thrown when a user doesn't exist.
// so this method should hopefully clearly express that - and at least using exceptions for flow control *here*,
// removes the need to do the same in consumer code.
// god I hate this.
return await client.GetUserAsync(userId).ConfigureAwait(false);
}
catch (NotFoundException)
{
return null;
}
}
}

View File

@ -0,0 +1,239 @@
using DSharpPlus.Entities;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordEmbedBuilder" />.
/// </summary>
public static class DiscordEmbedBuilderExtensions
{
/// <summary>
/// Adds a field of any value type to the embed.
/// </summary>
/// <param name="builder">The <see cref="DiscordEmbedBuilder" /> to modify.</param>
/// <param name="name">The name of the embed field.</param>
/// <param name="value">The value of the embed field.</param>
/// <param name="inline"><see langword="true" /> to display this field inline; otherwise, <see langword="false" />.</param>
/// <typeparam name="T">The type of <paramref name="value" />.</typeparam>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />; that is, <paramref name="builder" />.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder" /> is <see langword="null" />.</exception>
public static DiscordEmbedBuilder AddField<T>(
this DiscordEmbedBuilder builder,
string name,
T? value,
bool inline = false)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
#endif
return builder.AddField(name, value?.ToString(), inline);
}
/// <summary>
/// Conditionally adds a field to the embed.
/// </summary>
/// <param name="builder">The <see cref="DiscordEmbedBuilder" /> to modify.</param>
/// <param name="condition">The condition whose value is used to determine whether the field will be added.</param>
/// <param name="name">The name of the embed field.</param>
/// <param name="value">The value of the embed field.</param>
/// <param name="inline"><see langword="true" /> to display this field inline; otherwise, <see langword="false" />.</param>
/// <typeparam name="T">The type of <paramref name="value" />.</typeparam>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />; that is, <paramref name="builder" />.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder" /> is <see langword="null" />.</exception>
public static DiscordEmbedBuilder AddFieldIf<T>(
this DiscordEmbedBuilder builder,
bool condition,
string name,
T? value,
bool inline = false)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
#endif
if (condition)
{
builder.AddField(name, value?.ToString(), inline);
}
return builder;
}
/// <summary>
/// Conditionally adds a field to the embed.
/// </summary>
/// <param name="builder">The <see cref="DiscordEmbedBuilder" /> to modify.</param>
/// <param name="predicate">The predicate whose return value is used to determine whether the field will be added.</param>
/// <param name="name">The name of the embed field.</param>
/// <param name="value">The value of the embed field.</param>
/// <param name="inline"><see langword="true" /> to display this field inline; otherwise, <see langword="false" />.</param>
/// <typeparam name="T">The type of <paramref name="value" />.</typeparam>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />; that is, <paramref name="builder" />.</returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="builder" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="predicate" /> is <see langword="null" />.</para>
/// </exception>
public static DiscordEmbedBuilder AddFieldIf<T>(
this DiscordEmbedBuilder builder,
Func<bool> predicate,
string name,
T? value,
bool inline = false)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
if (predicate.Invoke())
{
builder.AddField(name, value?.ToString(), inline);
}
return builder;
}
/// <summary>
/// Conditionally adds a field to the embed.
/// </summary>
/// <param name="builder">The <see cref="DiscordEmbedBuilder" /> to modify.</param>
/// <param name="predicate">The predicate whose return value is used to determine whether the field will be added.</param>
/// <param name="name">The name of the embed field.</param>
/// <param name="valueFactory">The delegate whose return value will be used as the value of the embed field.</param>
/// <param name="inline"><see langword="true" /> to display this field inline; otherwise, <see langword="false" />.</param>
/// <typeparam name="T">The return type of <paramref name="valueFactory" />.</typeparam>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />; that is, <paramref name="builder" />.</returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="builder" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="predicate" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="valueFactory" /> is <see langword="null" />.</para>
/// </exception>
public static DiscordEmbedBuilder AddFieldIf<T>(
this DiscordEmbedBuilder builder,
Func<bool> predicate,
string name,
Func<T?> valueFactory,
bool inline = false)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(predicate);
ArgumentNullException.ThrowIfNull(valueFactory);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
if (valueFactory is null)
{
throw new ArgumentNullException(nameof(valueFactory));
}
#endif
if (predicate.Invoke())
{
builder.AddField(name, valueFactory.Invoke()?.ToString(), inline);
}
return builder;
}
/// <summary>
/// Conditionally adds a field to the embed.
/// </summary>
/// <param name="builder">The <see cref="DiscordEmbedBuilder" /> to modify.</param>
/// <param name="condition">The condition whose value is used to determine whether the field will be added.</param>
/// <param name="name">The name of the embed field.</param>
/// <param name="valueFactory">The delegate whose return value will be used as the value of the embed field.</param>
/// <param name="inline"><see langword="true" /> to display this field inline; otherwise, <see langword="false" />.</param>
/// <typeparam name="T">The return type of <paramref name="valueFactory" />.</typeparam>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />; that is, <paramref name="builder" />.</returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="builder" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="valueFactory" /> is <see langword="null" />.</para>
/// </exception>
public static DiscordEmbedBuilder AddFieldIf<T>(
this DiscordEmbedBuilder builder,
bool condition,
string name,
Func<T?> valueFactory,
bool inline = false)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(valueFactory);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
if (valueFactory is null)
{
throw new ArgumentNullException(nameof(valueFactory));
}
#endif
if (condition)
{
builder.AddField(name, valueFactory.Invoke()?.ToString(), inline);
}
return builder;
}
/// <summary>
/// Sets the embed's author.
/// </summary>
/// <param name="builder">The embed builder to modify.</param>
/// <param name="user">The author.</param>
/// <returns>The current instance of <see cref="DiscordEmbedBuilder" />.</returns>
public static DiscordEmbedBuilder WithAuthor(this DiscordEmbedBuilder builder, DiscordUser user)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(user);
#else
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
#endif
return builder.WithAuthor(user.GetUsernameWithDiscriminator(), iconUrl: user.AvatarUrl);
}
}

View File

@ -0,0 +1,97 @@
using DSharpPlus;
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordGuild" />.
/// </summary>
public static class DiscordGuildExtensions
{
/// <summary>
/// Joins all active threads in the guild that this client has permission to view.
/// </summary>
/// <param name="guild">The guild whose active threads to join.</param>
/// <exception cref="ArgumentNullException"><paramref name="guild" /> is <see langword="null" />.</exception>
public static async Task JoinAllThreadsAsync(this DiscordGuild guild)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(guild);
#else
if (guild is null)
{
throw new ArgumentNullException(nameof(guild));
}
#endif
await Task.WhenAll(guild.Threads.Values.Select(t => t.JoinThreadAsync())).ConfigureAwait(false);
}
/// <summary>
/// Gets a guild member by their ID. If the member is not found, <see langword="null" /> is returned instead of
/// <see cref="NotFoundException" /> being thrown.
/// </summary>
/// <param name="guild">The guild whose member list to search.</param>
/// <param name="userId">The ID of the member to retrieve.</param>
/// <exception cref="ArgumentNullException"><paramref name="guild" /> is <see langword="null" />.</exception>
public static async Task<DiscordMember?> GetMemberOrNullAsync(this DiscordGuild guild, ulong userId)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(guild);
#else
if (guild is null)
{
throw new ArgumentNullException(nameof(guild));
}
#endif
try
{
// we should never use exceptions for flow control but this is D#+ we're talking about.
// NotFoundException isn't even documented, and yet it gets thrown when a member doesn't exist.
// so this method should hopefully clearly express that - and at least using exceptions for flow control *here*,
// removes the need to do the same in consumer code.
// god I hate this.
return await guild.GetMemberAsync(userId).ConfigureAwait(false);
}
catch (NotFoundException)
{
return null;
}
}
/// <summary>
/// Normalizes a <see cref="DiscordGuild" /> so that the internal client is assured to be a specified value.
/// </summary>
/// <param name="guild">The <see cref="DiscordGuild" /> to normalize.</param>
/// <param name="client">The target client.</param>
/// <returns>
/// A <see cref="DiscordGuild" /> whose public values will match <paramref name="guild" />, but whose internal client is
/// <paramref name="client" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="guild" /> is <see langword="null" /></para>
/// -or-
/// <para><paramref name="client" /> is <see langword="null" /></para>
/// </exception>
public static async Task<DiscordGuild> NormalizeClientAsync(this DiscordGuild guild, DiscordClient client)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(guild);
ArgumentNullException.ThrowIfNull(client);
#else
if (guild is null)
{
throw new ArgumentNullException(nameof(guild));
}
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
return await client.GetGuildAsync(guild.Id).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,73 @@
using DSharpPlus;
using DSharpPlus.Entities;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordMember" />.
/// </summary>
public static class DiscordMemberExtensions
{
/// <summary>
/// Returns a value indicating whether this member has the specified role.
/// </summary>
/// <param name="member">The member whose roles to search.</param>
/// <param name="role">The role for which to check.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="member" /> has the role; otherwise, <see langword="false" />.
/// </returns>
public static bool HasRole(this DiscordMember member, DiscordRole role)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(member);
ArgumentNullException.ThrowIfNull(role);
#else
if (member is null)
{
throw new ArgumentNullException(nameof(member));
}
if (role is null)
{
throw new ArgumentNullException(nameof(role));
}
#endif
return member.Roles.Contains(role);
}
/// <summary>
/// Normalizes a <see cref="DiscordMember" /> so that the internal client is assured to be a specified value.
/// </summary>
/// <param name="member">The <see cref="DiscordMember" /> to normalize.</param>
/// <param name="client">The target client.</param>
/// <returns>
/// A <see cref="DiscordMember" /> whose public values will match <paramref name="member" />, but whose internal client
/// is <paramref name="client" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="member" /> is <see langword="null" /></para>
/// -or-
/// <para><paramref name="client" /> is <see langword="null" /></para>
/// </exception>
public static async Task<DiscordMember> NormalizeClientAsync(this DiscordMember member, DiscordClient client)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(member);
ArgumentNullException.ThrowIfNull(client);
#else
if (member is null)
{
throw new ArgumentNullException(nameof(member));
}
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
DiscordGuild guild = await member.Guild.NormalizeClientAsync(client).ConfigureAwait(false);
return await guild.GetMemberAsync(member.Id).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,89 @@
using DSharpPlus;
using DSharpPlus.Entities;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordMessage" />.
/// </summary>
public static class DiscordMessageExtensions
{
/// <summary>
/// Deletes this message after a specified delay.
/// </summary>
/// <param name="message">The message to delete.</param>
/// <param name="delay">The delay before deletion.</param>
/// <param name="reason">The reason for the deletion.</param>
/// <exception cref="ArgumentNullException"><paramref name="message" /> is <see langword="null" />.</exception>
public static async Task DeleteAfterAsync(this DiscordMessage message, TimeSpan delay, string? reason = null)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(message);
#else
if (message is null)
{
throw new ArgumentNullException(nameof(message));
}
#endif
await Task.Delay(delay).ConfigureAwait(false);
await message.DeleteAsync(reason).ConfigureAwait(false);
}
/// <summary>
/// Deletes the message as created by this task after a specified delay.
/// </summary>
/// <param name="task">The task whose <see cref="DiscordMessage" /> result should be deleted.</param>
/// <param name="delay">The delay before deletion.</param>
/// <param name="reason">The reason for the deletion.</param>
/// <exception cref="ArgumentNullException"><paramref name="task" /> is <see langword="null" />.</exception>
public static async Task DeleteAfterAsync(this Task<DiscordMessage> task, TimeSpan delay, string? reason = null)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(task);
#else
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}
#endif
DiscordMessage message = await task.ConfigureAwait(false);
await message.DeleteAfterAsync(delay, reason).ConfigureAwait(false);
}
/// <summary>
/// Normalizes a <see cref="DiscordMessage" /> so that the internal client is assured to be a specified value.
/// </summary>
/// <param name="message">The <see cref="DiscordMessage" /> to normalize.</param>
/// <param name="client">The target client.</param>
/// <returns>
/// A <see cref="DiscordMessage" /> whose public values will match <paramref name="message" />, but whose internal client
/// is <paramref name="client" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="message" /> is <see langword="null" /></para>
/// -or-
/// <para><paramref name="client" /> is <see langword="null" /></para>
/// </exception>
public static async Task<DiscordMessage> NormalizeClientAsync(this DiscordMessage message, DiscordClient client)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(message);
ArgumentNullException.ThrowIfNull(client);
#else
if (message is null)
{
throw new ArgumentNullException(nameof(message));
}
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
DiscordChannel channel = await message.Channel.NormalizeClientAsync(client).ConfigureAwait(false);
return await channel.GetMessageAsync(message.Id).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,158 @@
using DSharpPlus;
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;
namespace X10D.DSharpPlus;
/// <summary>
/// Extension methods for <see cref="DiscordUser" />.
/// </summary>
public static class DiscordUserExtensions
{
/// <summary>
/// Returns the current <see cref="DiscordUser" /> as a member of the specified guild.
/// </summary>
/// <param name="user">The user to transform.</param>
/// <param name="guild">The guild whose member list to search.</param>
/// <returns>
/// A <see cref="DiscordMember" /> whose <see cref="DiscordMember.Guild" /> is equal to <paramref name="guild" />, or
/// <see langword="null" /> if this user is not in the specified <paramref name="guild" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="user" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="guild" /> is <see langword="null" />.</para>
/// </exception>
public static async Task<DiscordMember?> GetAsMemberOfAsync(this DiscordUser user, DiscordGuild guild)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(user);
ArgumentNullException.ThrowIfNull(guild);
#else
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
if (guild is null)
{
throw new ArgumentNullException(nameof(guild));
}
#endif
if (user is DiscordMember member && member.Guild == guild)
{
return member;
}
if (guild.Members.TryGetValue(user.Id, out member!))
{
return member;
}
try
{
return await guild.GetMemberAsync(user.Id).ConfigureAwait(false);
}
catch (NotFoundException)
{
return null;
}
}
/// <summary>
/// Returns the user's username with the discriminator, in the format <c>username#discriminator</c>.
/// </summary>
/// <param name="user">The user whose username and discriminator to retrieve.</param>
/// <returns>A string in the format <c>username#discriminator</c></returns>
/// <exception cref="ArgumentNullException"><paramref name="user" /> is <see langword="null" />.</exception>
public static string GetUsernameWithDiscriminator(this DiscordUser user)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(user);
#else
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
#endif
return $"{user.Username}#{user.Discriminator}";
}
/// <summary>
/// Returns a value indicating whether the current user is in the specified guild.
/// </summary>
/// <param name="user">The user to check.</param>
/// <param name="guild">The guild whose member list to search.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="user" /> is a member of <paramref name="guild" />; otherwise,
/// <see langword="false" />.
/// </returns>
public static async Task<bool> IsInGuildAsync(this DiscordUser user, DiscordGuild guild)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(user);
ArgumentNullException.ThrowIfNull(guild);
#else
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
if (guild is null)
{
throw new ArgumentNullException(nameof(guild));
}
#endif
if (guild.Members.TryGetValue(user.Id, out _))
{
return true;
}
try
{
DiscordMember? member = await guild.GetMemberAsync(user.Id).ConfigureAwait(false);
return member is not null;
}
catch (NotFoundException)
{
return false;
}
}
/// <summary>
/// Normalizes a <see cref="DiscordUser" /> so that the internal client is assured to be a specified value.
/// </summary>
/// <param name="user">The <see cref="DiscordUser" /> to normalize.</param>
/// <param name="client">The target client.</param>
/// <returns>
/// A <see cref="DiscordUser" /> whose public values will match <paramref name="user" />, but whose internal client is
/// <paramref name="client" />.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="user" /> is <see langword="null" /></para>
/// -or-
/// <para><paramref name="client" /> is <see langword="null" /></para>
/// </exception>
public static async Task<DiscordUser> NormalizeClientAsync(this DiscordUser user, DiscordClient client)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(user);
ArgumentNullException.ThrowIfNull(client);
#else
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
if (client is null)
{
throw new ArgumentNullException(nameof(client));
}
#endif
return await client.GetUserAsync(user.Id).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,329 @@
using System.Globalization;
namespace X10D.DSharpPlus;
/// <summary>
/// Provides methods for encoding and decoding Discord mention strings.
/// </summary>
/// <remarks>
/// The implementations in this class are designed to resemble <c>MentionUtils</c> as provided by Discord.NET. The source is
/// available
/// <a href="https://github.com/discord-net/Discord.Net/blob/dev/src/Discord.Net.Core/Utils/MentionUtils.cs">
/// here
/// </a>.
/// </remarks>
public static class MentionUtility
{
/// <summary>
/// Returns a channel mention string built from the specified channel ID.
/// </summary>
/// <param name="id">The ID of the channel to mention.</param>
/// <returns>A channel mention string in the format <c>&lt;#123&gt;</c>.</returns>
public static string MentionChannel(decimal id)
{
return $"<#{id:N0}>";
}
/// <summary>
/// Returns a channel mention string built from the specified channel ID.
/// </summary>
/// <param name="id">The ID of the channel to mention.</param>
/// <returns>A channel mention string in the format <c>&lt;#123&gt;</c>.</returns>
[CLSCompliant(false)]
public static string MentionChannel(ulong id)
{
return $"<#{id}>";
}
/// <summary>
/// Returns a role mention string built from the specified channel ID.
/// </summary>
/// <param name="id">The ID of the role to mention.</param>
/// <returns>A role mention string in the format <c>&lt;@&amp;123&gt;</c>.</returns>
public static string MentionRole(decimal id)
{
return $"<@&{id:N0}>";
}
/// <summary>
/// Returns a role mention string built from the specified role ID.
/// </summary>
/// <param name="id">The ID of the role to mention.</param>
/// <returns>A role mention string in the format <c>&lt;@&amp;123&gt;</c>.</returns>
[CLSCompliant(false)]
public static string MentionRole(ulong id)
{
return $"<@&{id}>";
}
/// <summary>
/// Returns a user mention string built from the specified user ID.
/// </summary>
/// <param name="id">The ID of the user to mention.</param>
/// <returns>A user mention string in the format <c>&lt;@123&gt;</c>.</returns>
[CLSCompliant(false)]
public static string MentionUser(decimal id)
{
return MentionUser(id, false);
}
/// <summary>
/// Returns a user mention string built from the specified user ID.
/// </summary>
/// <param name="id">The ID of the user to mention.</param>
/// <param name="nickname">
/// <see langword="true" /> if the mention string should account for nicknames; otherwise, <see langword="false" />.
/// </param>
/// <returns>
/// A user mention string in the format <c>&lt;@!123&gt;</c> if <paramref name="nickname" /> is <see langword="true" />,
/// or in the format <c>&lt;@123&gt;</c> if <paramref name="nickname" /> is <see langword="false" />.
/// </returns>
[CLSCompliant(false)]
public static string MentionUser(decimal id, bool nickname)
{
return nickname ? $"<@!{id:N0}>" : $"<@{id:N0}>";
}
/// <summary>
/// Returns a user mention string built from the specified user ID.
/// </summary>
/// <param name="id">The ID of the user to mention.</param>
/// <returns>A user mention string in the format <c>&lt;@123&gt;</c>.</returns>
[CLSCompliant(false)]
public static string MentionUser(ulong id)
{
return MentionUser(id, false);
}
/// <summary>
/// Returns a user mention string built from the specified user ID.
/// </summary>
/// <param name="id">The ID of the user to mention.</param>
/// <param name="nickname">
/// <see langword="true" /> if the mention string should account for nicknames; otherwise, <see langword="false" />.
/// </param>
/// <returns>
/// A user mention string in the format <c>&lt;@!123&gt;</c> if <paramref name="nickname" /> is <see langword="true" />,
/// or in the format <c>&lt;@123&gt;</c> if <paramref name="nickname" /> is <see langword="false" />.
/// </returns>
[CLSCompliant(false)]
public static string MentionUser(ulong id, bool nickname)
{
return nickname ? $"<@!{id}>" : $"<@{id}>";
}
/// <summary>
/// Parses a provided channel mention string to a decimal value representing the channel ID. A return value indicates
/// whether the parse succeeded.
/// </summary>
/// <param name="value">A string containing a mention string to parse, in the format <c>&lt;#123&gt;</c>.</param>
/// <param name="result">
/// When this method returns, contains the decimal value representing the channel ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
public static bool TryParseChannel(string? value, out decimal result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 3 || value[0] != '<' || value[1] != '#' || value[^1] != '>')
{
return false;
}
value = value.Substring(2, value.Length - 3); // <#123>
if (!ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out ulong actual))
{
return false;
}
result = actual;
return true;
}
/// <summary>
/// Parses a provided channel mention string to a 64-bit unsigned integer representing the channel ID. A return value
/// indicates whether the parse succeeded.
/// </summary>
/// <param name="value">A string containing a mention string to parse, in the format <c>&lt;#123&gt;</c>.</param>
/// <param name="result">
/// When this method returns, contains the 64-bit unsigned integer value representing the channel ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
[CLSCompliant(false)]
public static bool TryParseChannel(string? value, out ulong result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 3 || value[0] != '<' || value[1] != '#' || value[^1] != '>')
{
return false;
}
value = value.Substring(2, value.Length - 3); // <#123>
return ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result);
}
/// <summary>
/// Parses a provided role mention string to a decimal value representing the role ID. A return value indicates whether
/// the parse succeeded.
/// </summary>
/// <param name="value">A string containing a mention string to parse, in the format <c>&lt;@&amp;123&gt;</c>.</param>
/// <param name="result">
/// When this method returns, contains the decimal value representing the role ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
public static bool TryParseRole(string? value, out decimal result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 4 || value[0] != '<' || value[1] != '@' || value[2] != '&' || value[^1] != '>')
{
return false;
}
value = value.Substring(3, value.Length - 4); // <@&123>
if (!ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out ulong actual))
{
return false;
}
result = actual;
return true;
}
/// <summary>
/// Parses a provided role mention string to a 64-bit unsigned integer representing the role ID. A return value indicates
/// whether the parse succeeded.
/// </summary>
/// <param name="value">A string containing a mention string to parse, in the format <c>&lt;@&amp;123&gt;</c>.</param>
/// <param name="result">
/// When this method returns, contains the 64-bit unsigned integer value representing the role ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
[CLSCompliant(false)]
public static bool TryParseRole(string? value, out ulong result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 4 || value[0] != '<' || value[1] != '@' || value[2] != '&' || value[^1] != '>')
{
return false;
}
value = value.Substring(3, value.Length - 4); // <@&123>
return ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result);
}
/// <summary>
/// Parses a provided user mention string to a decimal value representing the user ID. A return value indicates whether
/// the parse succeeded.
/// </summary>
/// <param name="value">
/// A string containing a mention string to parse, in the format <c>&lt;@123&gt;</c> or <c>&lt;@!123&gt;</c>.
/// </param>
/// <param name="result">
/// When this method returns, contains the decimal value representing the user ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
public static bool TryParseUser(string? value, out decimal result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 3 || value[0] != '<' || value[1] != '@' || value[^1] != '>')
{
return false;
}
if (value.Length >= 4 && value[2] == '!')
{
value = value.Substring(3, value.Length - 4); // <@!123>
}
else
{
value = value.Substring(2, value.Length - 3); // <@123>
}
if (!ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out ulong actual))
{
return false;
}
result = actual;
return true;
}
/// <summary>
/// Parses a provided user mention string to a 64-bit unsigned integer representing the user ID. A return value indicates
/// whether the parse succeeded.
/// </summary>
/// <param name="value">
/// A string containing a mention string to parse, in the format <c>&lt;@123&gt;</c> or <c>&lt;@!123&gt;</c>.
/// </param>
/// <param name="result">
/// When this method returns, contains the 64-bit unsigned integer value representing the user ID contained within
/// <paramref name="value" />, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the
/// <paramref name="value" /> parameter is <see langword="null" /> or <see cref="string.Empty" />, is not of the correct
/// format, or represents a number less than <see cref="ulong.MinValue" /> or greater than <see cref="ulong.MaxValue" />.
/// </param>
/// <returns><see langword="true" /> if the parse was successful; otherwise, <see langword="false" />.</returns>
[CLSCompliant(false)]
public static bool TryParseUser(string? value, out ulong result)
{
result = 0;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
if (value.Length < 3 || value[0] != '<' || value[1] != '@' || value[^1] != '>')
{
return false;
}
if (value.Length >= 4 && value[2] == '!')
{
value = value.Substring(3, value.Length - 4); // <@!123>
}
else
{
value = value.Substring(2, value.Length - 3); // <@123>
}
return ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result);
}
}

View File

@ -0,0 +1,69 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net6.0;netstandard2.1</TargetFrameworks>
<LangVersion>11.0</LangVersion>
<Optimize>true</Optimize>
<ImplicitUsings>true</ImplicitUsings>
<Authors>Oliver Booth</Authors>
<NeutralLanguage>en</NeutralLanguage>
<RepositoryUrl>https://github.com/oliverbooth/X10D</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Description>Extension methods on crack.</Description>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageIcon>branding_Icon.png</PackageIcon>
<PackageIconUrl/>
<PackageTags>dotnet extension-methods</PackageTags>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../CHANGELOG.md"))</PackageReleaseNotes>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<VersionPrefix>3.2.0</VersionPrefix>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' == ''">
<Version>$(VersionPrefix)-$(VersionSuffix)</Version>
<AssemblyVersion>$(VersionPrefix).0</AssemblyVersion>
<FileVersion>$(VersionPrefix).0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">
<Version>$(VersionPrefix)-$(VersionSuffix).$(BuildNumber)</Version>
<AssemblyVersion>$(VersionPrefix).$(BuildNumber)</AssemblyVersion>
<FileVersion>$(VersionPrefix).$(BuildNumber)</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(VersionSuffix)' == ''">
<Version>$(VersionPrefix)</Version>
<AssemblyVersion>$(VersionPrefix).0</AssemblyVersion>
<FileVersion>$(VersionPrefix).0</FileVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0"/>
</ItemGroup>
<ItemGroup>
<None Include="..\branding_Icon.png">
<Pack>True</Pack>
<PackagePath/>
</None>
<None Include="..\LICENSE.md">
<Pack>True</Pack>
<PackagePath/>
</None>
<None Include="..\CHANGELOG.md">
<Pack>True</Pack>
<PackagePath/>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
[assembly: CLSCompliant(true)]

View File

@ -0,0 +1,35 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace X10D.Hosting.DependencyInjection;
/// <summary>
/// Dependency injection extensions for <see cref="IServiceCollection" />.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds an <see cref="IHostedService" /> registration for the given type, while simultaneously adding it as a singleton.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the service to.</param>
/// <typeparam name="TService">The type of the service to add.</typeparam>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddHostedSingleton<TService>(this IServiceCollection services)
where TService : class, IHostedService
{
services.AddSingleton<TService>();
return services.AddSingleton<IHostedService, TService>(provider => provider.GetRequiredService<TService>());
}
/// <summary>
/// Adds an <see cref="IHostedService" /> registration for the given type, while simultaneously adding it as a singleton.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the service to.</param>
/// <param name="type">The type of the service to register and the implementation to use.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddHostedSingleton(this IServiceCollection services, Type type)
{
services.AddSingleton(type);
return services.AddSingleton(provider => (IHostedService)provider.GetRequiredService(type));
}
}

View File

@ -17,6 +17,7 @@ internal sealed class EmojiRegexGenerator : ISourceGenerator
public void Initialize(GeneratorInitializationContext context) public void Initialize(GeneratorInitializationContext context)
{ {
string response = HttpClient.GetStringAsync(TwemojiRegexUrl).GetAwaiter().GetResult(); string response = HttpClient.GetStringAsync(TwemojiRegexUrl).GetAwaiter().GetResult();
var regex = new Regex(@"export default /(?<regex>.+)/g;", RegexOptions.Compiled, Regex.InfiniteMatchTimeout);
using var reader = new StringReader(response); using var reader = new StringReader(response);
while (reader.ReadLine() is { } line) while (reader.ReadLine() is { } line)
@ -26,7 +27,7 @@ internal sealed class EmojiRegexGenerator : ISourceGenerator
continue; continue;
} }
Match match = Regex.Match(line, @"export default /(?<regex>.+)/g;"); Match match = regex.Match(line);
if (!match.Success) if (!match.Success)
{ {
continue; continue;

View File

@ -2,9 +2,11 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion> <LangVersion>11.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ExcludeFromCodeCoverage>true</ExcludeFromCodeCoverage>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,9 +2,12 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<LangVersion>11.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ExcludeFromCodeCoverage>true</ExcludeFromCodeCoverage>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,24 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks> <TargetFrameworks>net7.0;net6.0;netcoreapp3.1</TargetFrameworks>
<LangVersion>10.0</LangVersion> <LangVersion>11.0</LangVersion>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings> <ImplicitUsings>true</ImplicitUsings>
<CoverletOutputFormat>json,cobertura</CoverletOutputFormat>
<CollectCoverage>true</CollectCoverage>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/> <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/>
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="MSTest.TestFramework" Version="2.2.10"/> <PackageReference Include="Moq" Version="4.18.4"/>
<PackageReference Include="coverlet.collector" Version="3.1.2"> <PackageReference Include="MSTest.TestAdapter" Version="3.0.2"/>
<PackageReference Include="MSTest.TestFramework" Version="3.0.2"/>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\X10D.Hosting\X10D.Hosting.csproj"/>
<ProjectReference Include="..\X10D\X10D.csproj"/> <ProjectReference Include="..\X10D\X10D.csproj"/>
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,42 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class ArrayTests
{
[TestClass]
public class AsReadOnlyTests
{
[TestMethod]
public void AsReadOnly_ShouldReturnReadOnlyCollection_WhenArrayIsNotNull()
{
int[] array = {1, 2, 3};
IReadOnlyCollection<int> result = array.AsReadOnly();
Assert.IsInstanceOfType(result, typeof(IReadOnlyCollection<int>));
}
[TestMethod]
public void AsReadOnly_ShouldThrowArgumentNullException_WhenArrayIsNull()
{
int[]? array = null;
Assert.ThrowsException<ArgumentNullException>(() => array!.AsReadOnly());
}
[TestMethod]
public void AsReadOnly_ShouldReturnCorrectCount_WhenArrayIsNotEmpty()
{
int[] array = {1, 2, 3};
IReadOnlyCollection<int> result = array.AsReadOnly();
Assert.AreEqual(array.Length, result.Count);
}
[TestMethod]
public void AsReadOnly_ShouldReturnEmptyCollection_WhenArrayIsEmpty()
{
int[] array = Array.Empty<int>();
IReadOnlyCollection<int> result = array.AsReadOnly();
Assert.AreEqual(0, result.Count);
}
}
}

View File

@ -0,0 +1,63 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class ArrayTests
{
[TestClass]
public class ClearTests
{
[TestMethod]
public void Clear_ShouldClearTheArray()
{
var array = new int?[] {1, 2, 3, null, 4};
array.Clear();
Assert.IsTrue(array.All(x => x == null));
}
[TestMethod]
public void Clear_ShouldDoNothing_WhenArrayIsEmpty()
{
int[] array = Array.Empty<int>();
array.Clear();
}
[TestMethod]
public void Clear_WithRange_ShouldClearTheSpecifiedRangeOfTheArray()
{
var array = new int?[] {1, 2, 3, null, 4};
array.Clear(1..4);
Assert.AreEqual(5, array.Length);
Assert.AreEqual(1, array[0]);
Assert.AreEqual(4, array[4]);
Assert.IsTrue(array[1..4].All(x => x == null));
}
[TestMethod]
public void Clear_WithIndexAndLength_ShouldClearTheSpecifiedRangeOfTheArray()
{
var array = new int?[] {1, 2, 3, null, 4};
array.Clear(1, 3);
Assert.AreEqual(5, array.Length);
Assert.AreEqual(1, array[0]);
Assert.AreEqual(4, array[4]);
Assert.IsTrue(array[1..4].All(x => x == null));
}
[TestMethod]
public void Clear_ShouldThrowArgumentNullException_WhenArrayIsNull()
{
int[] array = null!;
Assert.ThrowsException<ArgumentNullException>(() => array.Clear());
Assert.ThrowsException<ArgumentNullException>(() => array.Clear(0, 1));
Assert.ThrowsException<ArgumentNullException>(() => array.Clear(..1));
}
}
}

View File

@ -1,49 +1,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
[TestClass] [TestClass]
public class ArrayTests public partial class ArrayTests
{ {
[TestMethod]
public void AsReadOnlyShouldBeReadOnly()
{
var array = new object[] {1, "f", true};
var readOnly = array.AsReadOnly();
Assert.IsNotNull(readOnly);
Assert.IsTrue(readOnly.Count == 3);
// ReSharper disable once ConvertTypeCheckToNullCheck
Assert.IsTrue(readOnly is IReadOnlyCollection<object>);
}
[TestMethod]
public void AsReadOnlyNullShouldThrow()
{
object[]? array = null;
Assert.ThrowsException<ArgumentNullException>(array!.AsReadOnly);
}
[CLSCompliant(false)]
[TestMethod]
[DataRow]
[DataRow(1)]
[DataRow(1, 2, 3)]
[DataRow(1, 2, 3, 4, 5)]
public void ClearShouldFillDefault(params int[] args)
{
args.Clear();
int[] clearedArray = Enumerable.Repeat(0, args.Length).ToArray();
CollectionAssert.AreEqual(clearedArray, args);
}
[TestMethod]
public void ClearNullShouldThrow()
{
int[]? array = null;
Assert.ThrowsException<ArgumentNullException>(array!.Clear);
Assert.ThrowsException<ArgumentNullException>(() => array!.Clear(0, 0));
}
} }

View File

@ -7,7 +7,7 @@ namespace X10D.Tests.Collections;
public class BoolListTests public class BoolListTests
{ {
[TestMethod] [TestMethod]
public void Pack8Bit_Should_Pack_Correctly() public void PackByte_Should_Pack_Correctly()
{ {
var array = new[] {true, false, true, false, true, false, true, false}; var array = new[] {true, false, true, false, true, false, true, false};
Assert.AreEqual(85, array.PackByte()); // 01010101 Assert.AreEqual(85, array.PackByte()); // 01010101

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Runtime.Intrinsics.X86;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections; using X10D.Collections;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
@ -7,9 +8,10 @@ namespace X10D.Tests.Collections;
public class ByteTests public class ByteTests
{ {
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToArrayCorrectly() public void Unpack_ShouldUnpackToArrayCorrectly()
{ {
bool[] bits = ((byte)0b11010100).Unpack(); const byte value = 0b11010100;
bool[] bits = value.Unpack();
Assert.AreEqual(8, bits.Length); Assert.AreEqual(8, bits.Length);
@ -24,10 +26,30 @@ public class ByteTests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToSpanCorrectly() public void Unpack_ShouldUnpackToSpanCorrectly()
{ {
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[8]; Span<bool> bits = stackalloc bool[8];
((byte)0b11010100).Unpack(bits); value.Unpack(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
}
#if NET5_0_OR_GREATER
[TestMethod]
public void UnpackInternal_Fallback_ShouldUnpackToSpanCorrectly()
{
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[8];
value.UnpackInternal_Fallback(bits);
Assert.IsFalse(bits[0]); Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]); Assert.IsFalse(bits[1]);
@ -40,18 +62,43 @@ public class ByteTests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldRepackEqually() public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
{ {
Assert.AreEqual(0b11010100, ((byte)0b11010100).Unpack().PackByte()); if (!Sse3.IsSupported)
{
return;
}
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[8];
value.UnpackInternal_Ssse3(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
}
#endif
[TestMethod]
public void Unpack_ShouldRepackEqually()
{
const byte value = 0b11010100;
Assert.AreEqual(value, value.Unpack().PackByte());
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldThrow_GivenTooSmallSpan() public void Unpack_ShouldThrow_GivenTooSmallSpan()
{ {
Assert.ThrowsException<ArgumentException>(() => Assert.ThrowsException<ArgumentException>(() =>
{ {
const byte value = 0b11010100;
Span<bool> bits = stackalloc bool[0]; Span<bool> bits = stackalloc bool[0];
((byte)0b11010100).Unpack(bits); value.Unpack(bits);
}); });
} }
} }

View File

@ -0,0 +1,45 @@
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class CollectionTests
{
[TestClass]
public class ClearAndDisposeAllTests
{
[TestMethod]
public void ClearAndDisposeAll_ShouldClearAndDisposeAllItems_WhenCalledWithValidList()
{
var mock1 = new Mock<IDisposable>();
var mock2 = new Mock<IDisposable>();
var mock3 = new Mock<IDisposable>();
var list = new List<IDisposable> {mock1.Object, mock2.Object, mock3.Object};
list.ClearAndDisposeAll();
mock1.Verify(i => i.Dispose(), Times.Once);
mock2.Verify(i => i.Dispose(), Times.Once);
mock3.Verify(i => i.Dispose(), Times.Once);
Assert.AreEqual(0, list.Count);
}
[TestMethod]
public void ClearAndDisposeAll_ShouldThrowArgumentNullException_WhenCalledWithNullList()
{
List<IDisposable>? list = null;
Assert.ThrowsException<ArgumentNullException>(() => list!.ClearAndDisposeAll());
}
[TestMethod]
public void ClearAndDisposeAll_ShouldThrowInvalidOperationException_WhenCalledWithReadOnlyList()
{
var mock = new Mock<IDisposable>();
var list = new ReadOnlyCollection<IDisposable>(new List<IDisposable> {mock.Object});
Assert.ThrowsException<InvalidOperationException>(() => list.ClearAndDisposeAll());
}
}
}

View File

@ -0,0 +1,45 @@
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class CollectionTests
{
[TestClass]
public class ClearAndDisposeAllAsyncTests
{
[TestMethod]
public async Task ClearAndDisposeAllAsync_ShouldClearAndDisposeAllItems_WhenCalledWithValidList()
{
var mock1 = new Mock<IAsyncDisposable>();
var mock2 = new Mock<IAsyncDisposable>();
var mock3 = new Mock<IAsyncDisposable>();
var list = new List<IAsyncDisposable> {mock1.Object, mock2.Object, mock3.Object};
await list.ClearAndDisposeAllAsync().ConfigureAwait(false);
mock1.Verify(i => i.DisposeAsync(), Times.Once);
mock2.Verify(i => i.DisposeAsync(), Times.Once);
mock3.Verify(i => i.DisposeAsync(), Times.Once);
Assert.AreEqual(0, list.Count);
}
[TestMethod]
public async Task ClearAndDisposeAllAsync_ShouldThrowArgumentNullException_WhenCalledWithNullList()
{
List<IAsyncDisposable>? list = null;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(list!.ClearAndDisposeAllAsync).ConfigureAwait(false);
}
[TestMethod]
public async Task ClearAndDisposeAllAsync_ShouldThrowInvalidOperationException_WhenCalledWithReadOnlyList()
{
var mock = new Mock<IAsyncDisposable>();
var list = new ReadOnlyCollection<IAsyncDisposable>(new List<IAsyncDisposable> {mock.Object});
await Assert.ThrowsExceptionAsync<InvalidOperationException>(list.ClearAndDisposeAllAsync).ConfigureAwait(false);
}
}
}

View File

@ -1,77 +1,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
[TestClass] [TestClass]
public class CollectionTests public partial class CollectionTests
{ {
[TestMethod]
public void ClearAndDisposeAll_ShouldDispose_GivenCollection()
{
var collection = new List<Disposable> {new(), new(), new()};
var copy = new List<Disposable>(collection);
collection.ClearAndDisposeAll();
Assert.IsTrue(copy.All(x => x.IsDisposed));
Assert.AreEqual(0, collection.Count);
}
[TestMethod]
public async Task ClearAndDisposeAllAsync_ShouldDispose_GivenCollection()
{
var collection = new List<Disposable> {new(), new(), new()};
var copy = new List<Disposable>(collection);
await collection.ClearAndDisposeAllAsync();
Assert.IsTrue(copy.All(x => x.IsDisposed));
Assert.AreEqual(0, collection.Count);
}
[TestMethod]
public void ClearAndDisposeAll_ShouldThrow_GivenNull()
{
List<Disposable>? collection = null;
Assert.ThrowsException<ArgumentNullException>(() => collection!.ClearAndDisposeAll());
}
[TestMethod]
public void ClearAndDisposeAllAsync_ShouldThrow_GivenNull()
{
List<Disposable>? collection = null;
Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.ClearAndDisposeAllAsync());
}
[TestMethod]
public void ClearAndDisposeAll_ShouldThrow_GivenReadOnlyCollection()
{
var collection = new List<Disposable>().AsReadOnly();
Assert.ThrowsException<InvalidOperationException>(() => collection.ClearAndDisposeAll());
}
[TestMethod]
public void ClearAndDisposeAllAsync_ShouldThrow_GivenReadOnlyCollection()
{
var collection = new List<Disposable>().AsReadOnly();
Assert.ThrowsExceptionAsync<InvalidOperationException>(async () => await collection.ClearAndDisposeAllAsync());
}
private class Disposable : IDisposable, IAsyncDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose()
{
Assert.IsTrue(IsDisposed = true);
}
#pragma warning disable CS1998
public async ValueTask DisposeAsync()
#pragma warning restore CS1998
{
Assert.IsTrue(IsDisposed = true);
}
}
} }

View File

@ -7,7 +7,7 @@ namespace X10D.Tests.Collections;
public class DictionaryTests public class DictionaryTests
{ {
[TestMethod] [TestMethod]
public void AddOrUpdate_ShouldAddNewKey_IfNotExists() public void AddOrUpdate_ShouldAddNewKey_IfNotExists_GivenConcreteDictionary()
{ {
var dictionary = new Dictionary<int, string>(); var dictionary = new Dictionary<int, string>();
Assert.IsFalse(dictionary.ContainsKey(1)); Assert.IsFalse(dictionary.ContainsKey(1));
@ -32,7 +32,32 @@ public class DictionaryTests
} }
[TestMethod] [TestMethod]
public void AddOrUpdate_ShouldUpdateKey_IfExists() public void AddOrUpdate_ShouldAddNewKey_IfNotExists_GivenIDictionary()
{
IDictionary<int, string> dictionary = new Dictionary<int, string>();
Assert.IsFalse(dictionary.ContainsKey(1));
dictionary.AddOrUpdate(1, "one", (_, _) => string.Empty);
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("one", dictionary[1]);
dictionary.Clear();
Assert.IsFalse(dictionary.ContainsKey(1));
dictionary.AddOrUpdate(1, _ => "one", (_, _) => string.Empty);
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("one", dictionary[1]);
dictionary.Clear();
Assert.IsFalse(dictionary.ContainsKey(1));
dictionary.AddOrUpdate(1, (_, _) => "one", (_, _, _) => string.Empty, 0);
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("one", dictionary[1]);
}
[TestMethod]
public void AddOrUpdate_ShouldUpdateKey_IfExists_GivenConcreteDirection()
{ {
var dictionary = new Dictionary<int, string> {[1] = "one"}; var dictionary = new Dictionary<int, string> {[1] = "one"};
Assert.IsTrue(dictionary.ContainsKey(1)); Assert.IsTrue(dictionary.ContainsKey(1));
@ -60,7 +85,35 @@ public class DictionaryTests
} }
[TestMethod] [TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullDictionary() public void AddOrUpdate_ShouldUpdateKey_IfExists_GivenIDictionary()
{
IDictionary<int, string> dictionary = new Dictionary<int, string> {[1] = "one"};
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("one", dictionary[1]);
dictionary.AddOrUpdate(1, string.Empty, (_, _) => "two");
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("two", dictionary[1]);
dictionary.Clear();
Assert.IsFalse(dictionary.ContainsKey(1));
dictionary[1] = "one";
dictionary.AddOrUpdate(1, _ => string.Empty, (_, _) => "two");
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("two", dictionary[1]);
dictionary.Clear();
Assert.IsFalse(dictionary.ContainsKey(1));
dictionary[1] = "one";
dictionary.AddOrUpdate(1, (_, _) => string.Empty, (_, _, _) => "two", 0);
Assert.IsTrue(dictionary.ContainsKey(1));
Assert.AreEqual("two", dictionary[1]);
}
[TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullDictionary_GivenConcreteDictionary()
{ {
Dictionary<int, string>? dictionary = null; Dictionary<int, string>? dictionary = null;
Assert.ThrowsException<ArgumentNullException>(() => dictionary!.AddOrUpdate(1, string.Empty, (_, _) => string.Empty)); Assert.ThrowsException<ArgumentNullException>(() => dictionary!.AddOrUpdate(1, string.Empty, (_, _) => string.Empty));
@ -71,7 +124,18 @@ public class DictionaryTests
} }
[TestMethod] [TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullUpdateValueFactory() public void AddOrUpdate_ShouldThrow_GivenNullDictionary_GivenIDictionary()
{
IDictionary<int, string>? dictionary = null;
Assert.ThrowsException<ArgumentNullException>(() => dictionary!.AddOrUpdate(1, string.Empty, (_, _) => string.Empty));
Assert.ThrowsException<ArgumentNullException>(() =>
dictionary!.AddOrUpdate(1, _ => string.Empty, (_, _) => string.Empty));
Assert.ThrowsException<ArgumentNullException>(() =>
dictionary!.AddOrUpdate(1, (_, _) => string.Empty, (_, _, _) => string.Empty, 0));
}
[TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullUpdateValueFactory_GivenConcreteDictionary()
{ {
var dictionary = new Dictionary<int, string>(); var dictionary = new Dictionary<int, string>();
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, string.Empty, null!)); Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, string.Empty, null!));
@ -80,7 +144,16 @@ public class DictionaryTests
} }
[TestMethod] [TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullAddValueFactory() public void AddOrUpdate_ShouldThrow_GivenNullUpdateValueFactory_GivenIDictionary()
{
IDictionary<int, string> dictionary = new Dictionary<int, string>();
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, string.Empty, null!));
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, _ => string.Empty, null!));
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, (_, _) => string.Empty, null!, 0));
}
[TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullAddValueFactory_GivenConcreteDictionary()
{ {
var dictionary = new Dictionary<int, string>(); var dictionary = new Dictionary<int, string>();
Func<int, string>? addValueFactory = null; Func<int, string>? addValueFactory = null;
@ -88,6 +161,15 @@ public class DictionaryTests
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, null!, (_, _, _) => "one", 0)); Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, null!, (_, _, _) => "one", 0));
} }
[TestMethod]
public void AddOrUpdate_ShouldThrow_GivenNullAddValueFactory_GivenIDictionary()
{
IDictionary<int, string> dictionary = new Dictionary<int, string>();
Func<int, string>? addValueFactory = null;
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, addValueFactory!, (_, _) => "one"));
Assert.ThrowsException<ArgumentNullException>(() => dictionary.AddOrUpdate(1, null!, (_, _, _) => "one", 0));
}
[TestMethod] [TestMethod]
public void ToConnectionString_ShouldReturnConnectionString() public void ToConnectionString_ShouldReturnConnectionString()
{ {

View File

@ -0,0 +1,34 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class EnumerableTests
{
[TestClass]
public class DisposeAllTests
{
[TestMethod]
public void DisposeAll_ShouldDisposeAllItems_WhenCalledWithValidList()
{
var mock1 = new Mock<IDisposable>();
var mock2 = new Mock<IDisposable>();
var mock3 = new Mock<IDisposable>();
var list = new List<IDisposable> {mock1.Object, mock2.Object, null!, mock3.Object};
list.DisposeAll();
mock1.Verify(i => i.Dispose(), Times.Once);
mock2.Verify(i => i.Dispose(), Times.Once);
mock3.Verify(i => i.Dispose(), Times.Once);
}
[TestMethod]
public void DisposeAll_ShouldThrowArgumentNullException_WhenCalledWithNullList()
{
List<IDisposable>? list = null;
Assert.ThrowsException<ArgumentNullException>(() => list!.DisposeAll());
}
}
}

View File

@ -0,0 +1,34 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using X10D.Collections;
namespace X10D.Tests.Collections;
public partial class EnumerableTests
{
[TestClass]
public class DisposeAllAsyncTests
{
[TestMethod]
public async Task DisposeAllAsync_ShouldDisposeAllItems_WhenCalledWithValidList()
{
var mock1 = new Mock<IAsyncDisposable>();
var mock2 = new Mock<IAsyncDisposable>();
var mock3 = new Mock<IAsyncDisposable>();
var list = new List<IAsyncDisposable> {mock1.Object, mock2.Object, null!, mock3.Object};
await list.DisposeAllAsync().ConfigureAwait(false);
mock1.Verify(i => i.DisposeAsync(), Times.Once);
mock2.Verify(i => i.DisposeAsync(), Times.Once);
mock3.Verify(i => i.DisposeAsync(), Times.Once);
}
[TestMethod]
public async Task DisposeAllAsync_ShouldThrowArgumentNullException_WhenCalledWithNullList()
{
List<IAsyncDisposable>? list = null;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => list!.DisposeAllAsync()).ConfigureAwait(false);
}
}
}

View File

@ -1,39 +1,112 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections; using X10D.Collections;
using X10D.Core;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
[TestClass] [TestClass]
public class EnumerableTests public partial class EnumerableTests
{ {
[TestMethod] [TestMethod]
public void DisposeAll_ShouldDispose_GivenCollection() public void CountWhereNot_ShouldReturnCorrectCount_GivenSequence()
{ {
var collection = new List<Disposable> {new(), new(), new()}; var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
collection.DisposeAll(); int count = enumerable.CountWhereNot(x => x % 2 == 0);
Assert.IsTrue(collection.All(x => x.IsDisposed)); Assert.AreEqual(2, count);
} }
[TestMethod] [TestMethod]
public async Task DisposeAllAsync_ShouldDispose_GivenCollection() public void CountWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{ {
var collection = new List<Disposable> {new(), new(), new()}; Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.CountWhereNot(x => x % 2 == 0));
await collection.DisposeAllAsync();
Assert.IsTrue(collection.All(x => x.IsDisposed));
} }
[TestMethod] [TestMethod]
public void DisposeAll_ShouldThrow_GivenNull() public void CountWhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{ {
List<Disposable>? collection = null; Assert.ThrowsException<ArgumentNullException>(() => Enumerable.Empty<int>().CountWhereNot(null!));
Assert.ThrowsException<ArgumentNullException>(() => collection!.DisposeAll());
} }
[TestMethod] [TestMethod]
public async Task DisposeAllAsync_ShouldThrow_GivenNull() public void CountWhereNot_ShouldThrowOverflowException_GivenLargeSource()
{ {
List<Disposable>? collection = null; IEnumerable<byte> GetValues()
await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.DisposeAllAsync()); {
while (true)
{
yield return 1;
}
// ReSharper disable once IteratorNeverReturns
}
Assert.ThrowsException<OverflowException>(() => GetValues().CountWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.FirstWhereNot(x => x % 2 == 0);
Assert.AreEqual(7, result);
}
[TestMethod]
public void FirstWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Enumerable.Range(0, 1).FirstWhereNot(null!));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() => Enumerable.Empty<int>().FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowInvalidOperationException_GivenSourceWithNoMatchingElements()
{
Assert.ThrowsException<InvalidOperationException>(() => 2.AsArrayValue().FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(7, result);
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.FirstWhereNotOrDefault(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Enumerable.Empty<int>().FirstWhereNotOrDefault(null!));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnDefault_GivenEmptySource()
{
int result = Enumerable.Empty<int>().FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnDefault_GivenSourceWithNoMatchingElements()
{
int result = 2.AsArrayValue().FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
} }
[TestMethod] [TestMethod]
@ -94,6 +167,72 @@ public class EnumerableTests
Assert.ThrowsException<ArgumentNullException>(() => source.ForEach(null!)); Assert.ThrowsException<ArgumentNullException>(() => source.ForEach(null!));
} }
[TestMethod]
public void LastWhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.LastWhereNot(x => x % 2 == 0);
Assert.AreEqual(9, result);
}
[TestMethod]
public void LastWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().LastWhereNot(null!));
}
[TestMethod]
public void LastWhereNot_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() => Array.Empty<int>().LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNot_ShouldThrowInvalidOperationException_GivenSourceWithNoMatchingElements()
{
Assert.ThrowsException<InvalidOperationException>(() => 2.AsArrayValue().LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(9, result);
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.LastWhereNotOrDefault(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().LastWhereNotOrDefault(null!));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnDefault_GivenEmptySource()
{
int result = Array.Empty<int>().LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnDefault_GivenSourceWithNoMatchingElements()
{
int result = 2.AsArrayValue().LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod] [TestMethod]
public void Shuffled_ShouldThrow_GivenNull() public void Shuffled_ShouldThrow_GivenNull()
{ {
@ -112,25 +251,56 @@ public class EnumerableTests
CollectionAssert.AreNotEqual(array, shuffled); CollectionAssert.AreNotEqual(array, shuffled);
} }
[TestMethod]
public void WhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
IEnumerable<int> result = enumerable.WhereNot(x => x % 2 == 0);
CollectionAssert.AreEqual(new[] {7, 9}, result.ToArray());
}
[TestMethod]
public void WhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.WhereNot(x => x % 2 == 0));
}
[TestMethod]
public void WhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Enumerable.Empty<int>().WhereNot(null!));
}
[TestMethod]
public void WhereNotNull_ShouldContainNoNullElements()
{
object?[] array = Enumerable.Repeat(new object(), 10).ToArray();
array[1] = null;
array[2] = null;
array[8] = null;
array[9] = null;
const int expectedCount = 6;
var actualCount = 0;
foreach (object o in array.WhereNotNull())
{
Assert.IsNotNull(o);
actualCount++;
}
Assert.AreEqual(expectedCount, actualCount);
}
[TestMethod]
public void WhereNotNull_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<string> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.WhereNotNull());
}
private class DummyClass private class DummyClass
{ {
public int Value { get; set; } public int Value { get; set; }
} }
private class Disposable : IDisposable, IAsyncDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose()
{
Assert.IsTrue(IsDisposed = true);
}
#pragma warning disable CS1998
public async ValueTask DisposeAsync()
#pragma warning restore CS1998
{
Assert.IsTrue(IsDisposed = true);
}
}
} }

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Runtime.Intrinsics.X86;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections; using X10D.Collections;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
@ -7,9 +8,10 @@ namespace X10D.Tests.Collections;
public class Int16Tests public class Int16Tests
{ {
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToArrayCorrectly() public void Unpack_ShouldUnpackToArrayCorrectly()
{ {
bool[] bits = ((short)0b11010100).Unpack(); const short value = 0b11010100;
bool[] bits = value.Unpack();
Assert.AreEqual(16, bits.Length); Assert.AreEqual(16, bits.Length);
@ -29,10 +31,11 @@ public class Int16Tests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToSpanCorrectly() public void Unpack_ShouldUnpackToSpanCorrectly()
{ {
const short value = 0b11010100;
Span<bool> bits = stackalloc bool[16]; Span<bool> bits = stackalloc bool[16];
((short)0b11010100).Unpack(bits); value.Unpack(bits);
Assert.IsFalse(bits[0]); Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]); Assert.IsFalse(bits[1]);
@ -50,18 +53,71 @@ public class Int16Tests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldRepackEqually() public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation()
{ {
Assert.AreEqual(0b11010100, ((short)0b11010100).Unpack().PackInt16()); const short value = 0b11010100;
Span<bool> bits = stackalloc bool[16];
value.UnpackInternal_Fallback(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
for (var index = 8; index < 16; index++)
{
Assert.IsFalse(bits[index]);
}
}
#if NET5_0_OR_GREATER
[TestMethod]
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
{
if (!Sse3.IsSupported)
{
return;
}
const short value = 0b11010100;
Span<bool> bits = stackalloc bool[16];
value.UnpackInternal_Ssse3(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
for (var index = 8; index < 16; index++)
{
Assert.IsFalse(bits[index]);
}
}
#endif
[TestMethod]
public void Unpack_ShouldRepackEqually()
{
const short value = 0b11010100;
Assert.AreEqual(value, value.Unpack().PackInt16());
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldThrow_GivenTooSmallSpan() public void Unpack_ShouldThrow_GivenTooSmallSpan()
{ {
Assert.ThrowsException<ArgumentException>(() => Assert.ThrowsException<ArgumentException>(() =>
{ {
const short value = 0b11010100;
Span<bool> bits = stackalloc bool[0]; Span<bool> bits = stackalloc bool[0];
((short)0b11010100).Unpack(bits); value.Unpack(bits);
}); });
} }
} }

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Runtime.Intrinsics.X86;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections; using X10D.Collections;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
@ -7,9 +8,10 @@ namespace X10D.Tests.Collections;
public class Int32Tests public class Int32Tests
{ {
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToArrayCorrectly() public void Unpack_ShouldUnpackToArrayCorrectly()
{ {
bool[] bits = 0b11010100.Unpack(); const int value = 0b11010100;
bool[] bits = value.Unpack();
Assert.AreEqual(32, bits.Length); Assert.AreEqual(32, bits.Length);
@ -29,10 +31,11 @@ public class Int32Tests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldUnpackToSpanCorrectly() public void Unpack_ShouldUnpackToSpanCorrectly()
{ {
const int value = 0b11010100;
Span<bool> bits = stackalloc bool[32]; Span<bool> bits = stackalloc bool[32];
0b11010100.Unpack(bits); value.Unpack(bits);
Assert.IsFalse(bits[0]); Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]); Assert.IsFalse(bits[1]);
@ -50,18 +53,98 @@ public class Int32Tests
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldRepackEqually() public void UnpackInternal_Fallback_ShouldUnpackToSpanCorrectly()
{ {
Assert.AreEqual(0b11010100, 0b11010100.Unpack().PackInt32()); const int value = 0b11010100;
Span<bool> bits = stackalloc bool[32];
value.UnpackInternal_Fallback(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
for (var index = 8; index < 32; index++)
{
Assert.IsFalse(bits[index]);
}
}
#if NET5_0_OR_GREATER
[TestMethod]
public void UnpackInternal_Ssse3_ShouldUnpackToSpanCorrectly()
{
if (!Ssse3.IsSupported)
{
return;
}
const int value = 0b11010100;
Span<bool> bits = stackalloc bool[32];
value.UnpackInternal_Ssse3(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
for (var index = 8; index < 32; index++)
{
Assert.IsFalse(bits[index]);
}
} }
[TestMethod] [TestMethod]
public void UnpackBits_ShouldThrow_GivenTooSmallSpan() public void UnpackInternal_Avx2_ShouldUnpackToSpanCorrectly()
{
if (!Avx2.IsSupported)
{
return;
}
const int value = 0b11010100;
Span<bool> bits = stackalloc bool[32];
value.UnpackInternal_Avx2(bits);
Assert.IsFalse(bits[0]);
Assert.IsFalse(bits[1]);
Assert.IsTrue(bits[2]);
Assert.IsFalse(bits[3]);
Assert.IsTrue(bits[4]);
Assert.IsFalse(bits[5]);
Assert.IsTrue(bits[6]);
Assert.IsTrue(bits[7]);
for (var index = 8; index < 32; index++)
{
Assert.IsFalse(bits[index]);
}
}
#endif
[TestMethod]
public void Unpack_ShouldRepackEqually()
{
const int value = 0b11010100;
Assert.AreEqual(value, value.Unpack().PackInt32());
}
[TestMethod]
public void Unpack_ShouldThrow_GivenTooSmallSpan()
{ {
Assert.ThrowsException<ArgumentException>(() => Assert.ThrowsException<ArgumentException>(() =>
{ {
const int value = 0b11010100;
Span<bool> bits = stackalloc bool[0]; Span<bool> bits = stackalloc bool[0];
0b11010100.Unpack(bits); value.Unpack(bits);
}); });
} }
} }

View File

@ -79,6 +79,124 @@ public class ListTests
Assert.ThrowsException<ArgumentNullException>(() => list!.Fill(0, 0, 0)); Assert.ThrowsException<ArgumentNullException>(() => list!.Fill(0, 0, 0));
} }
[TestMethod]
public void IndexOf_ShouldReturnCorrectValue_FromStartOfList()
{
int[] array = {0, 1, 2, 3, 4};
Assert.AreEqual(2, array.IndexOf(2));
Assert.AreEqual(2, array.IndexOf(2, 0));
Assert.AreEqual(2, array.IndexOf(2, 0, 5));
}
[TestMethod]
public void IndexOf_ShouldReturnCorrectValue_GivenSubRange()
{
int[] array = {0, 1, 2, 3, 4, 0};
Assert.AreEqual(0, array.IndexOf(0));
Assert.AreEqual(0, array.IndexOf(0, 0));
Assert.AreEqual(0, array.IndexOf(0, 0, 5));
Assert.AreEqual(5, array.IndexOf(0, 1));
Assert.AreEqual(5, array.IndexOf(0, 1, 5));
}
[TestMethod]
public void IndexOf_ShouldReturnNegative1_ForEmptyList()
{
int[] array = Array.Empty<int>();
Assert.AreEqual(-1, array.IndexOf(0));
Assert.AreEqual(-1, array.IndexOf(0, 0));
Assert.AreEqual(-1, array.IndexOf(0, 0, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentNullException_GivenNullList()
{
int[]? array = null;
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0));
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0, 0));
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0, 0, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, 0, -1));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenNegativeStartIndex()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, -1));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, -1, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenInvalidStartIndexCountPair()
{
int[] array = {0, 1, 2};
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, 2, 4));
}
[TestMethod]
public void Random_ShouldReturnContainedObject_GivenNotNull()
{
var list = new List<int>(Enumerable.Range(1, 52)); // 52! chance of being shuffled to the same order
int random = list.Random();
Assert.IsTrue(list.Contains(random));
}
[TestMethod]
public void Random_ShouldThrow_GivenNull()
{
Assert.ThrowsException<ArgumentNullException>(() => ((List<int>?)null)!.Random());
}
[TestMethod]
public void RemoveRange_ShouldThrowArgumentNullException_GivenNull()
{
Assert.ThrowsException<ArgumentNullException>(() => ((List<int>?)null)!.RemoveRange(new Range()));
}
[TestMethod]
public void RemoveRange_ShouldThrowArgumentException_GivenEndIndexLessThanStart()
{
Assert.ThrowsException<ArgumentException>(() => new List<int>().RemoveRange(2..0));
}
[TestMethod]
public void RemoveRange_ShouldThrowArgumentOutOfRangeException_GivenEndIndexGreaterThanOrEqualToCount()
{
Assert.ThrowsException<ArgumentOutOfRangeException>(() => new List<int>().RemoveRange(..0));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => new List<int> {1}.RemoveRange(..2));
}
[TestMethod]
public void RemoveRange_ShouldRemoveElements_GivenList()
{
var list = new List<int>
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
Assert.AreEqual(10, list.Count);
list.RemoveRange(2..5);
Assert.AreEqual(6, list.Count);
CollectionAssert.AreEqual(new[] {1, 2, 7, 8, 9, 10}, list);
}
[TestMethod] [TestMethod]
public void Shuffle_ShouldReorder_GivenNotNull() public void Shuffle_ShouldReorder_GivenNotNull()
{ {
@ -99,17 +217,107 @@ public class ListTests
} }
[TestMethod] [TestMethod]
public void Random_ShouldReturnContainedObject_GivenNotNull() public void Slice_ShouldReturnCorrectValue_GivenStartIndex()
{ {
var list = new List<int>(Enumerable.Range(1, 52)); // 52! chance of being shuffled to the same order int[] array = {0, 1, 2, 3, 4, 5};
int random = list.Random(); CollectionAssert.AreEqual(new[] {2, 3, 4, 5}, array.Slice(2).ToArray());
Assert.IsTrue(list.Contains(random));
} }
[TestMethod] [TestMethod]
public void Random_ShouldThrow_GivenNull() public void Slice_ShouldReturnCorrectValue_GivenStartIndexAndLength()
{ {
Assert.ThrowsException<ArgumentNullException>(() => ((List<int>?)null)!.Random()); int[] array = {0, 1, 2, 3, 4, 5};
CollectionAssert.AreEqual(new[] {2, 3, 4}, array.Slice(2, 3).ToArray());
}
[TestMethod]
public void Slice_ShouldReturnEmptyList_ForEmptyList()
{
int[] array = Array.Empty<int>();
CollectionAssert.AreEqual(Array.Empty<int>(), array.Slice(0).ToArray());
CollectionAssert.AreEqual(Array.Empty<int>(), array.Slice(0, 0).ToArray());
}
[TestMethod]
public void Slice_ShouldThrowArgumentNullException_GivenNullList()
{
int[]? array = null;
Assert.ThrowsException<ArgumentNullException>(() => array!.Slice(0));
Assert.ThrowsException<ArgumentNullException>(() => array!.Slice(0, 0));
}
[TestMethod]
public void Slice_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.Slice(0, -1));
}
[TestMethod]
public void Slice_ShouldThrowArgumentOutOfRangeException_GivenNegativeStartIndex()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.Slice(-1));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.Slice(-1, 0));
}
[TestMethod]
public void Slice_ShouldThrowArgumentOutOfRangeException_GivenInvalidStartIndexCountPair()
{
int[] array = {0, 1, 2};
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.Slice(2, 4));
}
[TestMethod]
public void Swap_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IList<int>?)null)!.Swap(new List<int>()));
}
[TestMethod]
public void Swap_ShouldThrowArgumentNullException_GivenNullTarget()
{
Assert.ThrowsException<ArgumentNullException>(() => new List<int>().Swap(null!));
}
[TestMethod]
public void Swap_ShouldSwapElements_GivenMatchingElementCount()
{
var first = new List<int> {1, 2, 3};
var second = new List<int> {4, 5, 6};
first.Swap(second);
CollectionAssert.AreEqual(new[] {4, 5, 6}, first, string.Join(' ', first));
CollectionAssert.AreEqual(new[] {1, 2, 3}, second, string.Join(' ', second));
first.Swap(second);
CollectionAssert.AreEqual(new[] {1, 2, 3}, first, string.Join(' ', first));
CollectionAssert.AreEqual(new[] {4, 5, 6}, second, string.Join(' ', second));
}
[TestMethod]
public void Swap_ShouldSwapElements_GivenDifferentElementCount()
{
var first = new List<int>
{
1,
2,
3,
4,
5
};
var second = new List<int> {6, 7};
first.Swap(second);
CollectionAssert.AreEqual(new[] {6, 7}, first, string.Join(' ', first));
CollectionAssert.AreEqual(new[] {1, 2, 3, 4, 5}, second, string.Join(' ', second));
first.Swap(second);
CollectionAssert.AreEqual(new[] {1, 2, 3, 4, 5}, first, string.Join(' ', first));
CollectionAssert.AreEqual(new[] {6, 7}, second, string.Join(' ', second));
} }
} }

View File

@ -0,0 +1,476 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
namespace X10D.Tests.Collections;
[TestClass]
public class SpanTest
{
[TestMethod]
public void Count_ShouldReturn0_GivenEmptySpan()
{
Span<int> span = Span<int>.Empty;
int count = span.Count(2);
Assert.AreEqual(0, count);
}
[TestMethod]
public void Count_ShouldReturn0_GivenEmptyReadOnlySpan()
{
ReadOnlySpan<int> span = ReadOnlySpan<int>.Empty;
int count = span.Count(2);
Assert.AreEqual(0, count);
}
[TestMethod]
public void Count_ShouldReturn8_GivenSpanWith8MatchingElements()
{
Span<int> span = stackalloc int[16] {1, 2, 3, 2, 5, 2, 7, 2, 9, 2, 11, 2, 13, 2, 15, 2};
int count = span.Count(2);
Assert.AreEqual(8, count);
}
[TestMethod]
public void Count_ShouldReturn8_GivenReadOnlySpanWith8MatchingElements()
{
ReadOnlySpan<int> span = stackalloc int[16] {1, 2, 3, 2, 5, 2, 7, 2, 9, 2, 11, 2, 13, 2, 15, 2};
int count = span.Count(2);
Assert.AreEqual(8, count);
}
[TestMethod]
public void Split_OnEmptySpan_ShouldYieldNothing_UsingCharDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = ReadOnlySpan<char>.Empty;
var index = 0;
foreach (ReadOnlySpan<char> unused in span.Split(' '))
{
index++;
}
Assert.AreEqual(0, index);
}
[TestMethod]
public void Split_OnEmptySpan_ShouldYieldNothing_UsingCharDelimiter_GivenSpan()
{
Span<char> span = Span<char>.Empty;
var index = 0;
foreach (ReadOnlySpan<char> unused in span.Split(' '))
{
index++;
}
Assert.AreEqual(0, index);
}
[TestMethod]
public void Split_OnEmptySpan_ShouldYieldNothing_UsingStringDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = ReadOnlySpan<char>.Empty;
var index = 0;
foreach (ReadOnlySpan<char> unused in span.Split(" "))
{
index++;
}
Assert.AreEqual(0, index);
}
[TestMethod]
public void Split_OnEmptySpan_ShouldYieldNothing_UsingStringDelimiter_GivenSpan()
{
Span<char> span = Span<char>.Empty;
var index = 0;
foreach (ReadOnlySpan<char> unused in span.Split(" "))
{
index++;
}
Assert.AreEqual(0, index);
}
[TestMethod]
public void Split_OnOneWord_ShouldYieldLength1_UsingCharDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWord_ShouldYieldLength1_UsingCharDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWord_ShouldYieldLength1_UsingStringDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWord_ShouldYieldLength1_UsingStringDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWordWithoutDelimiter_ShouldYieldLength1_UsingCharDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWordWithoutDelimiter_ShouldYieldLength1_UsingCharDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWordWithoutDelimiter_ShouldYieldLength1_UsingStringDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnOneWordWithoutDelimiter_ShouldYieldLength1_UsingStringDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
if (index == 0)
{
Assert.AreEqual("Hello", subSpan.ToString());
}
index++;
}
Assert.AreEqual(1, index);
}
[TestMethod]
public void Split_OnTwoWords_ShouldYieldLength2_UsingCharDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello World ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello", subSpan.ToString());
break;
case 1:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(2, index);
}
[TestMethod]
public void Split_OnTwoWords_ShouldYieldLength2_UsingCharDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello World ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello", subSpan.ToString());
break;
case 1:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(2, index);
}
[TestMethod]
public void Split_OnTwoWords_ShouldYieldLength2_UsingStringDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello World ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello", subSpan.ToString());
break;
case 1:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(2, index);
}
[TestMethod]
public void Split_OnTwoWords_ShouldYieldLength2_UsingStringDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello World ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello", subSpan.ToString());
break;
case 1:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(2, index);
}
[TestMethod]
public void Split_OnThreeWords_ShouldYieldLength3_UsingCharDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello, the World ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello,", subSpan.ToString());
break;
case 1:
Assert.AreEqual("the", subSpan.ToString());
break;
case 2:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(3, index);
}
[TestMethod]
public void Split_OnThreeWords_ShouldYieldLength3_UsingCharDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello, the World ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(' '))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello,", subSpan.ToString());
break;
case 1:
Assert.AreEqual("the", subSpan.ToString());
break;
case 2:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(3, index);
}
[TestMethod]
public void Split_OnThreeWords_ShouldYieldLength3_UsingStringDelimiter_GivenReadOnlySpan()
{
ReadOnlySpan<char> span = "Hello, the World ".AsSpan();
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello,", subSpan.ToString());
break;
case 1:
Assert.AreEqual("the", subSpan.ToString());
break;
case 2:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(3, index);
}
[TestMethod]
public void Split_OnThreeWords_ShouldYieldLength3_UsingStringDelimiter_GivenSpan()
{
ReadOnlySpan<char> source = "Hello, the World ".AsSpan();
Span<char> span = stackalloc char[source.Length];
source.CopyTo(span);
var index = 0;
foreach (ReadOnlySpan<char> subSpan in span.Split(" "))
{
switch (index)
{
case 0:
Assert.AreEqual("Hello,", subSpan.ToString());
break;
case 1:
Assert.AreEqual("the", subSpan.ToString());
break;
case 2:
Assert.AreEqual("World", subSpan.ToString());
break;
}
index++;
}
Assert.AreEqual(3, index);
}
}

View File

@ -0,0 +1,226 @@
#if NET6_0_OR_GREATER
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Core;
namespace X10D.Tests.Core;
[TestClass]
public class IntrinsicTests
{
[TestMethod]
public void CorrectBoolean_ShouldReturnExpectedVector64Result_GivenInputVector()
{
var inputVector = Vector64.Create(0, 1, 2, 0, 3, 0, 0, (byte)4);
var expectedResult = Vector64.Create(0, 1, 1, 0, 1, 0, 0, (byte)1);
Vector64<byte> result = inputVector.CorrectBoolean();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBooleanInternal_Fallback_ShouldReturnExpectedVector128Result_GivenInputVector()
{
var inputVector = Vector128.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, (byte)8);
var expectedResult = Vector128.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, (byte)1);
Vector128<byte> result = inputVector.CorrectBooleanInternal_Fallback();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBooleanInternal_Sse2_ShouldReturnExpectedVector128Result_GivenInputVector()
{
if (!Sse2.IsSupported)
{
return;
}
var inputVector = Vector128.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, (byte)8);
var expectedResult = Vector128.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, (byte)1);
Vector128<byte> result = inputVector.CorrectBooleanInternal_Sse2();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBooleanInternal_Avx2_ShouldReturnExpectedVector256Result_GivenInputVector()
{
if (!Avx2.IsSupported)
{
return;
}
var inputVector = Vector256.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, 8, 0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0,
0, 7, (byte)8);
var expectedResult = Vector256.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
0, 0, 1, (byte)1);
Vector256<byte> result = inputVector.CorrectBooleanInternal_Avx2();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void CorrectBooleanInternal_Fallback_ShouldReturnExpectedVector256Result_GivenInputVector()
{
var inputVector = Vector256.Create(0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0, 0, 7, 8, 0, 1, 2, 0, 3, 0, 0, 4, 5, 0, 0, 6, 0,
0, 7, (byte)8);
var expectedResult = Vector256.Create(0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
0, 0, 1, (byte)1);
Vector256<byte> result = inputVector.CorrectBooleanInternal_Fallback();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void HorizontalOr_ShouldReturnCombinedVector_GivenInputVector128OfUInt32()
{
Vector128<uint> left = Vector128.Create(1U, 2U, 3U, 4U);
Vector128<uint> right = Vector128.Create(5U, 6U, 7U, 8U);
Vector128<uint> expected = Vector128.Create(3U, 7U, 7U, 15U);
Vector128<uint> actual = IntrinsicUtility.HorizontalOr(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void HorizontalOrInternal_Sse_ShouldReturnCombinedVector_GivenInputVector128OfInt32()
{
Vector128<int> left = Vector128.Create(1, 2, 3, 4);
Vector128<int> right = Vector128.Create(5, 6, 7, 8);
Vector128<int> expected = Vector128.Create(3, 7, 7, 15);
Vector128<int> actual = IntrinsicUtility.HorizontalOr_Sse(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void HorizontalOrInternal_Fallback_ShouldReturnCombinedVector_GivenInputVector128OfInt32()
{
Vector128<int> left = Vector128.Create(1, 2, 3, 4);
Vector128<int> right = Vector128.Create(5, 6, 7, 8);
Vector128<int> expected = Vector128.Create(3, 7, 7, 15);
Vector128<int> actual = IntrinsicUtility.HorizontalOrInternal_Fallback(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void Multiply_ShouldReturnMultipliedVector_GivenInputVector128OfInt64()
{
Vector128<long> left = Vector128.Create(6L, 4L);
Vector128<long> right = Vector128.Create(2L, 3L);
Vector128<long> expected = Vector128.Create(12L, 12L);
Vector128<long> actual = IntrinsicUtility.Multiply(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void MultiplyInternal_Sse2_ShouldReturnMultipliedVector_GivenInputVector128OfUInt64()
{
if (!Sse2.IsSupported)
{
return;
}
Vector128<ulong> left = Vector128.Create(6UL, 4UL);
Vector128<ulong> right = Vector128.Create(2UL, 3UL);
Vector128<ulong> expected = Vector128.Create(12UL, 12UL);
Vector128<ulong> actual = IntrinsicUtility.MultiplyInternal_Sse2(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void MultiplyInternal_Fallback_ShouldReturnMultipliedVector_GivenInputVector128OfUInt64()
{
Vector128<ulong> left = Vector128.Create(6UL, 4UL);
Vector128<ulong> right = Vector128.Create(2UL, 3UL);
Vector128<ulong> expected = Vector128.Create(12UL, 12UL);
Vector128<ulong> actual = IntrinsicUtility.MultiplyInternal_Fallback(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void Multiply_ShouldReturnMultipliedVector_GivenInputVector256OfInt64()
{
Vector256<long> left = Vector256.Create(4L, 6L, 8L, 10L);
Vector256<long> right = Vector256.Create(2L, 3L, 4L, 5L);
Vector256<long> expected = Vector256.Create(8L, 18L, 32L, 50L);
Vector256<long> actual = IntrinsicUtility.Multiply(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void MultiplyInternal_Avx2_ShouldReturnMultipliedVector_GivenInputVector256OfUInt64()
{
if (!Avx2.IsSupported)
{
return;
}
Vector256<ulong> left = Vector256.Create(4UL, 6UL, 8UL, 10UL);
Vector256<ulong> right = Vector256.Create(2UL, 3UL, 4UL, 5UL);
Vector256<ulong> expected = Vector256.Create(8UL, 18UL, 32UL, 50UL);
Vector256<ulong> actual = IntrinsicUtility.MultiplyInternal_Avx2(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void MultiplyInternal_Fallback_ShouldReturnMultipliedVector_GivenInputVector256OfUInt64()
{
Vector256<ulong> left = Vector256.Create(4UL, 6UL, 8UL, 10UL);
Vector256<ulong> right = Vector256.Create(2UL, 3UL, 4UL, 5UL);
Vector256<ulong> expected = Vector256.Create(8UL, 18UL, 32UL, 50UL);
Vector256<ulong> actual = IntrinsicUtility.MultiplyInternal_Fallback(left, right);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReverseElementsInternal_Fallback_ShouldReturnExpectedVector128Result_GivenInputVector()
{
var inputVector = Vector128.Create(42UL, 69UL);
var expectedResult = Vector128.Create(69UL, 42UL);
Vector128<ulong> result = inputVector.ReverseElementsInternal_Fallback();
Assert.AreEqual(expectedResult, result);
}
[TestMethod]
public void ReverseElementsInternal_Sse2_ShouldReturnExpectedVector128Result_GivenInputVector()
{
if (!Sse2.IsSupported)
{
return;
}
var inputVector = Vector128.Create(42UL, 69UL);
var expectedResult = Vector128.Create(69UL, 42UL);
Vector128<ulong> result = inputVector.ReverseElementsInternal_Sse2();
Assert.AreEqual(expectedResult, result);
}
}
#endif

View File

@ -0,0 +1,24 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Core;
namespace X10D.Tests.Core;
[TestClass]
public class NullableTests
{
[TestMethod]
public void TryGetValue_ShouldBeTrue_GivenValue()
{
int? value = 42;
Assert.IsTrue(value.TryGetValue(out int returnedValue));
Assert.AreEqual(value, returnedValue);
}
[TestMethod]
public void TryGetValue_ShouldBeFalse_GivenNull()
{
int? value = null;
Assert.IsFalse(value.TryGetValue(out int returnedValue));
Assert.AreEqual(default, returnedValue);
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections;
using X10D.Core; using X10D.Core;
namespace X10D.Tests.Core; namespace X10D.Tests.Core;
@ -126,6 +127,48 @@ public class RandomTests
Assert.AreEqual(0, random.NextFrom(Source())); Assert.AreEqual(0, random.NextFrom(Source()));
} }
[TestMethod]
public void NextFromSpan_ShouldThrow_GivenNullRandom()
{
Random? random = null;
Assert.ThrowsException<ArgumentNullException>(() =>
{
Span<int> span = stackalloc int[1];
return random!.NextFrom(span);
});
}
[TestMethod]
public void NextFromReadOnlySpan_ShouldThrow_GivenNullRandom()
{
Random? random = null;
Assert.ThrowsException<ArgumentNullException>(() =>
{
Span<int> span = stackalloc int[1];
return random!.NextFrom(span.AsReadOnly());
});
}
[TestMethod]
public void NextFromSpan_ShouldReturnOnlyValue_GivenSpanWithLength1()
{
Span<int> span = stackalloc int[1];
span[0] = 42;
var random = new Random(1234);
Assert.AreEqual(42, random.NextFrom(span));
}
[TestMethod]
public void NextFromReadOnlySpan_ShouldReturnOnlyValue_GivenSpanWithLength1()
{
Span<int> span = stackalloc int[1];
span[0] = 42;
var random = new Random(1234);
Assert.AreEqual(42, random.NextFrom(span.AsReadOnly()));
}
[TestMethod] [TestMethod]
public void NextInt16_ShouldBe13076_GivenSeed1234() public void NextInt16_ShouldBe13076_GivenSeed1234()
{ {
@ -183,6 +226,9 @@ public class RandomTests
Random? random = null; Random? random = null;
Assert.ThrowsException<ArgumentNullException>(() => random!.NextSingle(10)); Assert.ThrowsException<ArgumentNullException>(() => random!.NextSingle(10));
Assert.ThrowsException<ArgumentNullException>(() => random!.NextSingle(0, 10)); Assert.ThrowsException<ArgumentNullException>(() => random!.NextSingle(0, 10));
#if !NET6_0_OR_GREATER
Assert.ThrowsException<ArgumentNullException>(() => random!.NextSingle());
#endif
} }
[TestMethod] [TestMethod]

View File

@ -0,0 +1,570 @@
#if NET5_0_OR_GREATER
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
#endif
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Core;
namespace X10D.Tests.Core;
[TestClass]
public class SpanTest
{
[TestMethod]
public void Contains_ShouldReturnFalse_GivenReadOnlySpanWithNoMatchingElements_UsingByteEnum()
{
ReadOnlySpan<EnumByte> span = stackalloc EnumByte[1] {EnumByte.B};
Assert.IsFalse(span.Contains(EnumByte.A));
Assert.IsFalse(span.Contains(EnumByte.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenReadOnlySpanWithNoMatchingElements_UsingInt16Enum()
{
ReadOnlySpan<EnumInt16> span = stackalloc EnumInt16[1] {EnumInt16.B};
Assert.IsFalse(span.Contains(EnumInt16.A));
Assert.IsFalse(span.Contains(EnumInt16.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenReadOnlySpanWithNoMatchingElements_UsingInt32Enum()
{
ReadOnlySpan<EnumInt32> span = stackalloc EnumInt32[1] {EnumInt32.B};
Assert.IsFalse(span.Contains(EnumInt32.A));
Assert.IsFalse(span.Contains(EnumInt32.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenReadOnlySpanWithNoMatchingElements_UsingInt64Enum()
{
ReadOnlySpan<EnumInt64> span = stackalloc EnumInt64[1] {EnumInt64.B};
Assert.IsFalse(span.Contains(EnumInt64.A));
Assert.IsFalse(span.Contains(EnumInt64.C));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenReadOnlySpanWithMatchingElements_UsingByteEnum()
{
ReadOnlySpan<EnumByte> span = stackalloc EnumByte[1] {EnumByte.B};
Assert.IsTrue(span.Contains(EnumByte.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenReadOnlySpanWithMatchingElements_UsingInt16Enum()
{
ReadOnlySpan<EnumInt16> span = stackalloc EnumInt16[1] {EnumInt16.B};
Assert.IsTrue(span.Contains(EnumInt16.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenReadOnlySpanWithMatchingElements_UsingInt32Enum()
{
ReadOnlySpan<EnumInt32> span = stackalloc EnumInt32[1] {EnumInt32.B};
Assert.IsTrue(span.Contains(EnumInt32.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenReadOnlySpanWithMatchingElements_UsingInt64Enum()
{
ReadOnlySpan<EnumInt64> span = stackalloc EnumInt64[1] {EnumInt64.B};
Assert.IsTrue(span.Contains(EnumInt64.B));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenSpanWithNoMatchingElements_UsingByteEnum()
{
Span<EnumByte> span = stackalloc EnumByte[1] {EnumByte.B};
Assert.IsFalse(span.Contains(EnumByte.A));
Assert.IsFalse(span.Contains(EnumByte.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenSpanWithNoMatchingElements_UsingInt16Enum()
{
Span<EnumInt16> span = stackalloc EnumInt16[1] {EnumInt16.B};
Assert.IsFalse(span.Contains(EnumInt16.A));
Assert.IsFalse(span.Contains(EnumInt16.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenSpanWithNoMatchingElements_UsingInt32Enum()
{
Span<EnumInt32> span = stackalloc EnumInt32[1] {EnumInt32.B};
Assert.IsFalse(span.Contains(EnumInt32.A));
Assert.IsFalse(span.Contains(EnumInt32.C));
}
[TestMethod]
public void Contains_ShouldReturnFalse_GivenSpanWithNoMatchingElements_UsingInt64Enum()
{
Span<EnumInt64> span = stackalloc EnumInt64[1] {EnumInt64.B};
Assert.IsFalse(span.Contains(EnumInt64.A));
Assert.IsFalse(span.Contains(EnumInt64.C));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenSpanWithMatchingElements_UsingByteEnum()
{
Span<EnumByte> span = stackalloc EnumByte[1] {EnumByte.B};
Assert.IsTrue(span.Contains(EnumByte.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenSpanWithMatchingElements_UsingInt16Enum()
{
Span<EnumInt16> span = stackalloc EnumInt16[1] {EnumInt16.B};
Assert.IsTrue(span.Contains(EnumInt16.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenSpanWithMatchingElements_UsingInt32Enum()
{
Span<EnumInt32> span = stackalloc EnumInt32[1] {EnumInt32.B};
Assert.IsTrue(span.Contains(EnumInt32.B));
}
[TestMethod]
public void Contains_ShouldReturnTrue_GivenSpanWithMatchingElements_UsingInt64Enum()
{
Span<EnumInt64> span = stackalloc EnumInt64[1] {EnumInt64.B};
Assert.IsTrue(span.Contains(EnumInt64.B));
}
[TestMethod]
public void PackByte_ShouldThrowArgumentException_GivenSpanLengthGreaterThan8()
{
Assert.ThrowsException<ArgumentException>(() =>
{
Span<bool> span = stackalloc bool[9];
return span.PackByte();
});
}
[TestMethod]
public void PackInt16_ShouldThrowArgumentException_GivenSpanLengthGreaterThan16()
{
Assert.ThrowsException<ArgumentException>(() =>
{
Span<bool> span = stackalloc bool[17];
return span.PackInt16();
});
}
[TestMethod]
public void PackInt32_ShouldThrowArgumentException_GivenSpanLengthGreaterThan32()
{
Assert.ThrowsException<ArgumentException>(() =>
{
Span<bool> span = stackalloc bool[33];
return span.PackInt32();
});
}
[TestMethod]
public void PackInt64_ShouldThrowArgumentException_GivenSpanLengthGreaterThan64()
{
Assert.ThrowsException<ArgumentException>(() =>
{
Span<bool> span = stackalloc bool[65];
return span.PackInt64();
});
}
[TestMethod]
public void PackByteInternal_Fallback_ShouldReturnCorrectByte_GivenReadOnlySpan_Using()
{
const byte expected = 0b00110011;
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
byte actual = span.PackByteInternal_Fallback();
Assert.AreEqual(expected, actual);
}
#if NET5_0_OR_GREATER
[TestMethod]
public void PackByteInternal_Sse2_ShouldReturnCorrectByte_GivenReadOnlySpan_Using()
{
if (!Sse2.IsSupported)
{
return;
}
const byte expected = 0b00110011;
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
byte actual = span.PackByteInternal_Sse2();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackByteInternal_AdvSimd_ShouldReturnCorrectByte_GivenReadOnlySpan_Using()
{
if (!AdvSimd.IsSupported)
{
return;
}
const byte expected = 0b00110011;
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
byte actual = span.PackByteInternal_AdvSimd();
Assert.AreEqual(expected, actual);
}
#endif
[TestMethod]
public void PackInt16_ShouldReturnSameAsPackByte_WhenSpanHasLength8()
{
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
short expected = span.PackByte();
short actual = span.PackInt16();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt16Internal_Fallback_ShouldReturnCorrectInt16_GivenReadOnlySpan()
{
const short expected = 0b00101101_11010100;
ReadOnlySpan<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
short actual = span.PackInt16Internal_Fallback();
Assert.AreEqual(expected, actual);
}
#if NET5_0_OR_GREATER
[TestMethod]
public void PackInt16Internal_Sse2_ShouldReturnCorrectInt16_GivenReadOnlySpan_Using()
{
if (!Sse2.IsSupported)
{
return;
}
const short expected = 0b00101101_11010100;
ReadOnlySpan<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
short actual = span.PackInt16Internal_Sse2();
Assert.AreEqual(expected, actual);
}
#endif
[TestMethod]
public void PackInt32Internal_Fallback_ShouldReturnCorrectInt32_GivenReadOnlySpan()
{
const int expected = 0b01010101_10101010_01010101_10101010;
ReadOnlySpan<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
int actual = span.PackInt32Internal_Fallback();
Assert.AreEqual(expected, actual);
}
#if NET5_0_OR_GREATER
[TestMethod]
public void PackInt32Internal_Sse2_ShouldReturnCorrectInt32_GivenReadOnlySpan()
{
if (!Sse2.IsSupported)
{
return;
}
const int expected = 0b01010101_10101010_01010101_10101010;
ReadOnlySpan<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
int actual = span.PackInt32Internal_Sse2();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt32Internal_Avx2_ShouldReturnCorrectInt32_GivenReadOnlySpan()
{
if (!Avx2.IsSupported)
{
return;
}
const int expected = 0b01010101_10101010_01010101_10101010;
ReadOnlySpan<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
int actual = span.PackInt32Internal_Avx2();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt32Internal_AdvSimd_ShouldReturnCorrectInt32_GivenReadOnlySpan()
{
if (!AdvSimd.IsSupported)
{
return;
}
const int expected = 0b01010101_10101010_01010101_10101010;
ReadOnlySpan<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
int actual = span.PackInt32Internal_AdvSimd();
Assert.AreEqual(expected, actual);
}
#endif
[TestMethod]
public void PackInt32_ShouldReturnSameAsPackByte_WhenSpanHasLength8_UsingReadOnlySpan()
{
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
int expected = span.PackByte();
int actual = span.PackInt32();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt32_ShouldReturnSameAsPackByte_WhenSpanHasLength8_UsingSpan()
{
Span<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
int expected = span.PackByte();
int actual = span.PackInt32();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt32_ShouldReturnSameAsPackInt16_WhenSpanHasLength16_UsingReadOnlySpan()
{
ReadOnlySpan<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
int expected = span.PackInt16();
int actual = span.PackInt32();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt32_ShouldReturnSameAsPackInt16_WhenSpanHasLength16_UsingSpan()
{
Span<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
int expected = span.PackInt16();
int actual = span.PackInt32();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnCorrectInt64_GivenReadOnlySpan()
{
const long expected = 0b01010101_11010110_01101001_11010110_00010010_10010111_00101100_10100101;
ReadOnlySpan<bool> span = stackalloc bool[64]
{
true, false, true, false, false, true, false, true, false, false, true, true, false, true, false, false, true,
true, true, false, true, false, false, true, false, true, false, false, true, false, false, false, false, true,
true, false, true, false, true, true, true, false, false, true, false, true, true, false, false, true, true,
false, true, false, true, true, true, false, true, false, true, false, true, false,
};
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnCorrectInt64_GivenSpan()
{
const long expected = 0b01010101_11010110_01101001_11010110_00010010_10010111_00101100_10100101;
Span<bool> span = stackalloc bool[64]
{
true, false, true, false, false, true, false, true, false, false, true, true, false, true, false, false, true,
true, true, false, true, false, false, true, false, true, false, false, true, false, false, false, false, true,
true, false, true, false, true, true, true, false, false, true, false, true, true, false, false, true, true,
false, true, false, true, true, true, false, true, false, true, false, true, false,
};
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackByte_WhenSpanHasLength8_UsingReadOnlySpan()
{
ReadOnlySpan<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
long expected = span.PackByte();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackByte_WhenSpanHasLength8_UsingSpan()
{
Span<bool> span = stackalloc bool[8] {true, true, false, false, true, true, false, false};
long expected = span.PackByte();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackInt16_WhenSpanHasLength16_UsingReadOnlySpan()
{
ReadOnlySpan<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
long expected = span.PackInt16();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackInt16_WhenSpanHasLength16_UsingSpan()
{
Span<bool> span = stackalloc bool[16]
{
false, false, true, false, true, false, true, true, true, false, true, true, false, true, false, false,
};
long expected = span.PackInt16();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackInt32_WhenSpanHasLength16_UsingReadOnlySpan()
{
ReadOnlySpan<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
long expected = span.PackInt32();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldReturnSameAsPackInt32_WhenSpanHasLength16_UsingSpan()
{
Span<bool> span = stackalloc bool[32]
{
false, true, false, true, false, true, false, true, true, false, true, false, true, false, true, false, false,
true, false, true, false, true, false, true, true, false, true, false, true, false, true, false,
};
long expected = span.PackInt32();
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldFallbackAndReturnCorrectValue_GivenNonPowerOfTwoLength_UsingReadOnlySpan()
{
const long expected = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000001_01010011;
ReadOnlySpan<bool> span = stackalloc bool[10] {true, true, false, false, true, false, true, false, true, false};
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void PackInt64_ShouldFallbackAndReturnCorrectValue_GivenNonPowerOfTwoLength_UsingSpan()
{
const long expected = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000001_01010011;
Span<bool> span = stackalloc bool[10] {true, true, false, false, true, false, true, false, true, false};
long actual = span.PackInt64();
Assert.AreEqual(expected, actual);
}
private enum EnumByte : byte
{
A,
B,
C
}
private enum EnumInt16 : short
{
A,
B,
C
}
private enum EnumInt32
{
A,
B,
C
}
private enum EnumInt64 : long
{
A,
B,
C
}
}

View File

@ -0,0 +1,167 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class CircleFTests
{
[TestMethod]
public void Area_ShouldBePiRadiusRadius_GivenUnitCircle()
{
var unitCircle = CircleF.Unit;
Assert.AreEqual(MathF.PI, unitCircle.Area);
}
[TestMethod]
public void Circumference_ShouldBe2PiRadius_GivenUnitCircle()
{
var unitCircle = CircleF.Unit;
Assert.AreEqual(2 * MathF.PI, unitCircle.Circumference, 1e-6f);
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(-1, CircleF.Empty.CompareTo(CircleF.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(1, CircleF.Unit.CompareTo(CircleF.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyCircleAndUnitCircleAsObject()
{
Assert.AreEqual(-1, CircleF.Empty.CompareTo((object)CircleF.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, CircleF.Unit.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitCircle()
{
var unitCircle = CircleF.Unit;
Assert.AreEqual(0, unitCircle.CompareTo(unitCircle));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => CircleF.Unit.CompareTo(new object()));
}
[TestMethod]
public void Diameter_ShouldBe2_GivenUnitCircle()
{
Assert.AreEqual(2.0f, CircleF.Unit.Diameter, 1e-6f);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
{
var unitCircle1 = CircleF.Unit;
var unitCircle2 = CircleF.Unit;
Assert.AreEqual(unitCircle1, unitCircle2);
Assert.IsTrue(unitCircle1 == unitCircle2);
Assert.IsFalse(unitCircle1 != unitCircle2);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenUnitCirclesAsObjects()
{
CircleF unitCircle1 = CircleF.Unit;
object unitCircle2 = CircleF.Unit;
Assert.AreEqual(unitCircle1, unitCircle2);
Assert.IsTrue(unitCircle1.Equals(unitCircle2));
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentCircles()
{
Assert.AreNotEqual(CircleF.Unit, CircleF.Empty);
Assert.IsFalse(CircleF.Unit == CircleF.Empty);
Assert.IsTrue(CircleF.Unit != CircleF.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.AreNotEqual((object?)null, CircleF.Empty);
Assert.IsFalse(CircleF.Empty.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = CircleF.Empty.GetHashCode();
Assert.AreEqual(hashCode, CircleF.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = CircleF.Unit.GetHashCode();
Assert.AreEqual(hashCode, CircleF.Unit.GetHashCode());
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentCircle_GivenCircle()
{
CircleF unitCircle = CircleF.Unit;
Circle converted = (Circle)unitCircle;
Assert.AreEqual(unitCircle, converted);
Assert.AreEqual(unitCircle.Radius, converted.Radius);
Assert.AreEqual(unitCircle.Center, converted.Center);
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(CircleF.Unit > CircleF.Empty);
Assert.IsTrue(CircleF.Unit >= CircleF.Empty);
Assert.IsFalse(CircleF.Unit < CircleF.Empty);
Assert.IsFalse(CircleF.Unit <= CircleF.Empty);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentCircle_GivenCircle()
{
Circle unitCircle = Circle.Unit;
CircleF converted = unitCircle;
Assert.AreEqual(unitCircle, converted);
Assert.AreEqual(unitCircle.Radius, converted.Radius);
Assert.AreEqual(unitCircle.Center, converted.Center);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(CircleF.Empty < CircleF.Unit);
Assert.IsTrue(CircleF.Empty <= CircleF.Unit);
Assert.IsFalse(CircleF.Empty > CircleF.Unit);
Assert.IsFalse(CircleF.Empty >= CircleF.Unit);
}
[TestMethod]
public void Radius_ShouldBe0_GivenEmptyCircle()
{
Assert.AreEqual(0.0f, CircleF.Empty.Radius, 1e-6f);
}
[TestMethod]
public void Radius_ShouldBe1_GivenUnitCircle()
{
Assert.AreEqual(1.0f, CircleF.Unit.Radius, 1e-6f);
}
}

View File

@ -0,0 +1,145 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class CircleTests
{
[TestMethod]
public void Area_ShouldBePiRadiusRadius_GivenUnitCircle()
{
var unitCircle = Circle.Unit;
Assert.AreEqual(MathF.PI * unitCircle.Radius * unitCircle.Radius, unitCircle.Area);
}
[TestMethod]
public void Circumference_ShouldBe2PiRadius_GivenUnitCircle()
{
var unitCircle = Circle.Unit;
Assert.AreEqual(2.0f * MathF.PI * unitCircle.Radius, unitCircle.Circumference, 1e-6f);
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(-1, Circle.Empty.CompareTo(Circle.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(1, Circle.Unit.CompareTo(Circle.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyCircleAndUnitCircleAsObject()
{
Assert.AreEqual(-1, Circle.Empty.CompareTo((object)Circle.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, Circle.Unit.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitCircle()
{
var unitCircle = Circle.Unit;
Assert.AreEqual(0, unitCircle.CompareTo(unitCircle));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => Circle.Unit.CompareTo(new object()));
}
[TestMethod]
public void Diameter_ShouldBe2_GivenUnitCircle()
{
Assert.AreEqual(2.0f, Circle.Unit.Diameter, 1e-6f);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
{
var unitCircle1 = Circle.Unit;
var unitCircle2 = Circle.Unit;
Assert.AreEqual(unitCircle1, unitCircle2);
Assert.IsTrue(unitCircle1 == unitCircle2);
Assert.IsFalse(unitCircle1 != unitCircle2);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenUnitCirclesAsObjects()
{
Circle unitCircle1 = Circle.Unit;
object unitCircle2 = Circle.Unit;
Assert.AreEqual(unitCircle1, unitCircle2);
Assert.IsTrue(unitCircle1.Equals(unitCircle2));
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentCircles()
{
Assert.AreNotEqual(Circle.Unit, Circle.Empty);
Assert.IsFalse(Circle.Unit == Circle.Empty);
Assert.IsTrue(Circle.Unit != Circle.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.AreNotEqual((object?)null, Circle.Empty);
Assert.IsFalse(Circle.Empty.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Circle.Empty.GetHashCode();
Assert.AreEqual(hashCode, Circle.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Circle.Unit.GetHashCode();
Assert.AreEqual(hashCode, Circle.Unit.GetHashCode());
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(Circle.Unit > Circle.Empty);
Assert.IsTrue(Circle.Unit >= Circle.Empty);
Assert.IsFalse(Circle.Unit < Circle.Empty);
Assert.IsFalse(Circle.Unit <= Circle.Empty);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(Circle.Empty < Circle.Unit);
Assert.IsTrue(Circle.Empty <= Circle.Unit);
Assert.IsFalse(Circle.Empty > Circle.Unit);
Assert.IsFalse(Circle.Empty >= Circle.Unit);
}
[TestMethod]
public void Radius_ShouldBe0_GivenEmptyCircle()
{
Assert.AreEqual(0, Circle.Empty.Radius);
}
[TestMethod]
public void Radius_ShouldBe1_GivenUnitCircle()
{
Assert.AreEqual(1, Circle.Unit.Radius);
}
}

View File

@ -16,6 +16,203 @@ public class ColorTests
private static readonly Color Magenta = Color.FromArgb(255, 0, 255); private static readonly Color Magenta = Color.FromArgb(255, 0, 255);
private static readonly Color Yellow = Color.FromArgb(255, 255, 0); private static readonly Color Yellow = Color.FromArgb(255, 255, 0);
[TestMethod]
public void Deconstruct_ShouldDeconstructColor_GivenColor()
{
(byte r, byte g, byte b) = Black;
Assert.AreEqual(0, r);
Assert.AreEqual(0, g);
Assert.AreEqual(0, b);
(byte a, r, g, b) = Black;
Assert.AreEqual(255, a);
Assert.AreEqual(0, r);
Assert.AreEqual(0, g);
Assert.AreEqual(0, b);
(r, g, b) = Red;
Assert.AreEqual(255, r);
Assert.AreEqual(0, g);
Assert.AreEqual(0, b);
(a, r, g, b) = Red;
Assert.AreEqual(255, a);
Assert.AreEqual(255, r);
Assert.AreEqual(0, g);
Assert.AreEqual(0, b);
(r, g, b) = Green;
Assert.AreEqual(0, r);
Assert.AreEqual(255, g);
Assert.AreEqual(0, b);
(a, r, g, b) = Green;
Assert.AreEqual(255, a);
Assert.AreEqual(0, r);
Assert.AreEqual(255, g);
Assert.AreEqual(0, b);
(r, g, b) = Blue;
Assert.AreEqual(0, r);
Assert.AreEqual(0, g);
Assert.AreEqual(255, b);
(a, r, g, b) = Blue;
Assert.AreEqual(255, a);
Assert.AreEqual(0, r);
Assert.AreEqual(0, g);
Assert.AreEqual(255, b);
}
[TestMethod]
public void GetClosestConsoleColor_ShouldReturnClosestColor_GivenValidColor()
{
Assert.AreEqual(ConsoleColor.White, Color.Transparent.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.AliceBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.AntiqueWhite.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.Aqua.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Aquamarine.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Azure.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Beige.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Bisque.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Black, Color.Black.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.BlanchedAlmond.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Blue, Color.Blue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.BlueViolet.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.Brown.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.BurlyWood.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.CadetBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Yellow, Color.Chartreuse.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Chocolate.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Coral.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.CornflowerBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Cornsilk.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Red, Color.Crimson.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.Cyan.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkBlue, Color.DarkBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.DarkCyan.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.DarkGoldenrod.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.DarkGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGreen, Color.DarkGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.DarkKhaki.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.DarkMagenta.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.DarkOliveGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.DarkOrange.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.DarkOrchid.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.DarkRed.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.DarkSalmon.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.DarkSeaGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.DarkSlateBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGreen, Color.DarkSlateGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.DarkTurquoise.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.DarkViolet.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Magenta, Color.DeepPink.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.DeepSkyBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.DimGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.DodgerBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.Firebrick.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.FloralWhite.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Green, Color.ForestGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Magenta, Color.Fuchsia.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Gainsboro.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.GhostWhite.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Yellow, Color.Gold.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Goldenrod.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.Gray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Green, Color.Green.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Yellow, Color.GreenYellow.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Honeydew.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.HotPink.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.IndianRed.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.Indigo.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Ivory.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Khaki.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Lavender.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.LavenderBlush.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Yellow, Color.LawnGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.LemonChiffon.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightCoral.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.LightCyan.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.LightGoldenrodYellow.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightPink.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightSalmon.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.LightSeaGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightSkyBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.LightSlateGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.LightSteelBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.LightYellow.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Green, Color.Lime.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Green, Color.LimeGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Linen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Magenta, Color.Magenta.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.Maroon.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.MediumAquamarine.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Blue, Color.MediumBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.MediumOrchid.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.MediumPurple.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.MediumSeaGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.MediumSlateBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.MediumSpringGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.MediumTurquoise.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.MediumVioletRed.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkBlue, Color.MidnightBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.MintCream.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.MistyRose.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Moccasin.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.NavajoWhite.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkBlue, Color.Navy.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.OldLace.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.Olive.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.OliveDrab.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Orange.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Red, Color.OrangeRed.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Orchid.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.PaleGoldenrod.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.PaleGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.PaleTurquoise.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.PaleVioletRed.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.PapayaWhip.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.PeachPuff.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Peru.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Pink.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Plum.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.PowderBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.Purple.GetClosestConsoleColor());
#if NET6_0_OR_GREATER
Assert.AreEqual(ConsoleColor.DarkMagenta, Color.RebeccaPurple.GetClosestConsoleColor());
#endif
Assert.AreEqual(ConsoleColor.Red, Color.Red.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.RosyBrown.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.RoyalBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.SaddleBrown.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Salmon.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.SandyBrown.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.SeaGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.SeaShell.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkRed, Color.Sienna.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Silver.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.SkyBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.SlateBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.SlateGray.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Snow.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.SpringGreen.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.SteelBlue.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Tan.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkCyan, Color.Teal.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Thistle.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkYellow, Color.Tomato.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Cyan, Color.Turquoise.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.DarkGray, Color.Violet.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.Wheat.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.White.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.White, Color.WhiteSmoke.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Yellow, Color.Yellow.GetClosestConsoleColor());
Assert.AreEqual(ConsoleColor.Gray, Color.YellowGreen.GetClosestConsoleColor());
}
[TestMethod] [TestMethod]
public void Inverted_ShouldReturnInvertedColor() public void Inverted_ShouldReturnInvertedColor()
{ {

View File

@ -0,0 +1,82 @@
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class CuboidTests
{
[TestMethod]
public void Corners_ShouldBeCorrect_GivenCubeOfSize1()
{
Cuboid cube = Cuboid.Cube;
Assert.AreEqual(new Vector3(0.5f, 0.5f, -0.5f), cube.FrontTopRight);
Assert.AreEqual(new Vector3(-0.5f, 0.5f, -0.5f), cube.FrontTopLeft);
Assert.AreEqual(new Vector3(0.5f, -0.5f, -0.5f), cube.FrontBottomRight);
Assert.AreEqual(new Vector3(-0.5f, -0.5f, -0.5f), cube.FrontBottomLeft);
Assert.AreEqual(new Vector3(0.5f, 0.5f, 0.5f), cube.BackTopRight);
Assert.AreEqual(new Vector3(-0.5f, 0.5f, 0.5f), cube.BackTopLeft);
Assert.AreEqual(new Vector3(0.5f, -0.5f, 0.5f), cube.BackBottomRight);
Assert.AreEqual(new Vector3(-0.5f, -0.5f, 0.5f), cube.BackBottomLeft);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoCubesOfSize1()
{
var cube1 = Cuboid.Cube;
var cube2 = Cuboid.Cube;
Assert.AreEqual(cube1, cube2);
Assert.IsTrue(cube1 == cube2);
Assert.IsFalse(cube1 != cube2);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentCubes()
{
Assert.AreNotEqual(Cuboid.Cube, Cuboid.Empty);
Assert.IsFalse(Cuboid.Cube == Cuboid.Empty);
Assert.IsTrue(Cuboid.Cube != Cuboid.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(Cuboid.Cube.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Cuboid.Empty.GetHashCode();
Assert.AreEqual(hashCode, Cuboid.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenCubeOfSize1()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Cuboid.Cube.GetHashCode();
Assert.AreEqual(hashCode, Cuboid.Cube.GetHashCode());
}
[TestMethod]
public void Size_ShouldBeOne_GivenCubeOfSize1()
{
Assert.AreEqual(Vector3.One, Cuboid.Cube.Size);
}
[TestMethod]
public void Size_ShouldBeOne_GivenRotatedCube()
{
Assert.AreEqual(Vector3.One, new Cuboid(0, 0, 0, 1, 1, 1, 90, 0, 0).Size);
}
[TestMethod]
public void Volume_ShouldBe1_GivenCubeOfSize1()
{
Assert.AreEqual(1.0f, Cuboid.Cube.Volume, 1e-6f);
}
}

View File

@ -0,0 +1,156 @@
using System.Drawing;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class EllipseFTests
{
[TestMethod]
public void Area_ShouldBePiRadiusRadius_GivenUnitEllipse()
{
var unitEllipse = EllipseF.Unit;
Assert.AreEqual(MathF.PI, unitEllipse.Area, 1e-6f);
}
[TestMethod]
public void ApproximateCircumference_ShouldBe2PiRadius_GivenUnitEllipse()
{
var unitEllipse = EllipseF.Unit;
Assert.AreEqual(2 * MathF.PI, unitEllipse.ApproximateCircumference, 1e-6f);
}
[TestMethod]
public void Constructor_ShouldGiveCorrectEllipse()
{
var ellipse = new EllipseF(PointF.Empty, new SizeF(2, 1));
Assert.AreEqual(new PointF(0, 0), ellipse.Center);
Assert.AreEqual(new SizeF(2, 1), ellipse.Radius);
ellipse = new EllipseF(0, 0, 2, 1);
Assert.AreEqual(new PointF(0, 0), ellipse.Center);
Assert.AreEqual(new SizeF(2, 1), ellipse.Radius);
ellipse = new EllipseF(PointF.Empty, new Vector2(2, 1));
Assert.AreEqual(new PointF(0, 0), ellipse.Center);
Assert.AreEqual(new SizeF(2, 1), ellipse.Radius);
ellipse = new EllipseF(Vector2.Zero, new Vector2(2, 1));
Assert.AreEqual(new PointF(0, 0), ellipse.Center);
Assert.AreEqual(new SizeF(2, 1), ellipse.Radius);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitEllipses()
{
var unitEllipse1 = EllipseF.Unit;
var unitEllipse2 = EllipseF.Unit;
Assert.AreEqual(unitEllipse1, unitEllipse2);
Assert.IsTrue(unitEllipse1 == unitEllipse2);
Assert.IsFalse(unitEllipse1 != unitEllipse2);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentEllipses()
{
Assert.AreNotEqual(EllipseF.Unit, EllipseF.Empty);
Assert.IsFalse(EllipseF.Unit == EllipseF.Empty);
Assert.IsTrue(EllipseF.Unit != EllipseF.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(EllipseF.Unit.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyEllipse()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = EllipseF.Empty.GetHashCode();
Assert.AreEqual(hashCode, EllipseF.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitEllipse()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = EllipseF.Unit.GetHashCode();
Assert.AreEqual(hashCode, EllipseF.Unit.GetHashCode());
}
[TestMethod]
public void HorizontalRadius_ShouldBe0_GivenEmptyEllipse()
{
Assert.AreEqual(0, EllipseF.Empty.HorizontalRadius);
}
[TestMethod]
public void HorizontalRadius_ShouldBe1_GivenUnitEllipse()
{
Assert.AreEqual(1, EllipseF.Unit.HorizontalRadius);
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentEllipse_GivenEllipse()
{
EllipseF unitEllipse = EllipseF.Unit;
Ellipse converted = (Ellipse)unitEllipse;
Assert.AreEqual(unitEllipse, converted);
Assert.AreEqual(unitEllipse.HorizontalRadius, converted.HorizontalRadius);
Assert.AreEqual(unitEllipse.VerticalRadius, converted.VerticalRadius);
Assert.AreEqual(unitEllipse.Center, converted.Center);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentEllipse_GivenCircle()
{
Circle unitCircle = Circle.Unit;
EllipseF converted = unitCircle;
Assert.AreEqual(unitCircle, converted);
Assert.AreEqual(unitCircle.Radius, converted.HorizontalRadius);
Assert.AreEqual(unitCircle.Radius, converted.VerticalRadius);
Assert.AreEqual(unitCircle.Center, converted.Center);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentEllipse_GivenCircleF()
{
CircleF unitCircle = CircleF.Unit;
EllipseF converted = unitCircle;
Assert.AreEqual(unitCircle, converted);
Assert.AreEqual(unitCircle.Radius, converted.HorizontalRadius);
Assert.AreEqual(unitCircle.Radius, converted.VerticalRadius);
Assert.AreEqual(unitCircle.Center, converted.Center);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentEllipse_GivenEllipse()
{
Ellipse unitEllipse = Ellipse.Unit;
EllipseF converted = unitEllipse;
Assert.AreEqual(unitEllipse, converted);
Assert.AreEqual(unitEllipse.HorizontalRadius, converted.HorizontalRadius);
Assert.AreEqual(unitEllipse.VerticalRadius, converted.VerticalRadius);
Assert.AreEqual(unitEllipse.Center, converted.Center);
}
[TestMethod]
public void VerticalRadius_ShouldBe0_GivenEmptyEllipse()
{
Assert.AreEqual(0, EllipseF.Empty.VerticalRadius);
}
[TestMethod]
public void VerticalRadius_ShouldBe1_GivenUnitEllipse()
{
Assert.AreEqual(1, EllipseF.Unit.VerticalRadius);
}
}

View File

@ -0,0 +1,111 @@
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class EllipseTests
{
[TestMethod]
public void Area_ShouldBePiRadiusRadius_GivenUnitEllipse()
{
var unitEllipse = Ellipse.Unit;
Assert.AreEqual(MathF.PI, unitEllipse.Area, 1e-6f);
}
[TestMethod]
public void ApproximateCircumference_ShouldBe2PiRadius_GivenUnitEllipse()
{
var unitEllipse = Ellipse.Unit;
Assert.AreEqual(2 * MathF.PI, unitEllipse.ApproximateCircumference, 1e-6f);
}
[TestMethod]
public void Constructor_ShouldGiveCorrectEllipse()
{
var ellipse = new Ellipse(Point.Empty, new Size(2, 1));
Assert.AreEqual(new Point(0, 0), ellipse.Center);
Assert.AreEqual(new Size(2, 1), ellipse.Radius);
ellipse = new Ellipse(0, 0, 2, 1);
Assert.AreEqual(new Point(0, 0), ellipse.Center);
Assert.AreEqual(new Size(2, 1), ellipse.Radius);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitEllipses()
{
var unitEllipse1 = Ellipse.Unit;
var unitEllipse2 = Ellipse.Unit;
Assert.AreEqual(unitEllipse1, unitEllipse2);
Assert.IsTrue(unitEllipse1 == unitEllipse2);
Assert.IsFalse(unitEllipse1 != unitEllipse2);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentEllipses()
{
Assert.AreNotEqual(Ellipse.Unit, Ellipse.Empty);
Assert.IsFalse(Ellipse.Unit == Ellipse.Empty);
Assert.IsTrue(Ellipse.Unit != Ellipse.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(Ellipse.Unit.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyEllipse()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Ellipse.Empty.GetHashCode();
Assert.AreEqual(hashCode, Ellipse.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitEllipse()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Ellipse.Unit.GetHashCode();
Assert.AreEqual(hashCode, Ellipse.Unit.GetHashCode());
}
[TestMethod]
public void HorizontalRadius_ShouldBe0_GivenEmptyEllipse()
{
Assert.AreEqual(0, Ellipse.Empty.HorizontalRadius);
}
[TestMethod]
public void HorizontalRadius_ShouldBe1_GivenUnitEllipse()
{
Assert.AreEqual(1, Ellipse.Unit.HorizontalRadius);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentEllipse_GivenCircle()
{
Circle unitCircle = Circle.Unit;
Ellipse converted = unitCircle;
Assert.AreEqual(unitCircle, converted);
Assert.AreEqual(unitCircle.Radius, converted.HorizontalRadius);
Assert.AreEqual(unitCircle.Radius, converted.VerticalRadius);
Assert.AreEqual(unitCircle.Center, converted.Center);
}
[TestMethod]
public void VerticalRadius_ShouldBe0_GivenEmptyEllipse()
{
Assert.AreEqual(0, Ellipse.Empty.VerticalRadius);
}
[TestMethod]
public void VerticalRadius_ShouldBe1_GivenUnitEllipse()
{
Assert.AreEqual(1, Ellipse.Unit.VerticalRadius);
}
}

View File

@ -0,0 +1,188 @@
using System.Drawing;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class Line3DTests
{
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyAndOne()
{
Assert.AreEqual(-1, Line3D.Empty.CompareTo(Line3D.One));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyLineAndOneLineAsObject()
{
Assert.AreEqual(-1, Line3D.Empty.CompareTo((object)Line3D.One));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, Line3D.One.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenOneAndEmpty()
{
Assert.AreEqual(1, Line3D.One.CompareTo(Line3D.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitLine()
{
var unitLine3D = Line3D.One;
Assert.AreEqual(0, unitLine3D.CompareTo(unitLine3D));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => Line3D.Empty.CompareTo(new object()));
}
[TestMethod]
public void Length_ShouldBe0_GivenEmptyLine()
{
Assert.AreEqual(0.0f, Line3D.Empty.Length);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitXLine()
{
Assert.AreEqual(1.0f, Line3D.UnitX.Length, 1e-6f);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitYLine()
{
Assert.AreEqual(1.0f, Line3D.UnitY.Length, 1e-6f);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitZLine()
{
Assert.AreEqual(1.0f, Line3D.UnitZ.Length, 1e-6f);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitLines()
{
Line3D first = Line3D.One;
Line3D second = Line3D.One;
Assert.AreEqual(first, second);
Assert.IsTrue(first == second);
Assert.IsFalse(first != second);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentLines()
{
Assert.AreNotEqual(Line3D.One, Line3D.Empty);
Assert.IsFalse(Line3D.One == Line3D.Empty);
Assert.IsTrue(Line3D.One != Line3D.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(Line3D.One.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Line3D.Empty.GetHashCode();
Assert.AreEqual(hashCode, Line3D.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Line3D.One.GetHashCode();
Assert.AreEqual(hashCode, Line3D.One.GetHashCode());
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentLine_GivenLine()
{
Line3D oneLine = new Line3D(Vector3.Zero, Vector3.UnitX + Vector3.UnitY);
Line converted = (Line)oneLine;
var expectedStart = new Point((int)oneLine.Start.X, (int)oneLine.Start.Y);
var expectedEnd = new Point((int)oneLine.End.X, (int)oneLine.End.Y);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(expectedStart, converted.Start);
Assert.AreEqual(expectedEnd, converted.End);
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentLineF_GivenLine()
{
Line3D oneLine = new Line3D(Vector3.Zero, Vector3.UnitX + Vector3.UnitY);
LineF converted = (LineF)oneLine;
var expectedStart = new PointF(oneLine.Start.X, oneLine.Start.Y);
var expectedEnd = new PointF(oneLine.End.X, oneLine.End.Y);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(expectedStart, converted.Start);
Assert.AreEqual(expectedEnd, converted.End);
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(Line3D.One > Line3D.Empty);
Assert.IsTrue(Line3D.One >= Line3D.Empty);
Assert.IsFalse(Line3D.One < Line3D.Empty);
Assert.IsFalse(Line3D.One <= Line3D.Empty);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentLine_GivenLine()
{
Line oneLine = Line.One;
Line3D converted = oneLine;
var expectedStart = new Vector3(oneLine.Start.X, oneLine.Start.Y, 0.0f);
var expectedEnd = new Vector3(oneLine.End.X, oneLine.End.Y, 0.0f);
Assert.AreEqual(oneLine, converted);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(expectedStart, converted.Start);
Assert.AreEqual(expectedEnd, converted.End);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentLine_GivenLineF()
{
LineF oneLine = LineF.One;
Line3D converted = oneLine;
var expectedStart = new Vector3(oneLine.Start.X, oneLine.Start.Y, 0.0f);
var expectedEnd = new Vector3(oneLine.End.X, oneLine.End.Y, 0.0f);
Assert.AreEqual(oneLine, converted);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(expectedStart, converted.Start);
Assert.AreEqual(expectedEnd, converted.End);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(Line3D.Empty < Line3D.One);
Assert.IsTrue(Line3D.Empty <= Line3D.One);
Assert.IsFalse(Line3D.Empty > Line3D.One);
Assert.IsFalse(Line3D.Empty >= Line3D.One);
}
}

View File

@ -0,0 +1,146 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class LineFTests
{
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyAndOne()
{
Assert.AreEqual(-1, LineF.Empty.CompareTo(LineF.One));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyLineAndOneLineAsObject()
{
Assert.AreEqual(-1, LineF.Empty.CompareTo((object)LineF.One));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, LineF.One.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenOneAndEmpty()
{
Assert.AreEqual(1, LineF.One.CompareTo(LineF.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitLine()
{
var unitLineF = LineF.One;
Assert.AreEqual(0, unitLineF.CompareTo(unitLineF));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => LineF.Empty.CompareTo(new object()));
}
[TestMethod]
public void Length_ShouldBe0_GivenEmptyLine()
{
Assert.AreEqual(0.0f, LineF.Empty.Length);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitXLine()
{
Assert.AreEqual(1.0f, LineF.UnitX.Length, 1e-6f);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitYLine()
{
Assert.AreEqual(1.0f, LineF.UnitY.Length, 1e-6f);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitLines()
{
LineF first = LineF.One;
LineF second = LineF.One;
Assert.AreEqual(first, second);
Assert.IsTrue(first == second);
Assert.IsFalse(first != second);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentLines()
{
Assert.AreNotEqual(LineF.One, LineF.Empty);
Assert.IsFalse(LineF.One == LineF.Empty);
Assert.IsTrue(LineF.One != LineF.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(LineF.One.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = LineF.Empty.GetHashCode();
Assert.AreEqual(hashCode, LineF.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = LineF.One.GetHashCode();
Assert.AreEqual(hashCode, LineF.One.GetHashCode());
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentLine_GivenLine()
{
LineF oneLine = LineF.One;
Line converted = (Line)oneLine;
Assert.AreEqual(oneLine, converted);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(oneLine.Start, converted.Start);
Assert.AreEqual(oneLine.End, converted.End);
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(LineF.One > LineF.Empty);
Assert.IsTrue(LineF.One >= LineF.Empty);
Assert.IsFalse(LineF.One < LineF.Empty);
Assert.IsFalse(LineF.One <= LineF.Empty);
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentLine_GivenLine()
{
Line oneLine = Line.One;
LineF converted = oneLine;
Assert.AreEqual(oneLine, converted);
Assert.AreEqual(oneLine.Length, converted.Length);
Assert.AreEqual(oneLine.Start, converted.Start);
Assert.AreEqual(oneLine.End, converted.End);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(LineF.Empty < LineF.One);
Assert.IsTrue(LineF.Empty <= LineF.One);
Assert.IsFalse(LineF.Empty > LineF.One);
Assert.IsFalse(LineF.Empty >= LineF.One);
}
}

View File

@ -0,0 +1,122 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class LineTests
{
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyAndOne()
{
Assert.AreEqual(-1, Line.Empty.CompareTo(Line.One));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyLineAndOneLineAsObject()
{
Assert.AreEqual(-1, Line.Empty.CompareTo((object)Line.One));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, Line.One.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenOneAndEmpty()
{
Assert.AreEqual(1, Line.One.CompareTo(Line.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitLine()
{
var unitLine = Line.One;
Assert.AreEqual(0, unitLine.CompareTo(unitLine));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => Line.Empty.CompareTo(new object()));
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitLines()
{
Line first = Line.One;
Line second = Line.One;
Assert.AreEqual(first, second);
Assert.IsTrue(first == second);
Assert.IsFalse(first != second);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentLines()
{
Assert.AreNotEqual(Line.One, Line.Empty);
Assert.IsFalse(Line.One == Line.Empty);
Assert.IsTrue(Line.One != Line.Empty);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(Line.One.Equals(null));
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Line.Empty.GetHashCode();
Assert.AreEqual(hashCode, Line.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitLine()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Line.One.GetHashCode();
Assert.AreEqual(hashCode, Line.One.GetHashCode());
}
[TestMethod]
public void Length_ShouldBe0_GivenEmptyLine()
{
Assert.AreEqual(0.0f, Line.Empty.Length);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitXLine()
{
Assert.AreEqual(1.0f, Line.UnitX.Length, 1e-6f);
}
[TestMethod]
public void Length_ShouldBe1_GivenUnitYLine()
{
Assert.AreEqual(1.0f, Line.UnitY.Length, 1e-6f);
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(Line.One > Line.Empty);
Assert.IsTrue(Line.One >= Line.Empty);
Assert.IsFalse(Line.One < Line.Empty);
Assert.IsFalse(Line.One <= Line.Empty);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(Line.Empty < Line.One);
Assert.IsTrue(Line.Empty <= Line.One);
Assert.IsFalse(Line.Empty > Line.One);
Assert.IsFalse(Line.Empty >= Line.One);
}
}

View File

@ -0,0 +1,65 @@
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#if !NET6_0_OR_GREATER
using X10D.Core;
#endif
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class PointFTests
{
[TestMethod]
public void IsOnLine_ShouldReturnTrue_GivenPointOnLine()
{
var point = new PointF(1.0f, 0.0f);
var line = new LineF(PointF.Empty, new PointF(2.0f, 0.0f));
Assert.IsTrue(point.IsOnLine(line));
Assert.IsTrue(point.IsOnLine(line.Start, line.End));
Assert.IsTrue(point.IsOnLine(line.Start.ToVector2(), line.End.ToVector2()));
}
[TestMethod]
public void IsOnLine_ShouldReturnFalse_GivenPointNotOnLine()
{
var point = new PointF(1.0f, 1.0f);
var line = new LineF(PointF.Empty, new PointF(2.0f, 0.0f));
Assert.IsFalse(point.IsOnLine(line));
Assert.IsFalse(point.IsOnLine(line.Start, line.End));
Assert.IsFalse(point.IsOnLine(line.Start.ToVector2(), line.End.ToVector2()));
}
[TestMethod]
public void Round_ShouldRoundToNearestInteger_GivenNoParameters()
{
var point = new PointF(1.5f, 2.6f);
var rounded = point.Round();
Assert.AreEqual(2, rounded.X);
Assert.AreEqual(3, rounded.Y);
}
[TestMethod]
public void Round_ShouldRoundToNearest10_GivenPrecision10()
{
var point = new PointF(1.5f, 25.2f);
var rounded = point.Round(10);
Assert.AreEqual(0, rounded.X);
Assert.AreEqual(30, rounded.Y);
}
[TestMethod]
public void ToSizeF_ShouldReturnSize_WithEquivalentMembers()
{
var random = new Random();
var point = new PointF(random.NextSingle(), random.NextSingle());
var size = point.ToSizeF();
Assert.AreEqual(point.X, size.Width, 1e-6f);
Assert.AreEqual(point.Y, size.Height, 1e-6f);
}
}

View File

@ -0,0 +1,64 @@
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class PointTests
{
[TestMethod]
public void IsOnLine_ShouldReturnTrue_GivenPointOnLine()
{
var point = new Point(1, 0);
var line = new Line(Point.Empty, new Point(2, 0));
Assert.IsTrue(point.IsOnLine(line));
Assert.IsTrue(point.IsOnLine(line.Start, line.End));
Assert.IsTrue(point.IsOnLine(line.Start.ToVector2(), line.End.ToVector2()));
}
[TestMethod]
public void IsOnLine_ShouldReturnFalse_GivenPointNotOnLine()
{
var point = new Point(1, 1);
var line = new Line(Point.Empty, new Point(2, 0));
Assert.IsFalse(point.IsOnLine(line));
Assert.IsFalse(point.IsOnLine(line.Start, line.End));
Assert.IsFalse(point.IsOnLine(line.Start.ToVector2(), line.End.ToVector2()));
}
[TestMethod]
public void ToSize_ShouldReturnSize_WithEquivalentMembers()
{
var random = new Random();
var point = new Point(random.Next(), random.Next());
var size = point.ToSize();
Assert.AreEqual(point.X, size.Width);
Assert.AreEqual(point.Y, size.Height);
}
[TestMethod]
public void ToSizeF_ShouldReturnSize_WithEquivalentMembers()
{
var random = new Random();
var point = new Point(random.Next(), random.Next());
var size = point.ToSizeF();
Assert.AreEqual(point.X, size.Width, 1e-6f);
Assert.AreEqual(point.Y, size.Height, 1e-6f);
}
[TestMethod]
public void ToVector2_ShouldReturnVector_WithEquivalentMembers()
{
var random = new Random();
var point = new Point(random.Next(), random.Next());
var size = point.ToVector2();
Assert.AreEqual(point.X, size.X, 1e-6f);
Assert.AreEqual(point.Y, size.Y, 1e-6f);
}
}

View File

@ -0,0 +1,247 @@
using System.Drawing;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class PolygonFTests
{
[TestMethod]
public void AddVertices_ShouldAddVertices()
{
var polygon = PolygonF.Empty;
polygon.AddVertices(new[] {new PointF(1, 2), new PointF(3, 4)});
Assert.AreEqual(2, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, PolygonF.Empty.VertexCount);
}
[TestMethod]
public void AddVertices_ShouldThrowArgumentNullException_GivenNullEnumerableOfPointF()
{
var polygon = PolygonF.Empty;
IEnumerable<PointF> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => polygon.AddVertices(vertices));
}
[TestMethod]
public void AddVertices_ShouldThrowArgumentNullException_GivenNullEnumerableOfVector2()
{
var polygon = PolygonF.Empty;
IEnumerable<Vector2> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => polygon.AddVertices(vertices));
}
[TestMethod]
public void ClearVertices_ShouldClearVertices()
{
var polygon = PolygonF.Empty;
polygon.AddVertices(new[] {new Vector2(1, 2), new Vector2(3, 4)});
Assert.AreEqual(2, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, PolygonF.Empty.VertexCount);
polygon.ClearVertices();
Assert.AreEqual(0, polygon.VertexCount);
}
[TestMethod]
public void Constructor_ShouldPopulateVertices_GivenPolygon()
{
var pointPolygon = new PolygonF(new[] {new PointF(1, 2), new PointF(3, 4)});
var vectorPolygon = new PolygonF(new[] {new Vector2(1, 2), new Vector2(3, 4)});
Assert.AreEqual(2, pointPolygon.VertexCount);
Assert.AreEqual(2, vectorPolygon.VertexCount);
}
[TestMethod]
public void Constructor_ShouldThrowArgumentNullException_GivenNullEnumerableOfPointF()
{
IEnumerable<PointF> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => new PolygonF(vertices));
}
[TestMethod]
public void Constructor_ShouldThrowArgumentNullException_GivenNullEnumerableOfVector2()
{
IEnumerable<Vector2> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => new PolygonF(vertices));
}
[TestMethod]
public void CopyConstructor_ShouldCopyVertices_GivenPolygon()
{
var first = PolygonF.Empty;
first.AddVertices(new[] {new PointF(1, 2), new PointF(3, 4)});
var second = new PolygonF(first);
Assert.AreEqual(2, first.VertexCount);
Assert.AreEqual(2, second.VertexCount);
// we cannot use CollectionAssert here for reasons I am not entirely sure of.
// it seems to dislike casting from IReadOnlyList<Point> to ICollection. but okay.
Assert.IsTrue(first.Vertices.SequenceEqual(second.Vertices));
// assert that the empty polygon was not modified
Assert.AreEqual(0, PolygonF.Empty.VertexCount);
}
[TestMethod]
public void CopyConstructor_ShouldThrowArgumentNullException_GivenNullPolygonF()
{
PolygonF polygon = null!;
Assert.ThrowsException<ArgumentNullException>(() => new PolygonF(polygon));
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoEmptyPolygons()
{
var first = PolygonF.Empty;
var second = PolygonF.Empty;
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoHexagons()
{
PolygonF first = CreateHexagon();
PolygonF second = CreateHexagon();
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenHexagonAndEmptyPolygon()
{
PolygonF first = CreateHexagon();
PolygonF second = PolygonF.Empty;
Assert.AreNotEqual(first, second);
Assert.AreNotEqual(second, first);
Assert.IsFalse(first.Equals(second));
Assert.IsFalse(second.Equals(first));
Assert.IsFalse(first == second);
Assert.IsFalse(second == first);
Assert.IsTrue(first != second);
Assert.IsTrue(second != first);
}
[TestMethod]
public void FromPolygon_ShouldThrowArgumentNullException_GivenNullPolygonF()
{
Assert.ThrowsException<ArgumentNullException>(() => PolygonF.FromPolygon(null!));
}
[TestMethod]
public void IsConvex_ShouldBeFalse_GivenEmptyPolygon()
{
Assert.IsFalse(PolygonF.Empty.IsConvex);
}
[TestMethod]
public void IsConvex_ShouldBeTrue_GivenHexagon()
{
Assert.IsTrue(CreateHexagon().IsConvex);
}
[TestMethod]
public void IsConvex_ShouldBeFalse_GivenConcavePolygon()
{
Assert.IsFalse(CreateConcavePolygon().IsConvex);
}
[TestMethod]
public void op_Explicit_ShouldReturnEquivalentCircle_GivenCircle()
{
PolygonF polygon = CreateHexagon();
Polygon converted = (Polygon)polygon;
Assert.AreEqual(polygon, converted);
Assert.AreEqual(polygon.IsConvex, converted.IsConvex);
Assert.AreEqual(polygon.VertexCount, converted.VertexCount);
Assert.IsTrue(polygon.Vertices.SequenceEqual(converted.Vertices.Select(p => (PointF)p)));
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentCircle_GivenCircle()
{
Polygon polygon = PolygonTests.CreateHexagon();
PolygonF converted = polygon;
Assert.AreEqual(polygon, converted);
Assert.AreEqual(polygon.IsConvex, converted.IsConvex);
Assert.AreEqual(polygon.VertexCount, converted.VertexCount);
Assert.IsTrue(converted.Vertices.SequenceEqual(polygon.Vertices.Select(p => (PointF)p)));
}
[TestMethod]
public void PointCount_ShouldBe1_GivenPolygonFWith1Point()
{
var polygon = new PolygonF();
polygon.AddVertex(new Point(1, 1));
Assert.AreEqual(1, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, PolygonF.Empty.VertexCount);
}
[TestMethod]
public void PointCount_ShouldBe0_GivenEmptyPolygon()
{
Assert.AreEqual(0, PolygonF.Empty.VertexCount);
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = PolygonF.Empty.GetHashCode();
Assert.AreEqual(hashCode, PolygonF.Empty.GetHashCode());
}
internal static PolygonF CreateHexagon()
{
var hexagon = new PolygonF();
hexagon.AddVertex(new Vector2(0, 0));
hexagon.AddVertex(new Vector2(1, 0));
hexagon.AddVertex(new Vector2(1, 1));
hexagon.AddVertex(new Vector2(0, 1));
hexagon.AddVertex(new Vector2(-1, 1));
hexagon.AddVertex(new Vector2(-1, 0));
return hexagon;
}
internal static PolygonF CreateConcavePolygon()
{
var hexagon = new PolygonF();
hexagon.AddVertex(new Vector2(0, 0));
hexagon.AddVertex(new Vector2(2, 0));
hexagon.AddVertex(new Vector2(1, 1));
hexagon.AddVertex(new Vector2(2, 1));
hexagon.AddVertex(new Vector2(0, 1));
return hexagon;
}
}

View File

@ -0,0 +1,228 @@
using System.Drawing;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class PolygonTests
{
[TestMethod]
public void AddVertices_ShouldAddVertices()
{
var polygon = Polygon.Empty;
polygon.AddVertices(new[] {new Point(1, 2), new Point(3, 4)});
Assert.AreEqual(2, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, Polygon.Empty.VertexCount);
}
[TestMethod]
public void AddVertices_ShouldThrowArgumentNullException_GivenNullEnumerable()
{
var polygon = Polygon.Empty;
IEnumerable<Point> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => polygon.AddVertices(vertices));
}
[TestMethod]
public void ClearVertices_ShouldClearVertices()
{
var polygon = Polygon.Empty;
polygon.AddVertices(new[] {new Point(1, 2), new Point(3, 4)});
Assert.AreEqual(2, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, Polygon.Empty.VertexCount);
polygon.ClearVertices();
Assert.AreEqual(0, polygon.VertexCount);
}
[TestMethod]
public void Constructor_ShouldPopulateVertices_GivenPolygon()
{
var pointPolygon = new Polygon(new[] {new Point(1, 2), new Point(3, 4)});
Assert.AreEqual(2, pointPolygon.VertexCount);
}
[TestMethod]
public void Constructor_ShouldThrowArgumentNullException_GivenNullEnumerableOfPoint()
{
IEnumerable<Point> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => new Polygon(vertices));
}
[TestMethod]
public void CopyConstructor_ShouldCopyVertices_GivenPolygon()
{
var first = Polygon.Empty;
first.AddVertices(new[] {new Point(1, 2), new Point(3, 4)});
var second = new Polygon(first);
Assert.AreEqual(2, first.VertexCount);
Assert.AreEqual(2, second.VertexCount);
// we cannot use CollectionAssert here for reasons I am not entirely sure of.
// it seems to dislike casting from IReadOnlyList<Point> to ICollection. but okay.
Assert.IsTrue(first.Vertices.SequenceEqual(second.Vertices));
// assert that the empty polygon was not modified
Assert.AreEqual(0, Polygon.Empty.VertexCount);
}
[TestMethod]
public void CopyConstructor_ShouldThrowArgumentNullException_GivenNullPolygon()
{
Polygon polygon = null!;
Assert.ThrowsException<ArgumentNullException>(() => new Polygon(polygon));
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoEmptyPolygons()
{
var first = Polygon.Empty;
var second = Polygon.Empty;
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoHexagons()
{
Polygon first = CreateHexagon();
Polygon second = CreateHexagon();
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenHexagonAndEmptyPolygon()
{
Polygon first = CreateHexagon();
Polygon second = Polygon.Empty;
Assert.AreNotEqual(first, second);
Assert.AreNotEqual(second, first);
Assert.IsFalse(first.Equals(second));
Assert.IsFalse(second.Equals(first));
Assert.IsFalse(first == second);
Assert.IsFalse(second == first);
Assert.IsTrue(first != second);
Assert.IsTrue(second != first);
}
[TestMethod]
public void FromPolygonF_ShouldReturnEquivalentPolygon_GivenPolygon()
{
PolygonF hexagon = CreateHexagonF();
Polygon expected = CreateHexagon();
Polygon actual = Polygon.FromPolygonF(hexagon);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void FromPolygonF_ShouldThrowArgumentNullException_GivenNullPolygon()
{
Assert.ThrowsException<ArgumentNullException>(() => Polygon.FromPolygonF(null!));
}
[TestMethod]
public void IsConvex_ShouldBeFalse_GivenEmptyPolygon()
{
Assert.IsFalse(Polygon.Empty.IsConvex);
}
[TestMethod]
public void IsConvex_ShouldBeTrue_GivenHexagon()
{
Assert.IsTrue(CreateHexagon().IsConvex);
}
[TestMethod]
public void IsConvex_ShouldBeFalse_GivenConcavePolygon()
{
Assert.IsFalse(CreateConcavePolygon().IsConvex);
}
[TestMethod]
public void PointCount_ShouldBe1_GivenPolygonWith1Point()
{
var polygon = Polygon.Empty;
polygon.AddVertex(new Point(1, 1));
Assert.AreEqual(1, polygon.VertexCount);
// assert that the empty polygon was not modified
Assert.AreEqual(0, Polygon.Empty.VertexCount);
}
[TestMethod]
public void PointCount_ShouldBe0_GivenEmptyPolygon()
{
Assert.AreEqual(0, Polygon.Empty.VertexCount);
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Polygon.Empty.GetHashCode();
Assert.AreEqual(hashCode, Polygon.Empty.GetHashCode());
}
internal static Polygon CreateHexagon()
{
var hexagon = new Polygon();
hexagon.AddVertex(new Point(0, 0));
hexagon.AddVertex(new Point(1, 0));
hexagon.AddVertex(new Point(1, 1));
hexagon.AddVertex(new Point(0, 1));
hexagon.AddVertex(new Point(-1, 1));
hexagon.AddVertex(new Point(-1, 0));
return hexagon;
}
internal static PolygonF CreateHexagonF()
{
var hexagon = new PolygonF();
hexagon.AddVertex(new PointF(0, 0));
hexagon.AddVertex(new PointF(1, 0));
hexagon.AddVertex(new PointF(1, 1));
hexagon.AddVertex(new PointF(0, 1));
hexagon.AddVertex(new PointF(-1, 1));
hexagon.AddVertex(new PointF(-1, 0));
return hexagon;
}
internal static Polygon CreateConcavePolygon()
{
var hexagon = new Polygon();
hexagon.AddVertex(new Point(0, 0));
hexagon.AddVertex(new Point(2, 0));
hexagon.AddVertex(new Point(1, 1));
hexagon.AddVertex(new Point(2, 1));
hexagon.AddVertex(new Point(0, 1));
return hexagon;
}
}

View File

@ -0,0 +1,215 @@
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class PolyhedronTests
{
[TestMethod]
public void AddVertices_ShouldAddVertices()
{
var polyhedron = Polyhedron.Empty;
polyhedron.AddVertices(new[] {new Vector3(1, 2, 3), new Vector3(4, 5, 6)});
Assert.AreEqual(2, polyhedron.VertexCount);
// assert that the empty polyhedron was not modified
Assert.AreEqual(0, Polyhedron.Empty.VertexCount);
}
[TestMethod]
public void AddVertices_ShouldThrowArgumentNullException_GivenNullEnumerableOfVector3()
{
var polygon = Polyhedron.Empty;
IEnumerable<Vector3> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => polygon.AddVertices(vertices));
}
[TestMethod]
public void ClearVertices_ShouldClearVertices()
{
var polyhedron = Polyhedron.Empty;
polyhedron.AddVertices(new[] {new Vector3(1, 2, 3), new Vector3(4, 5, 6)});
Assert.AreEqual(2, polyhedron.VertexCount);
// assert that the empty polyhedron was not modified
Assert.AreEqual(0, Polyhedron.Empty.VertexCount);
polyhedron.ClearVertices();
Assert.AreEqual(0, polyhedron.VertexCount);
}
[TestMethod]
public void Constructor_ShouldPopulateVertices_GivenPolyhedron()
{
var polyhedron = new Polyhedron(new[] {new Vector3(1, 2, 3), new Vector3(4, 5, 6)});
Assert.AreEqual(2, polyhedron.VertexCount);
}
[TestMethod]
public void Constructor_ShouldThrowArgumentNullException_GivenNullEnumerableOfVector3()
{
IEnumerable<Vector3> vertices = null!;
Assert.ThrowsException<ArgumentNullException>(() => new Polyhedron(vertices));
}
[TestMethod]
public void CopyConstructor_ShouldCopyVertices_GivenPolyhedron()
{
var first = Polyhedron.Empty;
first.AddVertices(new[] {new Vector3(1, 2, 3), new Vector3(4, 5, 6)});
var second = new Polyhedron(first);
Assert.AreEqual(2, first.VertexCount);
Assert.AreEqual(2, second.VertexCount);
// we cannot use CollectionAssert here for reasons I am not entirely sure of.
// it seems to dislike casting from IReadOnlyList<Point> to ICollection. but okay.
Assert.IsTrue(first.Vertices.SequenceEqual(second.Vertices));
// assert that the empty polyhedron was not modified
Assert.AreEqual(0, Polyhedron.Empty.VertexCount);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoEmptyPolyhedrons()
{
var first = Polyhedron.Empty;
var second = Polyhedron.Empty;
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoHexagons()
{
Polyhedron first = CreateHexagon();
Polyhedron second = CreateHexagon();
Assert.AreEqual(first, second);
Assert.AreEqual(second, first);
Assert.IsTrue(first.Equals(second));
Assert.IsTrue(second.Equals(first));
Assert.IsTrue(first == second);
Assert.IsTrue(second == first);
Assert.IsFalse(first != second);
Assert.IsFalse(second != first);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenHexagonAndEmptyPolyhedron()
{
Polyhedron first = CreateHexagon();
Polyhedron second = Polyhedron.Empty;
Assert.AreNotEqual(first, second);
Assert.AreNotEqual(second, first);
Assert.IsFalse(first.Equals(second));
Assert.IsFalse(second.Equals(first));
Assert.IsFalse(first == second);
Assert.IsFalse(second == first);
Assert.IsTrue(first != second);
Assert.IsTrue(second != first);
}
[TestMethod]
public void FromPolygon_ShouldThrowArgumentNullException_GivenNullPolygonF()
{
Assert.ThrowsException<ArgumentNullException>(() => Polyhedron.FromPolygon(null!));
}
[TestMethod]
public void FromPolygonF_ShouldThrowArgumentNullException_GivenNullPolygonF()
{
Assert.ThrowsException<ArgumentNullException>(() => Polyhedron.FromPolygonF(null!));
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentPolyhedron_GivenPolyhedron()
{
Polygon polygon = PolygonTests.CreateHexagon();
Polyhedron converted = polygon;
Assert.AreEqual(polygon, converted);
Assert.AreEqual(polygon.VertexCount, converted.VertexCount);
Assert.IsTrue(converted.Vertices.SequenceEqual(polygon.Vertices.Select(p =>
{
var point = p.ToVector2();
return new Vector3(point.X, point.Y, 0);
})));
}
[TestMethod]
public void op_Implicit_ShouldReturnEquivalentPolyhedron_GivenPolyhedronF()
{
PolygonF polygon = PolygonFTests.CreateHexagon();
Polyhedron converted = polygon;
Assert.AreEqual(polygon, converted);
Assert.AreEqual(polygon.VertexCount, converted.VertexCount);
Assert.IsTrue(converted.Vertices.SequenceEqual(polygon.Vertices.Select(v =>
{
var point = v.ToVector2();
return new Vector3(point.X, point.Y, 0);
})));
}
[TestMethod]
public void PointCount_ShouldBe1_GivenPolyhedronWith1Point()
{
var polyhedron = new Polyhedron();
polyhedron.AddVertex(Vector3.One);
Assert.AreEqual(1, polyhedron.VertexCount);
// assert that the empty polyhedron was not modified
Assert.AreEqual(0, Polyhedron.Empty.VertexCount);
}
[TestMethod]
public void PointCount_ShouldBe0_GivenEmptyPolyhedron()
{
Assert.AreEqual(0, Polyhedron.Empty.VertexCount);
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Polyhedron.Empty.GetHashCode();
Assert.AreEqual(hashCode, Polyhedron.Empty.GetHashCode());
}
internal static Polyhedron CreateHexagon()
{
var hexagon = new Polyhedron();
hexagon.AddVertex(new Vector3(0, 0, 0));
hexagon.AddVertex(new Vector3(1, 0, 0));
hexagon.AddVertex(new Vector3(1, 1, 0));
hexagon.AddVertex(new Vector3(0, 1, 0));
hexagon.AddVertex(new Vector3(-1, 1, 0));
hexagon.AddVertex(new Vector3(-1, 0, 0));
return hexagon;
}
internal static Polyhedron CreateConcavePolyhedron()
{
var hexagon = new Polyhedron();
hexagon.AddVertex(new Vector3(0, 0, 0));
hexagon.AddVertex(new Vector3(2, 0, 0));
hexagon.AddVertex(new Vector3(2, 1, 0));
hexagon.AddVertex(new Vector3(2, 1, 0));
hexagon.AddVertex(new Vector3(0, 1, 0));
return hexagon;
}
}

View File

@ -0,0 +1,42 @@
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class SizeTests
{
[TestMethod]
public void ToPoint_ShouldReturnPoint_WithEquivalentMembers()
{
var random = new Random();
var size = new Size(random.Next(), random.Next());
var point = size.ToPoint();
Assert.AreEqual(size.Width, point.X);
Assert.AreEqual(size.Height, point.Y);
}
[TestMethod]
public void ToPointF_ShouldReturnPoint_WithEquivalentMembers()
{
var random = new Random();
var size = new Size(random.Next(), random.Next());
var point = size.ToPointF();
Assert.AreEqual(size.Width, point.X, 1e-6f);
Assert.AreEqual(size.Height, point.Y, 1e-6f);
}
[TestMethod]
public void ToVector2_ShouldReturnVector_WithEquivalentMembers()
{
var random = new Random();
var point = new Size(random.Next(), random.Next());
var size = point.ToVector2();
Assert.AreEqual(point.Width, size.X, 1e-6f);
Assert.AreEqual(point.Height, size.Y, 1e-6f);
}
}

View File

@ -0,0 +1,135 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Drawing;
namespace X10D.Tests.Drawing;
[TestClass]
public class SphereTests
{
[TestMethod]
public void Circumference_ShouldBe2PiRadius_GivenUnitCircle()
{
var unitSphere = Sphere.Unit;
Assert.AreEqual(2.0f * MathF.PI * unitSphere.Radius, unitSphere.Circumference, 1e-6f);
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(-1, Sphere.Empty.CompareTo(Sphere.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenUnitCircleAndEmpty()
{
Assert.AreEqual(1, Sphere.Unit.CompareTo(Sphere.Empty));
}
[TestMethod]
public void CompareTo_ShouldBeNegativeOne_GivenEmptyCircleAndUnitCircleAsObject()
{
Assert.AreEqual(-1, Sphere.Empty.CompareTo((object)Sphere.Unit));
}
[TestMethod]
public void CompareTo_ShouldBeOne_GivenNull()
{
Assert.AreEqual(1, Sphere.Unit.CompareTo(null));
}
[TestMethod]
public void CompareTo_ShouldBeZero_GivenUnitCircle()
{
var unitCircle = Sphere.Unit;
Assert.AreEqual(0, unitCircle.CompareTo(unitCircle));
}
[TestMethod]
public void CompareTo_ShouldThrowArgumentException_GivenInvalidType()
{
Assert.ThrowsException<ArgumentException>(() => Sphere.Unit.CompareTo(new object()));
}
[TestMethod]
public void Diameter_ShouldBe2_GivenUnitSphere()
{
Assert.AreEqual(2.0f, Sphere.Unit.Diameter, 1e-6f);
}
[TestMethod]
public void Equals_ShouldBeTrue_GivenTwoUnitCircles()
{
var unitCircle1 = Sphere.Unit;
var unitCircle2 = Sphere.Unit;
Assert.AreEqual(unitCircle1, unitCircle2);
Assert.IsTrue(unitCircle1 == unitCircle2);
Assert.IsFalse(unitCircle1 != unitCircle2);
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentObjects()
{
Assert.IsFalse(Sphere.Unit.Equals(null));
}
[TestMethod]
public void Equals_ShouldBeFalse_GivenDifferentCircles()
{
Assert.AreNotEqual(Sphere.Unit, Sphere.Empty);
Assert.IsFalse(Sphere.Unit == Sphere.Empty);
Assert.IsTrue(Sphere.Unit != Sphere.Empty);
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenEmptyCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Sphere.Empty.GetHashCode();
Assert.AreEqual(hashCode, Sphere.Empty.GetHashCode());
}
[TestMethod]
public void GetHashCode_ShouldBeCorrect_GivenUnitCircle()
{
// this test is pretty pointless, it exists only for code coverage purposes
int hashCode = Sphere.Unit.GetHashCode();
Assert.AreEqual(hashCode, Sphere.Unit.GetHashCode());
}
[TestMethod]
public void op_GreaterThan_True_GivenUnitAndEmptyCircle()
{
Assert.IsTrue(Sphere.Unit > Sphere.Empty);
Assert.IsTrue(Sphere.Unit >= Sphere.Empty);
Assert.IsFalse(Sphere.Unit < Sphere.Empty);
Assert.IsFalse(Sphere.Unit <= Sphere.Empty);
}
[TestMethod]
public void op_LessThan_True_GivenEmptyAndUnitCircle()
{
Assert.IsTrue(Sphere.Empty < Sphere.Unit);
Assert.IsTrue(Sphere.Empty <= Sphere.Unit);
Assert.IsFalse(Sphere.Empty > Sphere.Unit);
Assert.IsFalse(Sphere.Empty >= Sphere.Unit);
}
[TestMethod]
public void Radius_ShouldBe0_GivenEmptySphere()
{
Assert.AreEqual(0, Sphere.Empty.Radius);
}
[TestMethod]
public void Radius_ShouldBe1_GivenUnitSphere()
{
Assert.AreEqual(1, Sphere.Unit.Radius);
}
[TestMethod]
public void Volume_ShouldBe4Over3TimesPi_GivenUnitCircle()
{
var unitSphere = Sphere.Unit;
Assert.AreEqual(4.0f / 3.0f * MathF.PI, unitSphere.Volume);
}
}

View File

@ -0,0 +1,59 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Hosting.DependencyInjection;
namespace X10D.Tests.Hosting;
[TestClass]
public class ServiceCollectionTests
{
[TestMethod]
public void AddHostedSingleton_ShouldRegisterServiceAsSingletonAndAsHostedService()
{
var services = new ServiceCollection();
services.AddHostedSingleton<TestService>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<TestService>();
var hostedService = serviceProvider.GetService<IHostedService>();
Assert.IsNotNull(service);
Assert.IsNotNull(hostedService);
Assert.IsInstanceOfType(service, typeof(TestService));
Assert.IsInstanceOfType(hostedService, typeof(TestService));
Assert.AreSame(service, hostedService);
}
[TestMethod]
public void AddHostedSingleton_ShouldRegisterServiceTypeAsSingletonAndAsHostedService()
{
var services = new ServiceCollection();
services.AddHostedSingleton(typeof(TestService));
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<TestService>();
var hostedService = serviceProvider.GetService<IHostedService>();
Assert.IsNotNull(service);
Assert.IsNotNull(hostedService);
Assert.IsInstanceOfType(service, typeof(TestService));
Assert.IsInstanceOfType(hostedService, typeof(TestService));
Assert.AreSame(service, hostedService);
}
private sealed class TestService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
[TestClass]
public class DirectoryInfoTests
{
[TestMethod]
public void Clear_ShouldClear_GivenValidDirectory()
{
string tempDirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var tempDirectory = new DirectoryInfo(tempDirectoryPath);
tempDirectory.Create();
Assert.IsTrue(tempDirectory.Exists);
var file = new FileInfo(Path.Combine(tempDirectory.FullName, "file"));
file.Create().Close();
var childDirectory = new DirectoryInfo(Path.Combine(tempDirectory.FullName, "child"));
childDirectory.Create();
var childFile = new FileInfo(Path.Combine(childDirectory.FullName, "childFile"));
childFile.Create().Close();
Assert.AreEqual(1, tempDirectory.GetFiles().Length);
Assert.AreEqual(1, tempDirectory.GetDirectories().Length);
tempDirectory.Clear();
Assert.AreEqual(0, tempDirectory.GetFiles().Length);
Assert.AreEqual(0, tempDirectory.GetDirectories().Length);
Assert.IsTrue(tempDirectory.Exists);
tempDirectory.Delete();
}
[TestMethod]
public void Clear_ShouldThrowArgumentNullException_GivenNull()
{
Assert.ThrowsException<ArgumentNullException>(() => ((DirectoryInfo?)null)!.Clear());
}
[TestMethod]
public void Clear_ShouldThrowDirectoryNotFoundException_GivenInvalidDirectory()
{
var directory = new DirectoryInfo(@"123:/@12#3");
Assert.ThrowsException<DirectoryNotFoundException>(() => directory.Clear());
}
[TestMethod]
public void Clear_ShouldThrowDirectoryNotFoundException_GivenNonExistentDirectory()
{
var directory = new DirectoryInfo(@"/@12#3");
Assert.IsFalse(directory.Exists);
Assert.ThrowsException<DirectoryNotFoundException>(() => directory.Clear());
}
}

View File

@ -0,0 +1,73 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadDecimal_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadDecimal());
Assert.ThrowsException<ArgumentException>(() => stream.ReadDecimal(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadDecimal(Endianness.BigEndian));
}
[TestMethod]
public void ReadDecimal_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDecimal());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDecimal(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDecimal(Endianness.BigEndian));
}
[TestMethod]
public void ReadDecimal_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadDecimal((Endianness)(-1)));
}
[TestMethod]
public void ReadDecimal_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[]
{
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x68
};
stream.Write(bytes);
stream.Position = 0;
const decimal expected = 420.0m;
decimal actual = stream.ReadDecimal(Endianness.BigEndian);
Assert.AreEqual(16, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadDecimal_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[]
{
0x68, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
};
stream.Write(bytes);
stream.Position = 0;
const decimal expected = 420.0m;
decimal actual = stream.ReadDecimal(Endianness.LittleEndian);
Assert.AreEqual(16, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadDouble_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadDouble());
Assert.ThrowsException<ArgumentException>(() => stream.ReadDouble(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadDouble(Endianness.BigEndian));
}
[TestMethod]
public void ReadDouble_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDouble());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDouble(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadDouble(Endianness.BigEndian));
}
[TestMethod]
public void ReadDouble_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadDouble((Endianness)(-1)));
}
[TestMethod]
public void ReadDouble_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x40, 0x7A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const double expected = 420.0;
double actual = stream.ReadDouble(Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadDouble_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x40};
stream.Write(bytes);
stream.Position = 0;
const double expected = 420.0;
double actual = stream.ReadDouble(Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadInt16_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt16());
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt16(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt16(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt16_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt16(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt16(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt16_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadInt16((Endianness)(-1)));
}
[TestMethod]
public void ReadInt16_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const short expected = 420;
short actual = stream.ReadInt16(Endianness.BigEndian);
Assert.AreEqual(2, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadInt16_ShouldReadLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01};
stream.Write(bytes);
stream.Position = 0;
const short expected = 420;
short actual = stream.ReadInt16(Endianness.LittleEndian);
Assert.AreEqual(2, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadInt32_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt32());
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt32(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt32(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt32_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt32(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt32(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt32_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadInt32((Endianness)(-1)));
}
[TestMethod]
public void ReadInt32_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const int expected = 420;
int actual = stream.ReadInt32(Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadInt32_ShouldReadLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const int expected = 420;
int actual = stream.ReadInt32(Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadInt64_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt64());
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt64(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadInt64(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt64_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt64());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt64(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadInt64(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt64_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadInt64((Endianness)(-1)));
}
[TestMethod]
public void ReadInt64_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const long expected = 420;
long actual = stream.ReadInt64(Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadInt64_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const long expected = 420;
long actual = stream.ReadInt64(Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void ReadSingle_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadSingle());
Assert.ThrowsException<ArgumentException>(() => stream.ReadSingle(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadSingle(Endianness.BigEndian));
}
[TestMethod]
public void ReadSingle_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadSingle());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadSingle(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadSingle(Endianness.BigEndian));
}
[TestMethod]
public void ReadSingle_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadSingle((Endianness)(-1)));
}
[TestMethod]
public void ReadSingle_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x43, 0xD2, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const float expected = 420.0f;
float actual = stream.ReadSingle(Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void ReadSingle_ShouldReadLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0xD2, 0x43};
stream.Write(bytes);
stream.Position = 0;
const float expected = 420.0f;
float actual = stream.ReadSingle(Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,72 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt16());
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt16(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt16(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt16(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt16(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadUInt16((Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const ushort expected = 420;
ushort actual = stream.ReadUInt16(Endianness.BigEndian);
Assert.AreEqual(2, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_ShouldReadLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01};
stream.Write(bytes);
stream.Position = 0;
const ushort expected = 420;
ushort actual = stream.ReadUInt16(Endianness.LittleEndian);
Assert.AreEqual(2, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,72 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt32());
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt32(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt32(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt32(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt32(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadUInt32((Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const uint expected = 420;
uint actual = stream.ReadUInt32(Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_ShouldReadLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const uint expected = 420;
uint actual = stream.ReadUInt32(Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,72 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_ShouldThrowArgumentException_GivenNonReadableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt64());
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt64(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.ReadUInt64(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt64());
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt64(Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.ReadUInt64(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.ReadUInt64((Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_ShouldReadBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xA4};
stream.Write(bytes);
stream.Position = 0;
const ulong expected = 420;
ulong actual = stream.ReadUInt64(Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
ReadOnlySpan<byte> bytes = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
stream.Write(bytes);
stream.Position = 0;
const ulong expected = 420;
ulong actual = stream.ReadUInt64(Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
Assert.AreEqual(expected, actual);
}
}

View File

@ -0,0 +1,77 @@
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteDecimal_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0m, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0m, Endianness.BigEndian));
}
[TestMethod]
public void WriteDecimal_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0m, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0m, Endianness.BigEndian));
}
[TestMethod]
public void WriteDecimal_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0m, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0m, (Endianness)(-1)));
}
[TestMethod]
public void WriteDecimal_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0m, Endianness.BigEndian);
Assert.AreEqual(16, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[16];
ReadOnlySpan<byte> expected = stackalloc byte[]
{
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x68
};
int read = stream.Read(actual);
Assert.AreEqual(16, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteDecimal_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0m, Endianness.LittleEndian);
Assert.AreEqual(16, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[16];
ReadOnlySpan<byte> expected = stackalloc byte[]
{
0x68, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
};
int read = stream.Read(actual);
Trace.WriteLine(string.Join(", ", actual.ToArray().Select(b => $"0x{b:X2}")));
Assert.AreEqual(16, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteDouble_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0, Endianness.BigEndian));
}
[TestMethod]
public void WriteDouble_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0, Endianness.BigEndian));
}
[TestMethod]
public void WriteDouble_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0, (Endianness)(-1)));
}
[TestMethod]
public void WriteDouble_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0, Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x40, 0x7A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteDouble_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0, Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x40};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteInt16_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write((short)420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write((short)420, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt16_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write((short)420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write((short)420, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt16_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write((short)420, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write((short)420, (Endianness)(-1)));
}
[TestMethod]
public void WriteInt16_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write((short)420, Endianness.BigEndian);
Assert.AreEqual(2, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[2];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(2, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteInt16_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write((short)420, Endianness.LittleEndian);
Assert.AreEqual(2, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[2];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01};
int read = stream.Read(actual);
Assert.AreEqual(2, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteInt32_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt32_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt32_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420, (Endianness)(-1)));
}
[TestMethod]
public void WriteInt32_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420, Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteInt32_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420, Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteInt64_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420L, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420L, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt64_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420L, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420L, Endianness.BigEndian));
}
[TestMethod]
public void WriteInt64_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420L, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420L, (Endianness)(-1)));
}
[TestMethod]
public void WriteInt64_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420L, Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteInt64_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420L, Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
public void WriteSingle_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0f, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420.0f, Endianness.BigEndian));
}
[TestMethod]
public void WriteSingle_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0f, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420.0f, Endianness.BigEndian));
}
[TestMethod]
public void WriteSingle_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0f, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420.0f, (Endianness)(-1)));
}
[TestMethod]
public void WriteSingle_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0f, Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x43, 0xD2, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
public void WriteSingle_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420.0f, Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0xD2, 0x43};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,73 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt16_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write((ushort)420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write((ushort)420, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt16_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write((ushort)420, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write((ushort)420, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt16_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write((ushort)420, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write((ushort)420, (Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt16_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write((ushort)420, Endianness.BigEndian);
Assert.AreEqual(2, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[2];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(2, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt16_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write((ushort)420, Endianness.LittleEndian);
Assert.AreEqual(2, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[2];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01};
int read = stream.Read(actual);
Assert.AreEqual(2, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,73 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt32_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420U, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420U, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt32_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420U, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420U, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt32_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420U, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420U, (Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt32_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420U, Endianness.BigEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt32_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420U, Endianness.LittleEndian);
Assert.AreEqual(4, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[4];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(4, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -0,0 +1,73 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
public partial class StreamTests
{
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt64_ShouldThrowArgumentException_GivenNonWriteableStream()
{
Stream stream = new DummyStream();
Assert.ThrowsException<ArgumentException>(() => stream.Write(420UL, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentException>(() => stream.Write(420UL, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt64_ShouldThrowArgumentNullException_GivenNullStream()
{
Stream stream = null!;
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420UL, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream.Write(420UL, Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt64_ShouldThrowArgumentOutOfRangeException_GivenInvalidEndiannessValue()
{
// we don't need to enclose this stream in a using declaration, since disposing a
// null stream is meaningless. NullStream.Dispose actually does nothing, anyway.
// that - coupled with the fact that encapsulating the stream in a using declaration causes the
// analyser to trip up and think the stream is disposed by the time the local is captured in
// assertion lambda - means this line is fine as it is. please do not change.
Stream stream = Stream.Null;
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420UL, (Endianness)(-1)));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => stream.Write(420UL, (Endianness)(-1)));
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt64_ShouldWriteBigEndian_GivenBigEndian()
{
using var stream = new MemoryStream();
stream.Write(420UL, Endianness.BigEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xA4};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
[TestMethod]
[CLSCompliant(false)]
public void WriteUInt64_ShouldWriteLittleEndian_GivenLittleEndian()
{
using var stream = new MemoryStream();
stream.Write(420UL, Endianness.LittleEndian);
Assert.AreEqual(8, stream.Position);
stream.Position = 0;
Span<byte> actual = stackalloc byte[8];
ReadOnlySpan<byte> expected = stackalloc byte[] {0xA4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int read = stream.Read(actual);
Assert.AreEqual(8, read);
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
}
}

View File

@ -7,7 +7,7 @@ using X10D.IO;
namespace X10D.Tests.IO; namespace X10D.Tests.IO;
[TestClass] [TestClass]
public class StreamTests public partial class StreamTests
{ {
[TestMethod] [TestMethod]
public void GetHashSha1ShouldBeCorrect() public void GetHashSha1ShouldBeCorrect()
@ -99,341 +99,6 @@ public class StreamTests
Stream.Null.TryWriteHash<HashAlgorithmTestClassNoCreateMethod>(Span<byte>.Empty, out _)); Stream.Null.TryWriteHash<HashAlgorithmTestClassNoCreateMethod>(Span<byte>.Empty, out _));
} }
[TestMethod]
public void Write_ShouldThrow_GivenUndefinedEndianness()
{
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0f, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0m, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write((short)0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0L, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write((ushort)0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0U, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0UL, (Endianness)(-1));
});
}
[TestMethod]
public void Read_ShouldThrow_GivenNullStream()
{
Stream? stream = null;
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadSingle());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadDouble());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadDecimal());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt64());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt64());
}
[TestMethod]
public void Write_ShouldThrow_GivenNullStream()
{
Stream? stream = null;
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0f, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0m, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write((short)0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0L));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write((ushort)0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0U));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0UL));
}
[TestMethod]
public void Read_ShouldThrow_GivenUndefinedEndianness()
{
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadSingle((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadDouble((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadDecimal((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt16((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt32((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt64((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadUInt16((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadUInt32((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadUInt64((Endianness)(-1));
});
}
[TestMethod]
public void ReadDouble_WriteDouble_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420.0, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0, stream.ReadDouble(), 1e-6);
stream.Position = 0;
stream.Write(420.0, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420.0, stream.ReadDouble(Endianness.LittleEndian), 1e-6);
stream.Position = 0;
stream.Write(420.0, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0, stream.ReadDouble(Endianness.BigEndian), 1e-6);
}
[TestMethod]
public void ReadDecimal_WriteSingle_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420.0m, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0m, stream.ReadDecimal());
stream.Position = 0;
stream.Write(420.0m, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420.0m, stream.ReadDecimal(Endianness.LittleEndian));
stream.Position = 0;
stream.Write(420.0m, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0m, stream.ReadDecimal(Endianness.BigEndian));
}
[TestMethod]
public void ReadSingle_WriteSingle_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420.0f, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0f, stream.ReadSingle(), 1e-6f);
stream.Position = 0;
stream.Write(420.0f, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420.0f, stream.ReadSingle(Endianness.LittleEndian), 1e-6f);
stream.Position = 0;
stream.Write(420.0f, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420.0f, stream.ReadSingle(Endianness.BigEndian), 1e-6f);
}
[TestMethod]
public void ReadInt16_WriteInt16_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write((short)420);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt16());
stream.Position = 0;
stream.Write((short)420, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt16(Endianness.LittleEndian));
stream.Position = 0;
stream.Write((short)420, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt16(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt32_WriteInt32_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt32());
stream.Position = 0;
stream.Write(420, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt32(Endianness.LittleEndian));
stream.Position = 0;
stream.Write(420, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420, stream.ReadInt32(Endianness.BigEndian));
}
[TestMethod]
public void ReadInt64_WriteInt64_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420L);
stream.Position = 0;
Assert.AreEqual(420L, stream.ReadInt64());
stream.Position = 0;
stream.Write(420L, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420L, stream.ReadInt64(Endianness.LittleEndian));
stream.Position = 0;
stream.Write(420L, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420L, stream.ReadInt64(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt16_WriteUInt16_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write((ushort)420);
stream.Position = 0;
Assert.AreEqual((ushort)420, stream.ReadUInt16());
stream.Position = 0;
stream.Write((ushort)420, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual((ushort)420, stream.ReadUInt16(Endianness.LittleEndian));
stream.Position = 0;
stream.Write((ushort)420, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual((ushort)420, stream.ReadUInt16(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt32_WriteUInt32_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420U);
stream.Position = 0;
Assert.AreEqual(420U, stream.ReadUInt32());
stream.Position = 0;
stream.Write(420U, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420U, stream.ReadUInt32(Endianness.LittleEndian));
stream.Position = 0;
stream.Write(420U, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420U, stream.ReadUInt32(Endianness.BigEndian));
}
[TestMethod]
[CLSCompliant(false)]
public void ReadUInt64_WriteUInt64_ShouldBeSymmetric()
{
using var stream = new MemoryStream();
stream.Write(420UL);
stream.Position = 0;
Assert.AreEqual(420UL, stream.ReadUInt64());
stream.Position = 0;
stream.Write(420UL, Endianness.LittleEndian);
stream.Position = 0;
Assert.AreEqual(420UL, stream.ReadUInt64(Endianness.LittleEndian));
stream.Position = 0;
stream.Write(420UL, Endianness.BigEndian);
stream.Position = 0;
Assert.AreEqual(420UL, stream.ReadUInt64(Endianness.BigEndian));
}
private class DummyStream : Stream private class DummyStream : Stream
{ {
public DummyStream(bool readable = false) public DummyStream(bool readable = false)
@ -492,17 +157,15 @@ public class StreamTests
protected override void HashCore(byte[] array, int ibStart, int cbSize) protected override void HashCore(byte[] array, int ibStart, int cbSize)
{ {
throw new NotImplementedException();
} }
protected override byte[] HashFinal() protected override byte[] HashFinal()
{ {
throw new NotImplementedException(); return Array.Empty<byte>();
} }
public override void Initialize() public override void Initialize()
{ {
throw new NotImplementedException();
} }
} }
@ -510,17 +173,15 @@ public class StreamTests
{ {
protected override void HashCore(byte[] array, int ibStart, int cbSize) protected override void HashCore(byte[] array, int ibStart, int cbSize)
{ {
throw new NotImplementedException();
} }
protected override byte[] HashFinal() protected override byte[] HashFinal()
{ {
throw new NotImplementedException(); return Array.Empty<byte>();
} }
public override void Initialize() public override void Initialize()
{ {
throw new NotImplementedException();
} }
} }
} }

View File

@ -0,0 +1,83 @@
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.IO;
namespace X10D.Tests.IO;
[TestClass]
public class TextReaderTests
{
[TestMethod]
public void EnumerateLines_ShouldYield10Lines_Given10LineString()
{
using var stream = new MemoryStream();
using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true))
{
for (var index = 0; index < 10; index++)
{
writer.WriteLine(index);
}
}
stream.Position = 0;
using var reader = new StreamReader(stream, Encoding.UTF8);
var lineCount = 0;
foreach (string _ in reader.EnumerateLines())
{
lineCount++;
}
Assert.AreEqual(10, lineCount);
}
[TestMethod]
public async Task EnumerateLinesAsync_ShouldYield10Lines_Given10LineString()
{
using var stream = new MemoryStream();
await using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true))
{
for (var index = 0; index < 10; index++)
{
writer.WriteLine(index);
}
}
stream.Position = 0;
using var reader = new StreamReader(stream, Encoding.UTF8);
var lineCount = 0;
await foreach (string _ in reader.EnumerateLinesAsync().ConfigureAwait(false))
{
lineCount++;
}
Assert.AreEqual(10, lineCount);
}
[TestMethod]
public void EnumerateLines_ShouldThrowArgumentNullException_GivenNullSource()
{
TextReader reader = null!;
Assert.ThrowsException<ArgumentNullException>(() =>
{
foreach (string _ in reader.EnumerateLines())
{
// loop body is intentionally empty
}
});
}
[TestMethod]
public async Task EnumerateLinesAsync_ShouldThrowArgumentNullException_GivenNullSource()
{
TextReader reader = null!;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () =>
{
await foreach (string _ in reader.EnumerateLinesAsync().ConfigureAwait(false))
{
// loop body is intentionally empty
}
}).ConfigureAwait(false);
}
}

View File

@ -34,6 +34,14 @@ public class ByteTests
// Π_(i=1)^n (2i) will overflow at i=4 for byte // Π_(i=1)^n (2i) will overflow at i=4 for byte
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<byte> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
[TestMethod] [TestMethod]
public void RangeTo_Byte_ShouldYieldCorrectValues() public void RangeTo_Byte_ShouldYieldCorrectValues()
{ {

View File

@ -41,4 +41,12 @@ public class DecimalTests
Assert.AreEqual(185794560m, Enumerable.Range(1, 9).Product(Double)); Assert.AreEqual(185794560m, Enumerable.Range(1, 9).Product(Double));
Assert.AreEqual(3715891200m, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200m, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<decimal> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -41,4 +41,12 @@ public class DoubleTests
Assert.AreEqual(185794560.0, Enumerable.Range(1, 9).Product(Double)); Assert.AreEqual(185794560.0, Enumerable.Range(1, 9).Product(Double));
Assert.AreEqual(3715891200.0, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200.0, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<double> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -0,0 +1,239 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Linq;
namespace X10D.Tests.Linq;
[TestClass]
public class EnumerableTests
{
[TestMethod]
public void ConcatOne_ShouldReturnConcatenatedSequence_GivenValidSequenceAndValue()
{
IEnumerable<string> source = new[] {"Hello"};
string[] expected = {"Hello", "World"};
string[] actual = source.ConcatOne("World").ToArray();
Assert.AreEqual(2, actual.Length);
CollectionAssert.AreEqual(expected, actual);
}
[TestMethod]
public void ConcatOne_ShouldReturnSingletonSequence_GivenEmptySequenceAndValidValue()
{
IEnumerable<string> source = Enumerable.Empty<string>();
string[] expected = {"Foobar"};
string[] actual = source.ConcatOne("Foobar").ToArray();
Assert.AreEqual(1, actual.Length);
CollectionAssert.AreEqual(expected, actual);
}
[TestMethod]
public void ConcatOne_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<string>? source = null;
Assert.ThrowsException<ArgumentNullException>(() => source!.ConcatOne("Foobar").ToArray());
}
[TestMethod]
public void MinMax_ShouldReturnCorrectValues_UsingDefaultComparer()
{
IEnumerable<int> source = Enumerable.Range(1, 10);
(int minimum, int maximum) = source.MinMax();
Assert.AreEqual(1, minimum);
Assert.AreEqual(10, maximum);
source = Enumerable.Range(1, 10).ToArray();
(minimum, maximum) = source.MinMax();
Assert.AreEqual(1, minimum);
Assert.AreEqual(10, maximum);
}
[TestMethod]
public void MinMax_ShouldReturnCorrectSelectedValues_UsingDefaultComparer()
{
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
(int minimum, int maximum) = source.MinMax(p => p.Age);
Assert.AreEqual(1, minimum);
Assert.AreEqual(10, maximum);
source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray();
(minimum, maximum) = source.MinMax(p => p.Age);
Assert.AreEqual(1, minimum);
Assert.AreEqual(10, maximum);
}
[TestMethod]
public void MinMax_ShouldReturnOppositeSelectedValues_UsingInverseComparer()
{
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
(int minimum, int maximum) = source.MinMax(p => p.Age, new InverseComparer<int>());
Assert.AreEqual(10, minimum);
Assert.AreEqual(1, maximum);
source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray();
(minimum, maximum) = source.MinMax(p => p.Age, new InverseComparer<int>());
Assert.AreEqual(10, minimum);
Assert.AreEqual(1, maximum);
}
[TestMethod]
public void MinMax_ShouldReturnOppositeValues_UsingInverseComparer()
{
(int minimum, int maximum) = Enumerable.Range(1, 10).MinMax(new InverseComparer<int>());
Assert.AreEqual(10, minimum);
Assert.AreEqual(1, maximum);
(minimum, maximum) = Enumerable.Range(1, 10).ToArray().MinMax(new InverseComparer<int>());
Assert.AreEqual(10, minimum);
Assert.AreEqual(1, maximum);
}
[TestMethod]
public void MinMax_ShouldThrowArgumentNullException_GivenNullSelector()
{
IEnumerable<int> source = Enumerable.Empty<int>();
Assert.ThrowsException<ArgumentNullException>(() => source.MinMax((Func<int, int>)(null!)));
Assert.ThrowsException<ArgumentNullException>(() => source.MinMax((Func<int, int>)(null!), null));
}
[TestMethod]
public void MinMax_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<int>? source = null;
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMax());
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMax(v => v));
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMax(null));
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMax(v => v, null));
}
[TestMethod]
public void MinMax_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() => Enumerable.Empty<int>().MinMax());
Assert.ThrowsException<InvalidOperationException>(() => Array.Empty<int>().MinMax());
Assert.ThrowsException<InvalidOperationException>(() => new List<int>().MinMax());
Assert.ThrowsException<InvalidOperationException>(() => Enumerable.Empty<int>().MinMax(i => i * 2));
Assert.ThrowsException<InvalidOperationException>(() => Array.Empty<int>().MinMax(i => i * 2));
Assert.ThrowsException<InvalidOperationException>(() => new List<int>().MinMax(i => i * 2));
}
[TestMethod]
public void MinMaxBy_ShouldReturnCorrectSelectedValues_UsingDefaultComparer()
{
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
(Person minimum, Person maximum) = source.MinMaxBy(p => p.Age);
Assert.AreEqual(1, minimum.Age);
Assert.AreEqual(10, maximum.Age);
source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray();
(minimum, maximum) = source.MinMaxBy(p => p.Age);
Assert.AreEqual(1, minimum.Age);
Assert.AreEqual(10, maximum.Age);
}
[TestMethod]
public void MinMaxBy_ShouldReturnOppositeSelectedValues_UsingInverseComparer()
{
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
(Person minimum, Person maximum) = source.MinMaxBy(p => p.Age, new InverseComparer<int>());
Assert.AreEqual(10, minimum.Age);
Assert.AreEqual(1, maximum.Age);
source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray();
(minimum, maximum) = source.MinMaxBy(p => p.Age, new InverseComparer<int>());
Assert.AreEqual(10, minimum.Age);
Assert.AreEqual(1, maximum.Age);
}
[TestMethod]
public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSelector()
{
Person[] source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray();
Assert.ThrowsException<ArgumentNullException>(() => source.MinMaxBy((Func<Person, int>)null!));
Assert.ThrowsException<ArgumentNullException>(() => source.MinMaxBy((Func<Person, int>)null!, null));
}
[TestMethod]
public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<Person>? source = null;
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMaxBy(p => p.Age));
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMaxBy(p => p.Age, null));
}
[TestMethod]
public void MinMaxBy_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() =>
{
IEnumerable<Person> source = Enumerable.Empty<Person>();
return source.MinMaxBy(p => p.Age);
});
Assert.ThrowsException<InvalidOperationException>(() =>
{
Person[] source = Array.Empty<Person>();
return source.MinMaxBy(p => p.Age);
});
}
private struct InverseComparer<T> : IComparer<T> where T : IComparable<T>
{
public int Compare(T? x, T? y)
{
if (x is null)
{
return y is null ? 0 : 1;
}
return y is null ? -1 : y.CompareTo(x);
}
}
private struct Person : IComparable<Person>, IComparable
{
public int Age { get; set; }
public static bool operator <(Person left, Person right)
{
return left.CompareTo(right) < 0;
}
public static bool operator >(Person left, Person right)
{
return left.CompareTo(right) > 0;
}
public static bool operator <=(Person left, Person right)
{
return left.CompareTo(right) <= 0;
}
public static bool operator >=(Person left, Person right)
{
return left.CompareTo(right) >= 0;
}
public int CompareTo(Person other)
{
return Age.CompareTo(other.Age);
}
public int CompareTo(object? obj)
{
if (ReferenceEquals(null, obj))
{
return 1;
}
return obj is Person other
? CompareTo(other)
: throw new ArgumentException(ExceptionMessages.ObjectIsNotAValidType);
}
}
}

View File

@ -38,6 +38,13 @@ public class Int16Tests
// Π_(i=1)^n (2i) will overflow at i=6 for short // Π_(i=1)^n (2i) will overflow at i=6 for short
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<short> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
}
[TestMethod] [TestMethod]
public void RangeTo_Int16_ShouldYieldCorrectValues() public void RangeTo_Int16_ShouldYieldCorrectValues()
{ {

View File

@ -41,6 +41,14 @@ public class Int32Tests
// Π_(i=1)^n (2i) will overflow at i=10 for int // Π_(i=1)^n (2i) will overflow at i=10 for int
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<int> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
[TestMethod] [TestMethod]
public void RangeTo_Int32_ShouldYieldCorrectValues() public void RangeTo_Int32_ShouldYieldCorrectValues()
{ {

View File

@ -42,6 +42,14 @@ public class Int64Tests
Assert.AreEqual(3715891200, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<long> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
[TestMethod] [TestMethod]
public void RangeTo_Int64_ShouldYieldCorrectValues() public void RangeTo_Int64_ShouldYieldCorrectValues()
{ {

View File

@ -34,4 +34,12 @@ public class SByteTests
// Π_(i=1)^(n(i*2)) will overflow at i=4 for sbyte // Π_(i=1)^(n(i*2)) will overflow at i=4 for sbyte
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<sbyte> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -41,4 +41,12 @@ public class SingleTests
Assert.AreEqual(185794560f, Enumerable.Range(1, 9).Product(Double)); Assert.AreEqual(185794560f, Enumerable.Range(1, 9).Product(Double));
Assert.AreEqual(3715891200f, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200f, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<float> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -40,4 +40,12 @@ public class UInt16Tests
// Π_(i=1)^n (2i) will overflow at i=7 for ushort // Π_(i=1)^n (2i) will overflow at i=7 for ushort
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<ushort> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -42,4 +42,12 @@ public class UInt32Tests
Assert.AreEqual(185794560U, Enumerable.Range(1, 9).Product(Double)); Assert.AreEqual(185794560U, Enumerable.Range(1, 9).Product(Double));
Assert.AreEqual(3715891200U, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200U, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<uint> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

View File

@ -42,4 +42,12 @@ public class UInt64Tests
Assert.AreEqual(185794560UL, Enumerable.Range(1, 9).Product(Double)); Assert.AreEqual(185794560UL, Enumerable.Range(1, 9).Product(Double));
Assert.AreEqual(3715891200UL, Enumerable.Range(1, 10).Product(Double)); Assert.AreEqual(3715891200UL, Enumerable.Range(1, 10).Product(Double));
} }
[TestMethod]
public void Product_ShouldThrowArgumentNullException_GivenNullSource()
{
IEnumerable<ulong> source = null!;
Assert.ThrowsException<ArgumentNullException>(() => source.Product());
Assert.ThrowsException<ArgumentNullException>(() => source.Product(v => v));
}
} }

Some files were not shown because too many files have changed in this diff Show More