diff --git a/.github/workflows/coverlet.msbuild b/.github/workflows/coverlet.msbuild new file mode 100644 index 00000000..5f923fc2 --- /dev/null +++ b/.github/workflows/coverlet.msbuild @@ -0,0 +1,3 @@ +dotnet add NetSdrClientAppTests package coverlet.msbuild +dotnet add NetSdrClientAppTests package Microsoft.NET.Test.Sdk +dotnet test NetSdrClientAppTests -c Release /p:CollectCoverage=true /p:CoverletOutput=TestResults/coverage.xml /p:CoverletOutputFormat=opencover diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index e7840696..ed2f0a23 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,83 +1,70 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow helps you trigger a SonarCloud analysis of your code and populates -# GitHub Code Scanning alerts with the vulnerabilities found. -# Free for open source project. - -# 1. Login to SonarCloud.io using your GitHub account - -# 2. Import your project on SonarCloud -# * Add your GitHub organization first, then add your repository as a new project. -# * Please note that many languages are eligible for automatic analysis, -# which means that the analysis will start automatically without the need to set up GitHub Actions. -# * This behavior can be changed in Administration > Analysis Method. -# -# 3. Follow the SonarCloud in-product tutorial -# * a. Copy/paste the Project Key and the Organization Key into the args parameter below -# (You'll find this information in SonarCloud. Click on "Information" at the bottom left) -# -# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN -# (On SonarCloud, click on your avatar on top-right > My account > Security -# or go directly to https://sonarcloud.io/account/security/) - -# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/) -# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9) - -name: SonarCloud analysis - +name: SonarQube on: push: - branches: [ "master" ] + branches: + - master pull_request: - branches: [ "master" ] - workflow_dispatch: - -permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results - + types: [opened, synchronize, reopened] jobs: - sonar-check: - name: Sonar Check - runs-on: windows-latest # безпечно для будь-яких .NET проектів + build: + name: Build and analyze + runs-on: windows-latest steps: + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: 'zulu' # Alternative distribution options are available. - uses: actions/checkout@v4 - with: { fetch-depth: 0 } - - - uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' - - # 1) BEGIN: SonarScanner for .NET - - name: SonarScanner Begin + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarQube Cloud packages + uses: actions/cache@v4 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarQube Cloud scanner + id: cache-sonar-scanner + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarQube Cloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell run: | - dotnet tool install --global dotnet-sonarscanner - echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH - dotnet sonarscanner begin ` - /k:"ppanchen_NetSdrClient" ` - /o:"ppanchen" ` - /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 - 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 + New-Item -Path ${{ runner.temp }}\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path ${{ runner.temp }}\scanner + - name: Build and analyze + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + ${{ runner.temp }}\scanner\dotnet-sonarscanner begin /k:"Yegres546_NetSdrClient" /o:"yegres546-1" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + dotnet build + ${{ runner.temp }}\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + - name: Test with coverage + run: | + dotnet test NetSdrClient.Tests/NetSdrClient.Tests.csproj -c Release \ + --logger "trx;LogFileName=test-results.trx" \ + /p:CollectCoverage=true \ + /p:CoverletOutput=TestResults/ \ + /p:CoverletOutputFormat=opencover \ + /p:Exclude="[nunit.*]*%2c[*.Tests]*%2c[Moq]*%2c[NUnit.*]*" + + - name: Upload test results + uses: actions/upload-artifact@v4 + with: + name: test-coverage-results + path: NetSdrClient.Tests/TestResults/ + retention-days: 30 + shell: pwsh # 3) END: SonarScanner - name: SonarScanner End run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" shell: pwsh + + diff --git a/EchoTcpServer/Program.cs b/EchoTcpServer/Program.cs index 5966c579..7f47378b 100644 --- a/EchoTcpServer/Program.cs +++ b/EchoTcpServer/Program.cs @@ -47,7 +47,7 @@ public async Task StartAsync() Console.WriteLine("Server shutdown."); } - private async Task HandleClientAsync(TcpClient client, CancellationToken token) + private static async Task HandleClientAsync(TcpClient client, CancellationToken token) { using (NetworkStream stream = client.GetStream()) { @@ -112,21 +112,22 @@ public static async Task Main(string[] args) } } - -public class UdpTimedSender : IDisposable +namespace EchoServerNamespace { - private readonly string _host; - private readonly int _port; - private readonly UdpClient _udpClient; - private Timer _timer; - - public UdpTimedSender(string host, int port) + public class UdpTimedSender : IDisposable { - _host = host; - _port = port; - _udpClient = new UdpClient(); - } + private readonly string _host; + private readonly int _port; + private readonly UdpClient _udpClient; + private Timer _timer; + public UdpTimedSender(string host, int port) + { + _host = host; + _port = port; + _udpClient = new UdpClient(); + } + } public void StartSending(int intervalMilliseconds) { if (_timer != null) @@ -170,4 +171,4 @@ public void Dispose() StopSending(); _udpClient.Dispose(); } -} \ No newline at end of file +} diff --git a/NetSdrClient.Tests/NetSdrClient.Tests.csproj b/NetSdrClient.Tests/NetSdrClient.Tests.csproj new file mode 100644 index 00000000..140ede72 --- /dev/null +++ b/NetSdrClient.Tests/NetSdrClient.Tests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + diff --git a/NetSdrClient.Tests/NetworkServiceTests.cs b/NetSdrClient.Tests/NetworkServiceTests.cs new file mode 100644 index 00000000..3d29fd9e --- /dev/null +++ b/NetSdrClient.Tests/NetworkServiceTests.cs @@ -0,0 +1,75 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetSdrClient.Services; + +namespace NetSdrClient.Tests +{ + [TestClass] + public class NetworkServiceTests + { + [TestMethod] + public void NetworkService_Constructor_ShouldInitialize() + { + // Arrange & Act + var service = new NetworkService(); + + // Assert + Assert.IsNotNull(service); + } + + [TestMethod] + public void IsValidIpAddress_WithValidIp_ShouldReturnTrue() + { + // Arrange + var service = new NetworkService(); + var validIp = "192.168.1.1"; + + // Act + var result = service.IsValidIpAddress(validIp); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + public void IsValidIpAddress_WithInvalidIp_ShouldReturnFalse() + { + // Arrange + var service = new NetworkService(); + var invalidIp = "999.999.999.999"; + + // Act + var result = service.IsValidIpAddress(invalidIp); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void IsValidPort_WithValidPort_ShouldReturnTrue() + { + // Arrange + var service = new NetworkService(); + var validPort = 8080; + + // Act + var result = service.IsValidPort(validPort); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + public void IsValidPort_WithInvalidPort_ShouldReturnFalse() + { + // Arrange + var service = new NetworkService(); + var invalidPort = 99999; + + // Act + var result = service.IsValidPort(invalidPort); + + // Assert + Assert.IsFalse(result); + } + } +} diff --git a/NetSdrClient.Tests/ProtocolHandlerTests.cs b/NetSdrClient.Tests/ProtocolHandlerTests.cs new file mode 100644 index 00000000..60b17bc4 --- /dev/null +++ b/NetSdrClient.Tests/ProtocolHandlerTests.cs @@ -0,0 +1,63 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetSdrClient.Services; + +namespace NetSdrClient.Tests +{ + [TestClass] + public class ProtocolHandlerTests + { + [TestMethod] + public void ProtocolHandler_Constructor_ShouldInitialize() + { + // Arrange & Act + var handler = new ProtocolHandler(); + + // Assert + Assert.IsNotNull(handler); + } + + [TestMethod] + public void CreateCommand_ShouldReturnValidCommand() + { + // Arrange + var handler = new ProtocolHandler(); + var expectedCommand = "CONNECT"; + + // Act + var result = handler.CreateCommand(expectedCommand); + + // Assert + Assert.IsNotNull(result); + Assert.IsTrue(result.Contains(expectedCommand)); + } + + [TestMethod] + public void ParseResponse_WithValidData_ShouldReturnParsedResponse() + { + // Arrange + var handler = new ProtocolHandler(); + var testData = "OK:CONNECTED"; + + // Act + var result = handler.ParseResponse(testData); + + // Assert + Assert.IsNotNull(result); + Assert.IsTrue(result.Contains("CONNECTED")); + } + + [TestMethod] + public void ParseResponse_WithNullData_ShouldReturnErrorMessage() + { + // Arrange + var handler = new ProtocolHandler(); + + // Act + var result = handler.ParseResponse(null); + + // Assert + Assert.IsNotNull(result); + Assert.IsTrue(result.Contains("ERROR")); + } + } +} diff --git a/NetSdrClient.Tests/SDRClientTests.cs b/NetSdrClient.Tests/SDRClientTests.cs new file mode 100644 index 00000000..05cb679d --- /dev/null +++ b/NetSdrClient.Tests/SDRClientTests.cs @@ -0,0 +1,97 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetSdrClient.Models; +using NetSdrClient.Services; +using System.Linq; + +namespace NetSdrClient.Tests +{ + [TestClass] + public class SDRClientTests + { + [TestMethod] + public void SDRClient_Constructor_ShouldInitializeDevicesList() + { + // Arrange & Act + var client = new SDRClient(); + + // Assert + Assert.IsNotNull(client.Devices); + Assert.AreEqual(0, client.Devices.Count); + } + + [TestMethod] + public void AddDevice_ShouldAddDeviceToList() + { + // Arrange + var client = new SDRClient(); + var device = new SDRDevice { Id = 1, Name = "Test Device" }; + + // Act + client.AddDevice(device); + + // Assert + Assert.AreEqual(1, client.Devices.Count); + Assert.AreEqual(device, client.Devices[0]); + } + + [TestMethod] + public void RemoveDevice_ShouldRemoveDeviceFromList() + { + // Arrange + var client = new SDRClient(); + var device = new SDRDevice { Id = 1, Name = "Test Device" }; + client.AddDevice(device); + + // Act + var result = client.RemoveDevice(1); + + // Assert + Assert.IsTrue(result); + Assert.AreEqual(0, client.Devices.Count); + } + + [TestMethod] + public void RemoveDevice_WithInvalidId_ShouldReturnFalse() + { + // Arrange + var client = new SDRClient(); + + // Act + var result = client.RemoveDevice(999); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void GetDevice_ShouldReturnCorrectDevice() + { + // Arrange + var client = new SDRClient(); + var device1 = new SDRDevice { Id = 1, Name = "Device 1" }; + var device2 = new SDRDevice { Id = 2, Name = "Device 2" }; + client.AddDevice(device1); + client.AddDevice(device2); + + // Act + var result = client.GetDevice(2); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual("Device 2", result.Name); + } + + [TestMethod] + public void GetDevice_WithInvalidId_ShouldReturnNull() + { + // Arrange + var client = new SDRClient(); + + // Act + var result = client.GetDevice(999); + + // Assert + Assert.IsNull(result); + } + } +} diff --git a/NetSdrClient.Tests/SDRDeviceTests.cs b/NetSdrClient.Tests/SDRDeviceTests.cs new file mode 100644 index 00000000..89295dd1 --- /dev/null +++ b/NetSdrClient.Tests/SDRDeviceTests.cs @@ -0,0 +1,55 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetSdrClient.Models; + +namespace NetSdrClient.Tests +{ + [TestClass] + public class SDRDeviceTests + { + [TestMethod] + public void SDRDevice_Constructor_ShouldInitializeProperties() + { + // Arrange & Act + var device = new SDRDevice(); + + // Assert + Assert.IsNotNull(device); + Assert.AreEqual(0, device.Id); + Assert.IsNull(device.Name); + Assert.IsNull(device.Description); + Assert.IsFalse(device.IsConnected); + } + + [TestMethod] + public void SDRDevice_Properties_ShouldSetAndGetCorrectly() + { + // Arrange + var device = new SDRDevice(); + + // Act + device.Id = 1; + device.Name = "Test Device"; + device.Description = "Test Description"; + device.IsConnected = true; + + // Assert + Assert.AreEqual(1, device.Id); + Assert.AreEqual("Test Device", device.Name); + Assert.AreEqual("Test Description", device.Description); + Assert.IsTrue(device.IsConnected); + } + + [TestMethod] + public void SDRDevice_ToString_ShouldReturnName() + { + // Arrange + var device = new SDRDevice { Name = "Test SDR" }; + + // Act + var result = device.ToString(); + + // Assert + Assert.AreEqual("Test SDR", result); + } + } +} diff --git a/NetSdrClientAppTests/NetSdrClientAppTests.csproj b/NetSdrClientAppTests/NetSdrClientAppTests.csproj index 3cbc46af..e1f5e652 100644 --- a/NetSdrClientAppTests/NetSdrClientAppTests.csproj +++ b/NetSdrClientAppTests/NetSdrClientAppTests.csproj @@ -1,17 +1,18 @@ - net8.0 enable enable - false true - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + @@ -19,11 +20,10 @@ - + - diff --git a/coverlet.msbuild b/coverlet.msbuild new file mode 100644 index 00000000..5f923fc2 --- /dev/null +++ b/coverlet.msbuild @@ -0,0 +1,3 @@ +dotnet add NetSdrClientAppTests package coverlet.msbuild +dotnet add NetSdrClientAppTests package Microsoft.NET.Test.Sdk +dotnet test NetSdrClientAppTests -c Release /p:CollectCoverage=true /p:CoverletOutput=TestResults/coverage.xml /p:CoverletOutputFormat=opencover