Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/unit-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Unit testing (Windows / MSBuild)

on:
workflow_dispatch:
push:
branches: ["master"]
pull_request:
branches: ["master"]
schedule:
- cron: "0 0 * * 0" # weekly, Sunday 00:00 UTC

permissions:
contents: read

jobs:
test:
runs-on: windows-latest

env:
SOLUTION_NAME: TechnitiumLibrary.sln
BUILD_CONFIGURATION: Debug

steps:
- uses: actions/checkout@v4

- name: Install .NET 9 SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x

- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1

- name: Restore
run: msbuild ${{ env.SOLUTION_NAME }} /t:Restore

- name: Build
run: msbuild ${{ env.SOLUTION_NAME }} /m /p:Configuration=${{ env.BUILD_CONFIGURATION }}

- name: Test (msbuild)
run: msbuild TechnitiumLibrary.Tests\TechnitiumLibrary.Tests.csproj /t:Test /p:Configuration=${{ env.BUILD_CONFIGURATION }}
Comment thread
zbalkan marked this conversation as resolved.
Outdated
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# TechnitiumLibrary
A library for .net based applications.

## Quality Assurance

[![Unit testing (Windows / MSBuild)](https://github.com/TechnitiumSoftware/TechnitiumLibrary/actions/workflows/unit-testing.yml/badge.svg)](https://github.com/TechnitiumSoftware/TechnitiumLibrary/actions/workflows/unit-testing.yml)
3 changes: 3 additions & 0 deletions TechnitiumLibrary.UnitTests/MSTestSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Linq;
using System.Text;
using TechnitiumLibrary.IO;

namespace TechnitiumLibrary.UnitTests.TechnitiumLibrary.IO
{
[TestClass]
public sealed class BinaryReaderExtensionsTests
{
private static BinaryReader ReaderOf(params byte[] bytes)
{
return new BinaryReader(new MemoryStream(bytes));
}

// -----------------------------------------------
// ReadLength()
// -----------------------------------------------

[TestMethod]
public void ReadLength_ShouldReadSingleByteLengths()
{
// GIVEN
BinaryReader reader = ReaderOf(0x05);

// WHEN
int length = reader.ReadLength();

// THEN
Assert.AreEqual(5, length);
Assert.AreEqual(1, reader.BaseStream.Position);
}

[TestMethod]
public void ReadLength_ShouldReadMultiByteBigEndianLengths()
{
// GIVEN
// 0x82 => 2-byte length follows → value = 0x01 0x2C → 300 decimal
BinaryReader reader = ReaderOf(0x82, 0x01, 0x2C);

// WHEN
int length = reader.ReadLength();

// THEN
Assert.AreEqual(300, length);
Assert.AreEqual(3, reader.BaseStream.Position);
}

[TestMethod]
public void ReadLength_ShouldThrow_WhenLengthPrefixTooLarge()
{
// GIVEN
// lower 7 bits = 0x05, meaning "next 5 bytes", exceeding allowed 4
BinaryReader reader = ReaderOf(0x85);

// WHEN-THEN
Assert.ThrowsExactly<IOException>(() => reader.ReadLength());
}

// -----------------------------------------------
// ReadBuffer()
// -----------------------------------------------

[TestMethod]
public void ReadBuffer_ShouldReturnBytes_WhenLengthPrefixed()
{
// GIVEN
// length=3, then bytes 0xAA, 0xBB, 0xCC
BinaryReader reader = ReaderOf(0x03, 0xAA, 0xBB, 0xCC);

// WHEN
byte[] data = reader.ReadBuffer();

// THEN
Assert.HasCount(3, data);
CollectionAssert.AreEqual(new byte[] { 0xAA, 0xBB, 0xCC }, data);
}

// -----------------------------------------------
// ReadShortString()
// -----------------------------------------------

[TestMethod]
public void ReadShortString_ShouldDecodeUtf8StringCorrectly()
{
// GIVEN
string text = "Hello";
byte[] encoded = Encoding.UTF8.GetBytes(text);

byte[] bytes = new byte[] { (byte)encoded.Length }.Concat(encoded).ToArray();
BinaryReader reader = ReaderOf(bytes);

// WHEN
string result = reader.ReadShortString();

// THEN
Assert.AreEqual(text, result);
}

[TestMethod]
public void ReadShortString_ShouldUseSpecifiedEncoding()
{
// GIVEN
string text = "Å";
Encoding encoding = Encoding.UTF32;
byte[] encoded = encoding.GetBytes(text);

byte[] bytes = new byte[] { (byte)encoded.Length }.Concat(encoded).ToArray();
BinaryReader reader = ReaderOf(bytes);

// WHEN
string result = reader.ReadShortString(encoding);

// THEN
Assert.AreEqual(text, result);
}

// -----------------------------------------------
// ReadDateTime()
// -----------------------------------------------

[TestMethod]
public void ReadDateTime_ShouldConvertEpochMilliseconds()
{
// GIVEN
DateTime expected = new DateTime(2024, 01, 01, 12, 00, 00, DateTimeKind.Utc);
long millis = (long)(expected - DateTime.UnixEpoch).TotalMilliseconds;

byte[] encoded = BitConverter.GetBytes(millis);

// Normalize to little-endian, which BinaryReader expects
if (!BitConverter.IsLittleEndian)
Array.Reverse(encoded);

BinaryReader reader = ReaderOf(encoded);

// WHEN
DateTime result = reader.ReadDateTime();

// THEN
Assert.AreEqual(expected, result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Linq;
using System.Text;
using TechnitiumLibrary.IO;

namespace TechnitiumLibrary.UnitTests.TechnitiumLibrary.IO
{
[TestClass]
public sealed class BinaryWriterExtensionsTests
{
private static (BinaryWriter writer, MemoryStream stream) CreateWriter()
{
MemoryStream ms = new MemoryStream();
Comment thread
zbalkan marked this conversation as resolved.
Outdated
BinaryWriter bw = new BinaryWriter(ms);
Comment thread
zbalkan marked this conversation as resolved.
Outdated
return (bw, ms);
}

private static byte[] WrittenBytes(MemoryStream ms) =>
ms.ToArray();

// ---------------------------------------
// WriteLength() tests
// ---------------------------------------

[TestMethod]
public void WriteLength_ShouldEncodeSingleByte_WhenLessThan128()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();

// WHEN
bw.WriteLength(42);

// THEN
CollectionAssert.AreEqual(new byte[] { 42 }, WrittenBytes(ms));
}

[TestMethod]
public void WriteLength_ShouldEncodeMultiByte_BigEndianForm()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();

// WHEN
// length = 0x0000012C (300 decimal)
bw.WriteLength(300);

// THEN
// Prefix = 0x82 (2 bytes follow)
// Then big-endian 01 2C
CollectionAssert.AreEqual(
new byte[] { 0x82, 0x01, 0x2C },
WrittenBytes(ms)
);
}

// ---------------------------------------
// WriteBuffer()
// ---------------------------------------

[TestMethod]
public void WriteBuffer_ShouldPrefixLength_AndWriteBytes()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();
byte[] data = new byte[] { 0xAA, 0xBB, 0xCC };

// WHEN
bw.WriteBuffer(data);

// THEN
CollectionAssert.AreEqual(
new byte[] { 0x03, 0xAA, 0xBB, 0xCC },
WrittenBytes(ms)
);
}

[TestMethod]
public void WriteBuffer_WithOffset_ShouldWriteExpectedSegment()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();
byte[] data = new byte[] { 1, 2, 3, 4, 5 };

// WHEN
bw.WriteBuffer(data, offset: 1, count: 3);

// THEN
CollectionAssert.AreEqual(
new byte[] { 0x03, 2, 3, 4 },
WrittenBytes(ms)
);
}

// ---------------------------------------
// WriteShortString()
// ---------------------------------------

[TestMethod]
public void WriteShortString_ShouldWriteUtf8EncodedWithLength()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();
string text = "Hello";
byte[] utf8 = Encoding.UTF8.GetBytes(text);

// WHEN
bw.WriteShortString(text);

// THEN
byte[] expected = new byte[] { (byte)utf8.Length }
.Concat(utf8)
.ToArray();

CollectionAssert.AreEqual(expected, WrittenBytes(ms));
}

[TestMethod]
public void WriteShortString_ShouldUseSpecifiedEncoding()
{
// GIVEN
(BinaryWriter bw, MemoryStream ms) = CreateWriter();
string text = "Å";
Encoding enc = Encoding.UTF32;
byte[] bytes = enc.GetBytes(text);

// WHEN
bw.WriteShortString(text, enc);

// THEN
byte[] expected = new byte[] { (byte)bytes.Length }
.Concat(bytes)
.ToArray();

CollectionAssert.AreEqual(expected, WrittenBytes(ms));
}

[TestMethod]
public void WriteShortString_ShouldThrow_WhenStringTooLong()
{
// GIVEN
(BinaryWriter bw, MemoryStream _) = CreateWriter();
string input = new string('x', 256); // UTF-8 => 256 bytes

// WHEN–THEN
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() =>
bw.WriteShortString(input)
);
}

// ---------------------------------------
// Write(DateTime)
// ---------------------------------------

[TestMethod]
public void WriteDate_ShouldEncodeMillisecondsFromUnixEpoch()
{
// GIVEN
DateTime expected = new DateTime(2024, 1, 2, 12, 00, 00, DateTimeKind.Utc);
long millis = (long)(expected - DateTime.UnixEpoch).TotalMilliseconds;

byte[] bytes = BitConverter.GetBytes(millis);
(BinaryWriter bw, MemoryStream ms) = CreateWriter();

// WHEN
bw.Write(expected);

// THEN
CollectionAssert.AreEqual(bytes, WrittenBytes(ms));
}
}
}
Loading
Loading