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..d23eba70 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -1,83 +1,92 @@
-# 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: SonarCloud Analysis
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
- workflow_dispatch:
-
-permissions:
- pull-requests: read # allows SonarCloud to decorate PRs with analysis results
jobs:
- sonar-check:
- name: Sonar Check
- runs-on: windows-latest # безпечно для будь-яких .NET проектів
+ build-and-analyze:
+ name: Build and Analyze
+ runs-on: windows-latest
+
steps:
- - 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
- 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" `
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Install SonarScanner
+ run: |
+ dotnet tool install --global dotnet-sonarscanner
+ echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH
+
+ - name: Install Coverlet
+ run: dotnet tool install --global coverlet.console
+
+ - name: Build and test with SonarCloud
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: |
+ # Start SonarScanner
+ dotnet sonarscanner begin `
+ /k:"Yegres546_NetSdrClient" `
+ /o:"yegres546" `
+ /d:sonar.token="$env:SONAR_TOKEN" `
+ /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" `
/d:sonar.cpd.cs.minimumTokens=40 `
/d:sonar.cpd.cs.minimumLines=5 `
- /d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml `
+ /d:sonar.exclusions="**/bin/**,**/obj/**,**/TestResults/**,**/*.Tests.cs" `
+ /d:sonar.coverage.exclusions="**Test*.cs" `
/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
- # 3) END: SonarScanner
- - name: SonarScanner End
- run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
- shell: pwsh
+
+ # Build the solution
+ dotnet build --configuration Release
+
+ # Run tests with coverage
+ dotnet test --configuration Release `
+ --no-build `
+ --verbosity normal `
+ --collect:"XPlat Code Coverage" `
+ --results-directory ./TestResults `
+ -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
+
+ # End SonarScanner
+ dotnet sonarscanner end /d:sonar.token="$env:SONAR_TOKEN"
+name: Architecture Rules Validation
+
+on:
+ push:
+ branches: [ "lab5" ]
+ pull_request:
+ branches: [ "lab5" ]
+
+jobs:
+ architecture-tests:
+ name: Architecture Rules Validation
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Install dependencies
+ run: dotnet restore
+
+ - name: Build
+ run: dotnet build --no-restore --configuration Release
+
+ - name: Run Architecture Tests
+ run: dotnet test --configuration Release --no-build --verbosity normal --filter "Category=Architecture"
+
+ - name: Run All Tests
+ run: dotnet test --configuration Release --no-build --verbosity normal
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/Infrastructure/DatabaseService.cs b/Infrastructure/DatabaseService.cs
new file mode 100644
index 00000000..2fcf4980
--- /dev/null
+++ b/Infrastructure/DatabaseService.cs
@@ -0,0 +1,10 @@
+namespace NetSdrClient.Infrastructure
+{
+ public class DatabaseService
+ {
+ public void SaveData(object data)
+ {
+ // Database operations
+ }
+ }
+}
diff --git a/Interfaces/IDeviceService.cs b/Interfaces/IDeviceService.cs
new file mode 100644
index 00000000..b19adbd7
--- /dev/null
+++ b/Interfaces/IDeviceService.cs
@@ -0,0 +1,8 @@
+namespace NetSdrClient.Interfaces
+{
+ public interface IDeviceService
+ {
+ void Connect();
+ void Disconnect();
+ }
+}
diff --git a/NetSdrClient.Tests/ArchitectureTests.cs b/NetSdrClient.Tests/ArchitectureTests.cs
new file mode 100644
index 00000000..a664fae4
--- /dev/null
+++ b/NetSdrClient.Tests/ArchitectureTests.cs
@@ -0,0 +1,148 @@
+using NetArchTest.Rules;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace NetSdrClient.Tests
+{
+ [TestClass]
+ public class ArchitectureTests
+ {
+ private const string ApplicationNamespace = "NetSdrClient";
+ private const string ModelsNamespace = "NetSdrClient.Models";
+ private const string ServicesNamespace = "NetSdrClient.Services";
+ private const string InterfacesNamespace = "NetSdrClient.Interfaces";
+ private const string InfrastructureNamespace = "NetSdrClient.Infrastructure";
+ private const string UINamespace = "NetSdrClient.UI";
+
+ [TestMethod]
+ public void ServicesLayer_ShouldNotDependOnUI()
+ {
+ // Arrange
+ var assembly = typeof(Services.SDRClient).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(ServicesNamespace)
+ .ShouldNot()
+ .HaveDependencyOn(UINamespace)
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Services layer should not depend on UI: {string.Join(", ", result.FailingTypes)}");
+ }
+
+ [TestMethod]
+ public void Models_ShouldNotReferenceServices()
+ {
+ // Arrange
+ var assembly = typeof(Models.SDRDevice).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(ModelsNamespace)
+ .ShouldNot()
+ .HaveDependencyOn(ServicesNamespace)
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Models should not depend on Services: {string.Join(", ", result.FailingTypes)}");
+ }
+
+ [TestMethod]
+ public void Interfaces_ShouldNotHaveDependencies()
+ {
+ // Arrange
+ var assembly = typeof(Services.SDRClient).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(InterfacesNamespace)
+ .Should()
+ .NotHaveDependencyOnAny(
+ ServicesNamespace,
+ ModelsNamespace,
+ InfrastructureNamespace,
+ UINamespace)
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Interfaces should not have dependencies: {string.Join(", ", result.FailingTypes)}");
+ }
+
+ [TestMethod]
+ public void AllClasses_ShouldHaveNamesEndingWithService_IfInServicesNamespace()
+ {
+ // Arrange
+ var assembly = typeof(Services.SDRClient).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(ServicesNamespace)
+ .And()
+ .AreClasses()
+ .Should()
+ .HaveNameEndingWith("Service")
+ .Or()
+ .HaveNameEndingWith("Client")
+ .Or()
+ .HaveNameEndingWith("Handler")
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Services should have proper naming: {string.Join(", ", result.FailingTypes)}");
+ }
+
+ [TestMethod]
+ public void Models_ShouldBeSealed()
+ {
+ // Arrange
+ var assembly = typeof(Models.SDRDevice).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(ModelsNamespace)
+ .And()
+ .AreClasses()
+ .Should()
+ .BeSealed()
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Models should be sealed: {string.Join(", ", result.FailingTypes)}");
+ }
+
+ [TestMethod]
+ public void Services_ShouldNotDependOnInfrastructureDirectly()
+ {
+ // Arrange
+ var assembly = typeof(Services.SDRClient).Assembly;
+
+ // Act
+ var result = Types
+ .InAssembly(assembly)
+ .That()
+ .ResideInNamespace(ServicesNamespace)
+ .ShouldNot()
+ .HaveDependencyOn(InfrastructureNamespace)
+ .GetResult();
+
+ // Assert
+ Assert.IsTrue(result.IsSuccessful,
+ $"Services should not depend directly on Infrastructure: {string.Join(", ", result.FailingTypes)}");
+ }
+ }
+}
diff --git a/NetSdrClient.Tests/NetSdrClient.Tests.csproj b/NetSdrClient.Tests/NetSdrClient.Tests.csproj
new file mode 100644
index 00000000..7697a375
--- /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/NetSdrClient/Services/BadService.cs b/NetSdrClient/Services/BadService.cs
new file mode 100644
index 00000000..51da3ba7
--- /dev/null
+++ b/NetSdrClient/Services/BadService.cs
@@ -0,0 +1,20 @@
+using NetSdrClient.Models;
+using NetSdrClient.UI; // Навмисне порушення - Services залежить від UI
+
+namespace NetSdrClient.Services
+{
+ public class BadService
+ {
+ private readonly UIComponent _uiComponent; // Порушення!
+
+ public BadService()
+ {
+ _uiComponent = new UIComponent();
+ }
+
+ public void DoSomething()
+ {
+ _uiComponent.ShowMessage("This violates architecture rules!");
+ }
+ }
+}
diff --git a/NetSdrClientApp/NetSdrClient.cs b/NetSdrClientApp/NetSdrClient.cs
index b0a7c058..d4466ac5 100644
--- a/NetSdrClientApp/NetSdrClient.cs
+++ b/NetSdrClientApp/NetSdrClient.cs
@@ -9,7 +9,29 @@
using System.Threading.Tasks;
using static NetSdrClientApp.Messages.NetSdrMessageHelper;
using static System.Runtime.InteropServices.JavaScript.JSType;
+using NetSdrClient.Models;
+using NetSdrClient.Interfaces;
+namespace NetSdrClient.Services
+{
+ public class SDRClient : IDeviceService
+ {
+ public List Devices { get; private set; }
+
+ // Реалізація інтерфейсу
+ public void Connect()
+ {
+ // Connection logic
+ }
+
+ public void Disconnect()
+ {
+ // Disconnection logic
+ }
+
+ // Інші методи...
+ }
+}
namespace NetSdrClientApp
{
public class NetSdrClient
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/UI/UIComponent.cs b/UI/UIComponent.cs
new file mode 100644
index 00000000..347ef573
--- /dev/null
+++ b/UI/UIComponent.cs
@@ -0,0 +1,10 @@
+namespace NetSdrClient.UI
+{
+ public class UIComponent
+ {
+ public void ShowMessage(string message)
+ {
+ // UI logic here - this should not be called from Services
+ }
+ }
+}
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
diff --git a/coverlet.runsettings b/coverlet.runsettings
new file mode 100644
index 00000000..9815d295
--- /dev/null
+++ b/coverlet.runsettings
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ opencover
+ [NetSdrClient]*
+ [NetSdrClient.Tests]*
+
+
+
+
+