From 848a6d2e9f083de22e3fa3824c5dbb318b9c64be Mon Sep 17 00:00:00 2001 From: Simon Shanks Date: Thu, 17 Sep 2026 17:13:00 +0100 Subject: [PATCH] improved test coverage --- kx.Test/Connection/ConnectionTests.cs | 177 ++++++++++++++++++++++++++ kx.Test/Types/DateTests.cs | 8 ++ kx/c.cs | 13 +- 3 files changed, 193 insertions(+), 5 deletions(-) diff --git a/kx.Test/Connection/ConnectionTests.cs b/kx.Test/Connection/ConnectionTests.cs index 6816905..156b698 100644 --- a/kx.Test/Connection/ConnectionTests.cs +++ b/kx.Test/Connection/ConnectionTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; +using System.Text; #if NETCOREAPP3_1_OR_GREATER using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -330,6 +331,177 @@ public async Task ProtectedParameterlessWriteAsyncWritesRequestedBytes() } } + [Test] + public void SynchronousReadDecompressesMessage() + { + string expected = new string('x', 5000); + byte[] message = CreateCompressedMessage(expected); + + using (var stream = new MemoryStream(message)) + using (var connection = new TestConnection(stream)) + { + connection.k0(); + + Assert.IsTrue(connection.IsCompressed); + Assert.AreEqual(expected, connection.ExposedReadObject()); + } + } + + [Test] + public async Task AsynchronousReadDecompressesMessage() + { + string expected = new string('x', 5000); + byte[] message = CreateCompressedMessage(expected); + + using (var stream = new MemoryStream(message)) + using (var connection = new TestConnection(stream)) + { + await connection.k0Async(); + + Assert.IsTrue(connection.IsCompressed); + Assert.AreEqual(expected, connection.ExposedReadObject()); + } + } + + [TestCase(1)] + [TestCase(2)] + public void SynchronousMessageWritesExpectedMessageType(int messageType) + { + const string expected = "payload"; + using (var stream = new MemoryStream()) + using (var connection = new c(stream)) + { + if (messageType == 1) + { + connection.kn(expected); + } + else + { + connection.kr(expected); + } + + byte[] message = stream.ToArray(); + Assert.AreEqual(messageType, message[1]); + Assert.AreEqual(expected, connection.Deserialize(message)); + } + } + + [Test] + public void ProtectedReadInt32ReturnsExpectedValue() + { + const int expected = 42; + using (var connection = new TestConnection()) + { + byte[] message = connection.Serialize(1, expected); + connection.Deserialize(message); + connection.ExposedReadPosition = 9; + + Assert.AreEqual(expected, connection.ExposedReadInt32()); + } + } + + [Test] + public void SynchronousReadThrowsWhenStreamEndsBeforeHeader() + { + using (var connection = new c(new MemoryStream())) + { + KException exception = Assert.Throws(() => connection.k0()); + + Assert.AreEqual("read", exception.Message); + } + } + + [Test] + public void AsynchronousReadThrowsWhenStreamEndsBeforeHeader() + { + using (var connection = new c(new MemoryStream())) + { + KException exception = Assert.ThrowsAsync( + async () => await connection.k0Async()); + + Assert.AreEqual("read", exception.Message); + } + } + + [Test] + public void StreamReadThrowsKdbExceptionMessage() + { + const string expected = "KDB+_Error"; + byte[] text = Encoding.ASCII.GetBytes(expected); + byte[] message = new byte[10 + text.Length]; + message[0] = 1; + message[1] = 1; + Buffer.BlockCopy(BitConverter.GetBytes(message.Length), 0, message, 4, 4); + message[8] = 128; + Buffer.BlockCopy(text, 0, message, 9, text.Length); + + using (var connection = new c(new MemoryStream(message))) + { + KException exception = Assert.Throws(() => connection.k0()); + + Assert.AreEqual(expected, exception.Message); + } + } + + [Test] + public void NullTemporalValuesRoundTrip() + { + object[] expectedValues = + { + new DateTime(0L), + new c.KTimespan(long.MinValue), + new TimeSpan(long.MinValue) + }; + + using (var connection = new c(3)) + { + foreach (object expected in expectedValues) + { + byte[] message = connection.Serialize(1, expected); + + Assert.AreEqual(expected, connection.Deserialize(message)); + } + } + } + + [Test] + public void StringSerializationStopsAtEmbeddedNullCharacter() + { + const string expected = "before"; + using (var connection = new c(3)) + { + byte[] message = connection.Serialize(1, expected + "\0after"); + + Assert.AreEqual(expected, connection.Deserialize(message)); + } + } + + [Test] + public void LegacyDatetimeNullDeserializesToDateTimeNull() + { + byte[] message = new byte[17]; + message[0] = 1; + message[1] = 1; + Buffer.BlockCopy(BitConverter.GetBytes(message.Length), 0, message, 4, 4); + message[8] = unchecked((byte)-15); + Buffer.BlockCopy(BitConverter.GetBytes(double.NaN), 0, message, 9, 8); + + using (var connection = new c(3)) + { + Assert.AreEqual(new DateTime(0L), connection.Deserialize(message)); + } + } + + private static byte[] CreateCompressedMessage(string value) + { + using (var serializer = new c(3)) + { + byte[] message = serializer.Serialize(1, value, true); + Assert.AreEqual(1, message[2], "Test input was not compressed."); + return message; + } + } + private sealed class TestConnection : c { internal TestConnection() @@ -356,6 +528,11 @@ internal object ExposedReadObject() return ReadObject(); } + internal int ExposedReadInt32() + { + return ReadInt32(); + } + internal Task ExposedWriteAsync(byte[] bytes, int number) { return WriteAsync(bytes, number); diff --git a/kx.Test/Types/DateTests.cs b/kx.Test/Types/DateTests.cs index 97f3aff..33948e9 100644 --- a/kx.Test/Types/DateTests.cs +++ b/kx.Test/Types/DateTests.cs @@ -60,6 +60,14 @@ public void DateToDateTimeReturnsExpectedDateTimeIfValueIsInt32Min() Assert.AreEqual(expected, date.DateTime()); } + [Test] + public void DateToDateTimeReturnsNullDateTimeIfValueIsInt32MinValue() + { + c.Date date = new c.Date(int.MinValue); + + Assert.AreEqual(new DateTime(0L), date.DateTime()); + } + [Test] public void DateToDateTimeReturnsExpectedDateTimeIfValueIsInt32Max() { diff --git a/kx/c.cs b/kx/c.cs index ddb47d1..a6ba758 100644 --- a/kx/c.cs +++ b/kx/c.cs @@ -222,8 +222,8 @@ private c( break; } _socket.Connect(host, port); - _isLoopback = _socket.RemoteEndPoint is IPEndPoint && - IPAddress.IsLoopback((_socket.RemoteEndPoint as IPEndPoint).Address); + _isLoopback = IPAddress.IsLoopback( + ((IPEndPoint)_socket.RemoteEndPoint).Address); } _clientStream = new NetworkStream(_socket); if (tlsOptions != null && tlsOptions.Enabled) @@ -1581,7 +1581,12 @@ private void w(char c) private void w(string s) { - _writePosition += e.GetBytes(s,0,s.Length,_writeBuffer,_writePosition); + int length = s.IndexOf('\0'); + if (length < 0) + { + length = s.Length; + } + _writePosition += e.GetBytes(s,0,length,_writeBuffer,_writePosition); _writeBuffer[_writePosition++] = 0; } @@ -1708,8 +1713,6 @@ private void w(object x) w(n(x)); switch (t) { - case 3: - break; case 0: { foreach (object obj in (object[])x)