diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index e784069..db9518c 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -44,6 +44,8 @@ jobs: runs-on: windows-latest # безпечно для будь-яких .NET проектів steps: - uses: actions/checkout@v4 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true with: { fetch-depth: 0 } - uses: actions/setup-dotnet@v4 @@ -56,27 +58,27 @@ jobs: dotnet tool install --global dotnet-sonarscanner echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH dotnet sonarscanner begin ` - /k:"ppanchen_NetSdrClient" ` - /o:"ppanchen" ` + /k:"MinTins_ReengineeringCourse" ` + /o:"roman-flakei" ` /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` /d:sonar.cs.opencover.reportsPaths="**/coverage.xml" ` /d:sonar.cpd.cs.minimumTokens=40 ` /d:sonar.cpd.cs.minimumLines=5 ` /d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml ` - /d:sonar.qualitygate.wait=true + /d:sonar.qualitygate.wait=false shell: pwsh # 2) BUILD & TEST - name: Restore run: dotnet restore NetSdrClient.sln - name: Build run: dotnet build NetSdrClient.sln -c Release --no-restore - #- name: Tests with coverage (OpenCover) - # run: | - # dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build ` - # /p:CollectCoverage=true ` - # /p:CoverletOutput=TestResults/coverage.xml ` - # /p:CoverletOutputFormat=opencover - # shell: pwsh + - name: Tests with coverage (OpenCover) + run: | + dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build ` + /p:CollectCoverage=true ` + /p:CoverletOutput=TestResults/coverage.xml ` + /p:CoverletOutputFormat=opencover + shell: pwsh # 3) END: SonarScanner - name: SonarScanner End run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" diff --git a/EchoTcpServer/EchoServer.csproj b/EchoTcpServer/EchoServer.csproj index 2150e37..ed9781c 100644 --- a/EchoTcpServer/EchoServer.csproj +++ b/EchoTcpServer/EchoServer.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 enable enable diff --git a/EchoTcpServer/Program.cs b/EchoTcpServer/Program.cs index 5966c57..fab6e1f 100644 --- a/EchoTcpServer/Program.cs +++ b/EchoTcpServer/Program.cs @@ -13,7 +13,7 @@ public class EchoServer { private readonly int _port; private TcpListener _listener; - private CancellationTokenSource _cancellationTokenSource; + private readonly CancellationTokenSource _cancellationTokenSource; public EchoServer(int port) diff --git a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs index 0d69b4d..1fb0248 100644 --- a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs +++ b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs @@ -83,7 +83,7 @@ public static bool TranslateMessage(byte[] msg, out MsgTypes type, out ControlIt msgEnumarable = msgEnumarable.Skip(_msgControlItemLength); msgLength -= _msgControlItemLength; - if (Enum.IsDefined(typeof(ControlItemCodes), value)) + if (Enum.IsDefined(typeof(ControlItemCodes), (int)value)) { itemCode = (ControlItemCodes)value; } diff --git a/NetSdrClientApp/NetSdrClient.cs b/NetSdrClientApp/NetSdrClient.cs index b0a7c05..fac1c6a 100644 --- a/NetSdrClientApp/NetSdrClient.cs +++ b/NetSdrClientApp/NetSdrClient.cs @@ -14,8 +14,8 @@ namespace NetSdrClientApp { public class NetSdrClient { - private ITcpClient _tcpClient; - private IUdpClient _udpClient; + private readonly ITcpClient _tcpClient; + private readonly IUdpClient _udpClient; public bool IQStarted { get; set; } @@ -66,7 +66,7 @@ public async Task StartIQAsync() return; } -; var iqDataMode = (byte)0x80; + var iqDataMode = (byte)0x80; var start = (byte)0x02; var fifo16bitCaptureMode = (byte)0x01; var n = (byte)1; @@ -116,7 +116,7 @@ public async Task ChangeFrequencyAsync(long hz, int channel) private void _udpClient_MessageReceived(object? sender, byte[] e) { - NetSdrMessageHelper.TranslateMessage(e, out MsgTypes type, out ControlItemCodes code, out ushort sequenceNum, out byte[] body); + NetSdrMessageHelper.TranslateMessage(e, out _, out _, out _, out byte[] body); var samples = NetSdrMessageHelper.GetSamples(16, body); Console.WriteLine($"Samples recieved: " + body.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}")); diff --git a/NetSdrClientApp/NetSdrClientApp.csproj b/NetSdrClientApp/NetSdrClientApp.csproj index 2ac9100..9cebf4e 100644 --- a/NetSdrClientApp/NetSdrClientApp.csproj +++ b/NetSdrClientApp/NetSdrClientApp.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 enable enable diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs index 1f37e2e..df06f2e 100644 --- a/NetSdrClientApp/Networking/TcpClientWrapper.cs +++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs @@ -7,13 +7,15 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; namespace NetSdrClientApp.Networking { + [ExcludeFromCodeCoverage] public class TcpClientWrapper : ITcpClient { - private string _host; - private int _port; + private readonly string _host; + private readonly int _port; private TcpClient? _tcpClient; private NetworkStream? _stream; private CancellationTokenSource _cts; @@ -117,7 +119,7 @@ private async Task StartListeningAsync() } } } - catch (OperationCanceledException ex) + catch (OperationCanceledException) { //empty } diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs index 31e0b79..9cbd66b 100644 --- a/NetSdrClientApp/Networking/UdpClientWrapper.cs +++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs @@ -5,7 +5,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; +[ExcludeFromCodeCoverage] public class UdpClientWrapper : IUdpClient { private readonly IPEndPoint _localEndPoint; diff --git a/NetSdrClientApp/Program.cs b/NetSdrClientApp/Program.cs index fda2e69..9577a7d 100644 --- a/NetSdrClientApp/Program.cs +++ b/NetSdrClientApp/Program.cs @@ -1,4 +1,7 @@ -using NetSdrClientApp; +// +// Excluded from coverage - entry point only + +using NetSdrClientApp; using NetSdrClientApp.Networking; Console.WriteLine(@"Usage: diff --git a/NetSdrClientAppTests/NetSdrClientAppTests.csproj b/NetSdrClientAppTests/NetSdrClientAppTests.csproj index 3cbc46a..e0b6e01 100644 --- a/NetSdrClientAppTests/NetSdrClientAppTests.csproj +++ b/NetSdrClientAppTests/NetSdrClientAppTests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -11,7 +11,11 @@ - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/NetSdrClientAppTests/NetSdrClientTests.cs b/NetSdrClientAppTests/NetSdrClientTests.cs index ad00c4f..936a525 100644 --- a/NetSdrClientAppTests/NetSdrClientTests.cs +++ b/NetSdrClientAppTests/NetSdrClientTests.cs @@ -115,5 +115,64 @@ public async Task StopIQTest() Assert.That(_client.IQStarted, Is.False); } - //TODO: cover the rest of the NetSdrClient code here + [Test] + public async Task StopIQNoConnectionTest() + { + // act + await _client.StopIQAsync(); + + // assert — без з'єднання StopListening не викликається + _updMock.Verify(udp => udp.StopListening(), Times.Never); + Assert.That(_client.IQStarted, Is.False); + } + + [Test] + public async Task ChangeFrequencyAsyncTest() + { + // Arrange + await ConnectAsyncTest(); + + // Act + await _client.ChangeFrequencyAsync(20000000, 1); + + // Assert + _tcpMock.Verify(tcp => tcp.SendMessageAsync(It.IsAny()), Times.Exactly(4)); // 3 з connect + 1 + } + + [Test] + public async Task ChangeFrequencyNoConnectionTest() + { + // Act + await _client.ChangeFrequencyAsync(20000000, 1); + + // Assert + _tcpMock.Verify(tcp => tcp.SendMessageAsync(It.IsAny()), Times.Never); + } + + [Test] + public async Task StartIQSetsIQStartedTrueTest() + { + // Arrange + await ConnectAsyncTest(); + + // Act + await _client.StartIQAsync(); + + // Assert + Assert.That(_client.IQStarted, Is.True); + } + + [Test] + public async Task StopIQSetsIQStartedFalseTest() + { + // Arrange + await ConnectAsyncTest(); + await _client.StartIQAsync(); + + // Act + await _client.StopIQAsync(); + + // Assert + Assert.That(_client.IQStarted, Is.False); + } } diff --git a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs index b40fff7..dfc34ce 100644 --- a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs +++ b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs @@ -64,6 +64,190 @@ public void GetDataItemMessageTest() Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength)); } - //TODO: add more NetSdrMessageHelper tests + [Test] + public void GetControlItemMessage_LengthIsCorrect() + { + var msg = NetSdrMessageHelper.GetControlItemMessage( + NetSdrMessageHelper.MsgTypes.SetControlItem, + NetSdrMessageHelper.ControlItemCodes.ReceiverFrequency, + new byte[6]); + + Assert.That(msg.Length, Is.EqualTo(10)); // 2 header + 2 code + 6 params + } + + [Test] + public void GetControlItemMessage_TypeIsEncodedCorrectly() + { + var type = NetSdrMessageHelper.MsgTypes.SetControlItem; + var msg = NetSdrMessageHelper.GetControlItemMessage( + type, + NetSdrMessageHelper.ControlItemCodes.ReceiverState, + new byte[4]); + + var num = BitConverter.ToUInt16(msg.Take(2).ToArray()); + var actualType = (NetSdrMessageHelper.MsgTypes)(num >> 13); + + Assert.That(actualType, Is.EqualTo(type)); + } + + [Test] + public void GetDataItemMessage_EmptyParams_ReturnsHeaderOnly() + { + var msg = NetSdrMessageHelper.GetDataItemMessage( + NetSdrMessageHelper.MsgTypes.DataItem0, + new byte[0]); + + Assert.That(msg.Length, Is.EqualTo(2)); // тільки header + } + + [Test] + public void GetDataItemMessage_TypeEncodedCorrectly() + { + var type = NetSdrMessageHelper.MsgTypes.DataItem0; + var msg = NetSdrMessageHelper.GetDataItemMessage(type, new byte[100]); + + var num = BitConverter.ToUInt16(msg.Take(2).ToArray()); + var actualType = (NetSdrMessageHelper.MsgTypes)(num >> 13); + + Assert.That(actualType, Is.EqualTo(type)); + } + + [Test] + public void TranslateMessage_ControlItem_ReturnsCorrectTypeAndCode() + { + // Arrange — побудувати повідомлення і одразу розібрати + var type = NetSdrMessageHelper.MsgTypes.SetControlItem; + var code = NetSdrMessageHelper.ControlItemCodes.ReceiverFrequency; + var parameters = new byte[] { 0x01, 0x02, 0x03 }; + var msg = NetSdrMessageHelper.GetControlItemMessage(type, code, parameters); + + // Act + var success = NetSdrMessageHelper.TranslateMessage(msg, out var outType, out var outCode, out var outSeq, out var body); + + // Assert + Assert.That(success, Is.True); + Assert.That(outType, Is.EqualTo(type)); + Assert.That(outCode, Is.EqualTo(code)); + Assert.That(outSeq, Is.EqualTo(0)); + Assert.That(body, Is.EqualTo(parameters)); + } + + [Test] + public void TranslateMessage_DataItem_ReturnsCorrectTypeAndSequence() + { + // Arrange + var type = NetSdrMessageHelper.MsgTypes.DataItem0; + var parameters = new byte[] { 0x10, 0x20, 0x30, 0x40 }; + var msg = NetSdrMessageHelper.GetDataItemMessage(type, parameters); + + // Act + var success = NetSdrMessageHelper.TranslateMessage(msg, out var outType, out _, out var outSeq, out var body); + + // Assert + Assert.That(success, Is.True); + Assert.That(outType, Is.EqualTo(type)); + Assert.That(body.Length, Is.GreaterThan(0)); + } + + [Test] + public void TranslateMessage_AckType_ParsedCorrectly() + { + var type = NetSdrMessageHelper.MsgTypes.Ack; + var code = NetSdrMessageHelper.ControlItemCodes.ReceiverState; + var msg = NetSdrMessageHelper.GetControlItemMessage(type, code, new byte[2]); + + var success = NetSdrMessageHelper.TranslateMessage(msg, out var outType, out var outCode, out _, out _); + + Assert.That(success, Is.True); + Assert.That(outType, Is.EqualTo(type)); + Assert.That(outCode, Is.EqualTo(code)); + } + + [Test] + public void GetSamples_16bit_ReturnsCorrectCount() + { + // Arrange — 8 байт = 4 семпли по 16 біт + var body = new byte[] { 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00 }; + + // Act + var samples = NetSdrMessageHelper.GetSamples(16, body).ToList(); + + // Assert + Assert.That(samples.Count, Is.EqualTo(4)); + } + + [Test] + public void GetSamples_16bit_ReturnsCorrectValues() + { + var body = new byte[] { 0x05, 0x00, 0x0A, 0x00 }; + + var samples = NetSdrMessageHelper.GetSamples(16, body).ToList(); + + Assert.That(samples[0], Is.EqualTo(5)); + Assert.That(samples[1], Is.EqualTo(10)); + } + + [Test] + public void GetSamples_8bit_ReturnsCorrectCount() + { + var body = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + + var samples = NetSdrMessageHelper.GetSamples(8, body).ToList(); + + Assert.That(samples.Count, Is.EqualTo(4)); + } + + [Test] + public void GetSamples_EmptyBody_ReturnsNoSamples() + { + var samples = NetSdrMessageHelper.GetSamples(16, new byte[0]).ToList(); + + Assert.That(samples.Count, Is.EqualTo(0)); + } + + [Test] + public void GetSamples_InvalidSampleSize_ThrowsException() + { + // sampleSize > 32 біти (4 байти) — має кинути виняток + Assert.Throws(() => + NetSdrMessageHelper.GetSamples(64, new byte[] { 0x01 }).ToList()); + } + + [Test] + public void GetHeader_MessageTooLong_ThrowsArgumentException() + { + // параметри довжиною більше ніж maxMessageLength + var hugeParams = new byte[9000]; + + Assert.Throws(() => + NetSdrMessageHelper.GetControlItemMessage( + NetSdrMessageHelper.MsgTypes.SetControlItem, + NetSdrMessageHelper.ControlItemCodes.ReceiverState, + hugeParams)); + } + + [Test] + public void GetControlItemMessage_ZeroParams_LengthIsHeaderPlusCode() + { + var msg = NetSdrMessageHelper.GetControlItemMessage( + NetSdrMessageHelper.MsgTypes.CurrentControlItem, + NetSdrMessageHelper.ControlItemCodes.ADModes, + new byte[0]); + + // 2 байти header + 2 байти code = 4 + Assert.That(msg.Length, Is.EqualTo(4)); + } + + [Test] + public void GetDataItemMessage_NoneCode_NoCodeBytesInMessage() + { + var parameters = new byte[] { 0xAA, 0xBB }; + var msg = NetSdrMessageHelper.GetDataItemMessage( + NetSdrMessageHelper.MsgTypes.DataItem1, + parameters); + + // 2 байти header + 2 байти parameters = 4 (без code) + Assert.That(msg.Length, Is.EqualTo(4)); + } } } \ No newline at end of file