Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
using Daqifi.Core.Communication.Consumers;
using Daqifi.Core.Communication.Messages;
using System.Text;

namespace Daqifi.Core.Tests.Communication.Consumers;

public class CompositeMessageParserTests
{
[Fact]
public void CompositeMessageParser_ParseMessages_WithTextData_ShouldUseTextParser()
{
// Arrange
var parser = new CompositeMessageParser();
var textData = Encoding.UTF8.GetBytes("*IDN?\r\nDAQiFi Device v1.0\r\n");

// Act
var messages = parser.ParseMessages(textData, out var consumedBytes);

// Assert
Assert.Equal(2, messages.Count());
Assert.All(messages, msg => Assert.IsType<string>(msg.Data));
Assert.Equal("*IDN?", messages.First().Data);
Assert.Equal("DAQiFi Device v1.0", messages.Last().Data);
Assert.Equal(textData.Length, consumedBytes);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithBinaryData_ShouldUseProtobufParser()
{
// Arrange
var parser = new CompositeMessageParser();
// Binary data with null bytes should trigger protobuf parsing attempt
var binaryDataWithNulls = new byte[] { 0x00, 0x01, 0x00, 0x02 };

// Act
var messages = parser.ParseMessages(binaryDataWithNulls, out var consumedBytes);

// Assert - Should attempt protobuf parsing due to null bytes
// Even if parsing fails, should not throw exceptions
Assert.True(consumedBytes >= 0);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithNullBytes_ShouldDetectAsBinary()
{
// Arrange
var parser = new CompositeMessageParser();
var dataWithNulls = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64 }; // "Hello\0World"

// Act
var messages = parser.ParseMessages(dataWithNulls, out var consumedBytes);

// Assert - Should try protobuf parser first due to null bytes
// May return empty if not valid protobuf, but should have attempted protobuf parsing
Assert.True(consumedBytes >= 0);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithNoNullBytes_ShouldDetectAsText()
{
// Arrange
var parser = new CompositeMessageParser();
var textOnlyData = Encoding.UTF8.GetBytes("Hello World\r\n");

// Act
var messages = parser.ParseMessages(textOnlyData, out var consumedBytes);

// Assert
Assert.Single(messages);
Assert.IsType<string>(messages.First().Data);
Assert.Equal("Hello World", messages.First().Data);
Assert.Equal(textOnlyData.Length, consumedBytes);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithEmptyData_ShouldReturnEmpty()
{
// Arrange
var parser = new CompositeMessageParser();
var emptyData = new byte[0];

// Act
var messages = parser.ParseMessages(emptyData, out var consumedBytes);

// Assert
Assert.Empty(messages);
Assert.Equal(0, consumedBytes);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithCustomParsers_ShouldUseProvided()
{
// Arrange
var mockTextParser = new LineBasedMessageParser("\n"); // Custom line ending
var mockProtobufParser = new ProtobufMessageParser();
var parser = new CompositeMessageParser(mockTextParser, mockProtobufParser);

var textData = Encoding.UTF8.GetBytes("Line 1\nLine 2\n");

// Act
var messages = parser.ParseMessages(textData, out var consumedBytes);

// Assert
Assert.Equal(2, messages.Count());
Assert.All(messages, msg => Assert.IsType<string>(msg.Data));
Assert.Equal("Line 1", messages.First().Data);
Assert.Equal("Line 2", messages.Last().Data);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithNullTextParser_ShouldStillWork()
{
// Arrange
var parser = new CompositeMessageParser(null, new ProtobufMessageParser());
var textData = Encoding.UTF8.GetBytes("Hello World\r\n");

// Act
var messages = parser.ParseMessages(textData, out var consumedBytes);

// Assert - Should fallback to protobuf parser, which likely won't parse text successfully
// But should not throw an exception
Assert.True(consumedBytes >= 0);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithNullProtobufParser_ShouldStillWork()
{
// Arrange
var parser = new CompositeMessageParser(new LineBasedMessageParser(), null);
var binaryData = new byte[] { 0x00, 0x01, 0x02, 0x03 }; // Binary data with null bytes

// Act
var messages = parser.ParseMessages(binaryData, out var consumedBytes);

// Assert - Should try text parser even for binary data
Assert.True(consumedBytes >= 0);
}

[Fact]
public void CompositeMessageParser_ParseMessages_ReturnsDifferentMessageTypes()
{
// Arrange
var parser = new CompositeMessageParser();

// Test text message
var textData = Encoding.UTF8.GetBytes("Text Message\r\n");
var textMessages = parser.ParseMessages(textData, out _);

// Test binary message (mock binary data)
var binaryData = new byte[] { 0x00, 0x08, 0x01 }; // Mock binary with null bytes
var binaryMessages = parser.ParseMessages(binaryData, out _);

// Assert
if (textMessages.Any())
{
Assert.IsType<string>(textMessages.First().Data);
}

// Binary messages might not parse successfully, but should not throw
Assert.True(binaryMessages.Count() >= 0);
}

[Fact]
public void CompositeMessageParser_ParseMessages_WithMixedScenarios_ShouldHandleGracefully()
{
// Arrange
var parser = new CompositeMessageParser();

// Test various data patterns that might come from real DAQiFi devices
var scenarios = new[]
{
Encoding.UTF8.GetBytes("*IDN?\r\n"), // SCPI command
Encoding.UTF8.GetBytes("SYST:ERR?\r\n"), // SCPI query
new byte[] { 0x0A, 0x04, 0x74, 0x65, 0x73, 0x74 }, // Mock protobuf-like
new byte[] { 0x00, 0x00, 0x00, 0x01 }, // Binary with nulls
Encoding.UTF8.GetBytes(""), // Empty
new byte[] { 0xFF } // Single byte
};

// Act & Assert - Should not throw exceptions for any scenario
foreach (var scenario in scenarios)
{
var exception = Record.Exception(() =>
{
var messages = parser.ParseMessages(scenario, out var consumed);
Assert.True(consumed >= 0);
});

Assert.Null(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using Daqifi.Core.Communication.Consumers;
using Daqifi.Core.Communication.Messages;
using Google.Protobuf;

namespace Daqifi.Core.Tests.Communication.Consumers;

public class ProtobufMessageParserTests
{
[Fact]
public void ProtobufMessageParser_ParseMessages_WithValidProtobuf_ShouldReturnMessage()
{
// Arrange
var parser = new ProtobufMessageParser();
// Create minimal valid protobuf data (empty message)
var data = new byte[] { }; // Empty protobuf message

// Act
var messages = parser.ParseMessages(data, out var consumedBytes);

// Assert - Empty message should parse successfully
if (messages.Any())
{
Assert.IsType<ProtobufMessage>(messages.First());
}
Assert.True(consumedBytes >= 0);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_WithInvalidData_ShouldReturnEmpty()
{
// Arrange
var parser = new ProtobufMessageParser();
var invalidData = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }; // Invalid protobuf data

// Act
var messages = parser.ParseMessages(invalidData, out var consumedBytes);

// Assert
Assert.Empty(messages);
Assert.Equal(0, consumedBytes);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_WithEmptyData_ShouldReturnEmpty()
{
// Arrange
var parser = new ProtobufMessageParser();
var data = new byte[0];

// Act
var messages = parser.ParseMessages(data, out var consumedBytes);

// Assert
Assert.Empty(messages);
Assert.Equal(0, consumedBytes);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_WithNullBytes_ShouldHandleCorrectly()
{
// Arrange - This simulates the actual issue with binary protobuf containing null bytes
var parser = new ProtobufMessageParser();
// Create data with null bytes that would break LineBasedMessageParser
var dataWithNulls = new byte[] { 0x00, 0x01, 0x02, 0x00, 0x03 };

// Act - Parser should handle the null bytes gracefully
var messages = parser.ParseMessages(dataWithNulls, out var consumedBytes);

// Assert - Should not throw exceptions, even if parsing fails
Assert.True(consumedBytes >= 0);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_WithIncompleteData_ShouldReturnEmpty()
{
// Arrange
var parser = new ProtobufMessageParser();
var originalMessage = new DaqifiOutMessage();
var completeData = originalMessage.ToByteArray();
var incompleteData = completeData.Take(completeData.Length / 2).ToArray(); // Half the data

// Act
var messages = parser.ParseMessages(incompleteData, out var consumedBytes);

// Assert - Should not parse incomplete protobuf
Assert.Empty(messages);
Assert.Equal(0, consumedBytes);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_WithMultipleMessages_ShouldParseFirst()
{
// Arrange
var parser = new ProtobufMessageParser();
// Create combined mock protobuf data
var data1 = new byte[] { 0x08, 0x01 }; // Mock protobuf message 1
var data2 = new byte[] { 0x08, 0x02 }; // Mock protobuf message 2
var combinedData = data1.Concat(data2).ToArray();

// Act
var messages = parser.ParseMessages(combinedData, out var consumedBytes);

// Assert - Should attempt to parse and consume some data
Assert.True(consumedBytes >= 0);
}

[Fact]
public void ProtobufMessageParser_ParseMessages_ReturnsCorrectMessageType()
{
// Arrange
var parser = new ProtobufMessageParser();
var data = new byte[] { }; // Empty protobuf

// Act
var messages = parser.ParseMessages(data, out var consumedBytes);

// Assert - If parsing succeeds, should return correct types
if (messages.Any())
{
var message = messages.First();
Assert.IsType<ProtobufMessage>(message);
Assert.IsType<DaqifiOutMessage>(message.Data);
}
Assert.True(consumedBytes >= 0);
}
}
Loading