mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-16 13:15:03 +00:00
chore: remove unnecessary files
This commit is contained in:
@@ -1,42 +0,0 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.0.31903.59
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A8B8F24D-3181-45BF-9ED3-F734E04F0BC8}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Core", "src\PepperDash.Core\PepperDash.Core.csproj", "{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{B8B8F24D-3181-45BF-9ED3-F734E04F0BC9}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core.Tests", "tests\PepperDash.Essentials.Core.Tests\PepperDash.Essentials.Core.Tests.csproj", "{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{1E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7A} = {A8B8F24D-3181-45BF-9ED3-F734E04F0BC8}
|
|
||||||
{2E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7B} = {A8B8F24D-3181-45BF-9ED3-F734E04F0BC8}
|
|
||||||
{3E5D8C7C-A4D0-4D0E-A6B0-9E3F3D9C8B7C} = {B8B8F24D-3181-45BF-9ED3-F734E04F0BC9}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
using Crestron.SimplSharpPro;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace EssentialsTests
|
|
||||||
{
|
|
||||||
public class CrestronMockTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void CrestronControlSystem_Constructor_ShouldBuildSuccessfully()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var exception = Record.Exception(() => new CrestronControlSystem());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CrestronControlSystem_Constructor_ShouldSetPropertiesCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var controlSystem = new CrestronControlSystem();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(controlSystem);
|
|
||||||
Assert.NotNull(controlSystem.ComPorts);
|
|
||||||
Assert.NotNull(controlSystem.RelayPorts);
|
|
||||||
Assert.NotNull(controlSystem.IROutputPorts);
|
|
||||||
Assert.NotNull(controlSystem.DigitalInputPorts);
|
|
||||||
Assert.NotNull(controlSystem.IRInputPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CrestronControlSystem_InitializeSystem_ShouldNotThrow()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var controlSystem = new CrestronControlSystem();
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
var exception = Record.Exception(() => controlSystem.InitializeSystem());
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockControlSystem_ShouldHaveRequiredStaticProperties()
|
|
||||||
{
|
|
||||||
// Act & Assert
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullCue);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullBoolInputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullBoolOutputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullUShortInputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullUShortOutputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullStringInputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.NullStringOutputSig);
|
|
||||||
Assert.NotNull(CrestronControlSystem.SigGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockControlSystem_ShouldCreateSigGroups()
|
|
||||||
{
|
|
||||||
// Act & Assert
|
|
||||||
var exception = Record.Exception(() =>
|
|
||||||
{
|
|
||||||
var sigGroup = CrestronControlSystem.CreateSigGroup(1, eSigType.Bool);
|
|
||||||
Assert.NotNull(sigGroup);
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockControlSystem_VirtualMethods_ShouldNotThrow()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var controlSystem = new CrestronControlSystem();
|
|
||||||
|
|
||||||
// Act & Assert - just test InitializeSystem since it's definitely available
|
|
||||||
var exception = Record.Exception(() =>
|
|
||||||
{
|
|
||||||
controlSystem.InitializeSystem();
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
using CrestronMock;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace EssentialsTests
|
|
||||||
{
|
|
||||||
public class DirectMockTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void CrestronMock_Should_Build_Successfully()
|
|
||||||
{
|
|
||||||
// This test verifies that our mock framework compiles and builds
|
|
||||||
// We've already proven this by the fact that the test project builds successfully
|
|
||||||
Assert.True(true, "Mock framework builds successfully in Test configuration");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockFramework_Should_Provide_Required_Types()
|
|
||||||
{
|
|
||||||
// Verify that the essential mock types are available
|
|
||||||
var mockSig = new Sig();
|
|
||||||
var mockBoolInputSig = new BoolInputSig();
|
|
||||||
var mockUShortInputSig = new UShortInputSig();
|
|
||||||
var mockStringInputSig = new StringInputSig();
|
|
||||||
|
|
||||||
Assert.NotNull(mockSig);
|
|
||||||
Assert.NotNull(mockBoolInputSig);
|
|
||||||
Assert.NotNull(mockUShortInputSig);
|
|
||||||
Assert.NotNull(mockStringInputSig);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockFramework_Should_Provide_Hardware_Types()
|
|
||||||
{
|
|
||||||
// Verify that hardware mock types are available
|
|
||||||
var mockComPort = new ComPort();
|
|
||||||
var mockRelay = new Relay();
|
|
||||||
var mockIROutputPort = new IROutputPort();
|
|
||||||
var mockIRInputPort = new IRInputPort();
|
|
||||||
var mockVersiPort = new VersiPort();
|
|
||||||
|
|
||||||
Assert.NotNull(mockComPort);
|
|
||||||
Assert.NotNull(mockRelay);
|
|
||||||
Assert.NotNull(mockIROutputPort);
|
|
||||||
Assert.NotNull(mockIRInputPort);
|
|
||||||
Assert.NotNull(mockVersiPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestConfiguration_Should_Use_MockFramework()
|
|
||||||
{
|
|
||||||
// In the Test configuration, CrestronControlSystem should come from our mock
|
|
||||||
// Let's verify this by checking we can create it without real Crestron dependencies
|
|
||||||
|
|
||||||
// Since we can't reliably test the namespace-conflicted version,
|
|
||||||
// let's at least verify our mock types exist
|
|
||||||
var mockControlSystemType = typeof(CrestronMock.CrestronControlSystem);
|
|
||||||
Assert.NotNull(mockControlSystemType);
|
|
||||||
Assert.Equal("CrestronMock.CrestronControlSystem", mockControlSystemType.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MockControlSystem_DirectTest_Should_Work()
|
|
||||||
{
|
|
||||||
// Test our mock directly using the CrestronMock namespace
|
|
||||||
var mockControlSystem = new CrestronMock.CrestronControlSystem();
|
|
||||||
|
|
||||||
Assert.NotNull(mockControlSystem);
|
|
||||||
Assert.NotNull(mockControlSystem.ComPorts);
|
|
||||||
Assert.NotNull(mockControlSystem.RelayPorts);
|
|
||||||
Assert.NotNull(mockControlSystem.IROutputPorts);
|
|
||||||
Assert.NotNull(mockControlSystem.DigitalInputPorts);
|
|
||||||
Assert.NotNull(mockControlSystem.IRInputPort);
|
|
||||||
|
|
||||||
// Test that virtual methods don't throw
|
|
||||||
var exception = Record.Exception(() => mockControlSystem.InitializeSystem());
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
using CrestronMock;
|
|
||||||
using PepperDash.Essentials;
|
|
||||||
using PepperDash.Essentials.Core;
|
|
||||||
|
|
||||||
namespace EssentialsTests;
|
|
||||||
|
|
||||||
public class ControlSystemTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_Constructor_ShouldBuildSuccessfully()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var exception = Record.Exception(() => new ControlSystem());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_Constructor_ShouldSetGlobalControlSystem()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var controlSystem = new ControlSystem();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(Global.ControlSystem);
|
|
||||||
Assert.Same(controlSystem, Global.ControlSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_InitializeSystem_ShouldNotThrow()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var controlSystem = new ControlSystem();
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
var exception = Record.Exception(() => controlSystem.InitializeSystem());
|
|
||||||
Assert.Null(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_ShouldImplementILoadConfig()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var controlSystem = new ControlSystem();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(controlSystem is ILoadConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_ShouldHaveRequiredInterfaces()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var controlSystem = new ControlSystem();
|
|
||||||
|
|
||||||
// Assert - Check that it inherits from base mock and implements hardware interfaces
|
|
||||||
Assert.NotNull(controlSystem);
|
|
||||||
Assert.True(controlSystem is IComPorts, "ControlSystem should implement IComPorts");
|
|
||||||
Assert.True(controlSystem is IRelayPorts, "ControlSystem should implement IRelayPorts");
|
|
||||||
Assert.True(controlSystem is IIROutputPorts, "ControlSystem should implement IIROutputPorts");
|
|
||||||
Assert.True(controlSystem is IIOPorts, "ControlSystem should implement IIOPorts");
|
|
||||||
Assert.True(controlSystem is IDigitalInputPorts, "ControlSystem should implement IDigitalInputPorts");
|
|
||||||
Assert.True(controlSystem is IIRInputPort, "ControlSystem should implement IIRInputPort");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ControlSystem_ShouldHaveRequiredProperties()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var controlSystem = new ControlSystem();
|
|
||||||
|
|
||||||
// Assert - Test by casting to interfaces to access properties
|
|
||||||
var comPorts = controlSystem as IComPorts;
|
|
||||||
var relayPorts = controlSystem as IRelayPorts;
|
|
||||||
var irOutputPorts = controlSystem as IIROutputPorts;
|
|
||||||
var ioPorts = controlSystem as IIOPorts;
|
|
||||||
var digitalInputPorts = controlSystem as IDigitalInputPorts;
|
|
||||||
var irInputPort = controlSystem as IIRInputPort;
|
|
||||||
|
|
||||||
Assert.NotNull(comPorts?.ComPorts);
|
|
||||||
Assert.NotNull(relayPorts?.RelayPorts);
|
|
||||||
Assert.NotNull(irOutputPorts?.IROutputPorts);
|
|
||||||
Assert.NotNull(ioPorts?.IOPorts);
|
|
||||||
Assert.NotNull(digitalInputPorts?.DigitalInputPorts);
|
|
||||||
Assert.NotNull(irInputPort?.IRInputPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Moq;
|
|
||||||
using PepperDash.Essentials.Core.Abstractions;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Tests.Abstractions
|
|
||||||
{
|
|
||||||
public class DigitalInputTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void StateChange_WhenDigitalInputChanges_RaisesEvent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockDigitalInput = new Mock<IDigitalInput>();
|
|
||||||
var eventRaised = false;
|
|
||||||
bool capturedState = false;
|
|
||||||
|
|
||||||
mockDigitalInput.Setup(d => d.State).Returns(true);
|
|
||||||
|
|
||||||
// Subscribe to the event
|
|
||||||
mockDigitalInput.Object.StateChange += (sender, args) =>
|
|
||||||
{
|
|
||||||
eventRaised = true;
|
|
||||||
capturedState = args.State;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act - Raise the event
|
|
||||||
mockDigitalInput.Raise(d => d.StateChange += null,
|
|
||||||
mockDigitalInput.Object,
|
|
||||||
new DigitalInputEventArgs(true));
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
eventRaised.Should().BeTrue();
|
|
||||||
capturedState.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void State_ReturnsCorrectValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockDigitalInput = new Mock<IDigitalInput>();
|
|
||||||
mockDigitalInput.Setup(d => d.State).Returns(true);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var state = mockDigitalInput.Object.State;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
state.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MultipleStateChanges_TrackStateCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockDigitalInput = new Mock<IDigitalInput>();
|
|
||||||
var stateChanges = new System.Collections.Generic.List<bool>();
|
|
||||||
|
|
||||||
mockDigitalInput.Object.StateChange += (sender, args) =>
|
|
||||||
{
|
|
||||||
stateChanges.Add(args.State);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act - Simulate multiple state changes
|
|
||||||
mockDigitalInput.Raise(d => d.StateChange += null,
|
|
||||||
mockDigitalInput.Object,
|
|
||||||
new DigitalInputEventArgs(true));
|
|
||||||
|
|
||||||
mockDigitalInput.Raise(d => d.StateChange += null,
|
|
||||||
mockDigitalInput.Object,
|
|
||||||
new DigitalInputEventArgs(false));
|
|
||||||
|
|
||||||
mockDigitalInput.Raise(d => d.StateChange += null,
|
|
||||||
mockDigitalInput.Object,
|
|
||||||
new DigitalInputEventArgs(true));
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
stateChanges.Should().HaveCount(3);
|
|
||||||
stateChanges[0].Should().BeTrue();
|
|
||||||
stateChanges[1].Should().BeFalse();
|
|
||||||
stateChanges[2].Should().BeTrue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Moq;
|
|
||||||
using PepperDash.Essentials.Core.Abstractions;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Tests.Abstractions
|
|
||||||
{
|
|
||||||
public class VersiPortTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void DigitalIn_ReturnsCorrectValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
mockVersiPort.Setup(v => v.DigitalIn).Returns(true);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var digitalIn = mockVersiPort.Object.DigitalIn;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
digitalIn.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SetDigitalOut_SetsCorrectValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
mockVersiPort.Object.SetDigitalOut(true);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockVersiPort.Verify(v => v.SetDigitalOut(true), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void AnalogIn_ReturnsCorrectValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
ushort expectedValue = 32768;
|
|
||||||
mockVersiPort.Setup(v => v.AnalogIn).Returns(expectedValue);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var analogIn = mockVersiPort.Object.AnalogIn;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
analogIn.Should().Be(expectedValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void VersiportChange_WhenDigitalChanges_RaisesEventWithCorrectType()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
var eventRaised = false;
|
|
||||||
VersiPortEventType? capturedEventType = null;
|
|
||||||
object capturedValue = null;
|
|
||||||
|
|
||||||
mockVersiPort.Object.VersiportChange += (sender, args) =>
|
|
||||||
{
|
|
||||||
eventRaised = true;
|
|
||||||
capturedEventType = args.EventType;
|
|
||||||
capturedValue = args.Value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
mockVersiPort.Raise(v => v.VersiportChange += null,
|
|
||||||
mockVersiPort.Object,
|
|
||||||
new VersiPortEventArgs
|
|
||||||
{
|
|
||||||
EventType = VersiPortEventType.DigitalInChange,
|
|
||||||
Value = true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
eventRaised.Should().BeTrue();
|
|
||||||
capturedEventType.Should().Be(VersiPortEventType.DigitalInChange);
|
|
||||||
capturedValue.Should().Be(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void VersiportChange_WhenAnalogChanges_RaisesEventWithCorrectValue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
var eventRaised = false;
|
|
||||||
VersiPortEventType? capturedEventType = null;
|
|
||||||
object capturedValue = null;
|
|
||||||
ushort expectedAnalogValue = 12345;
|
|
||||||
|
|
||||||
mockVersiPort.Object.VersiportChange += (sender, args) =>
|
|
||||||
{
|
|
||||||
eventRaised = true;
|
|
||||||
capturedEventType = args.EventType;
|
|
||||||
capturedValue = args.Value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
mockVersiPort.Raise(v => v.VersiportChange += null,
|
|
||||||
mockVersiPort.Object,
|
|
||||||
new VersiPortEventArgs
|
|
||||||
{
|
|
||||||
EventType = VersiPortEventType.AnalogInChange,
|
|
||||||
Value = expectedAnalogValue
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
eventRaised.Should().BeTrue();
|
|
||||||
capturedEventType.Should().Be(VersiPortEventType.AnalogInChange);
|
|
||||||
capturedValue.Should().Be(expectedAnalogValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MultipleVersiportChanges_TracksAllChangesCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockVersiPort = new Mock<IVersiPort>();
|
|
||||||
var changes = new System.Collections.Generic.List<(VersiPortEventType type, object value)>();
|
|
||||||
|
|
||||||
mockVersiPort.Object.VersiportChange += (sender, args) =>
|
|
||||||
{
|
|
||||||
changes.Add((args.EventType, args.Value));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act - Simulate multiple changes
|
|
||||||
mockVersiPort.Raise(v => v.VersiportChange += null,
|
|
||||||
mockVersiPort.Object,
|
|
||||||
new VersiPortEventArgs
|
|
||||||
{
|
|
||||||
EventType = VersiPortEventType.DigitalInChange,
|
|
||||||
Value = true
|
|
||||||
});
|
|
||||||
|
|
||||||
mockVersiPort.Raise(v => v.VersiportChange += null,
|
|
||||||
mockVersiPort.Object,
|
|
||||||
new VersiPortEventArgs
|
|
||||||
{
|
|
||||||
EventType = VersiPortEventType.AnalogInChange,
|
|
||||||
Value = (ushort)30000
|
|
||||||
});
|
|
||||||
|
|
||||||
mockVersiPort.Raise(v => v.VersiportChange += null,
|
|
||||||
mockVersiPort.Object,
|
|
||||||
new VersiPortEventArgs
|
|
||||||
{
|
|
||||||
EventType = VersiPortEventType.DigitalInChange,
|
|
||||||
Value = false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
changes.Should().HaveCount(3);
|
|
||||||
changes[0].type.Should().Be(VersiPortEventType.DigitalInChange);
|
|
||||||
changes[0].value.Should().Be(true);
|
|
||||||
changes[1].type.Should().Be(VersiPortEventType.AnalogInChange);
|
|
||||||
changes[1].value.Should().Be((ushort)30000);
|
|
||||||
changes[2].type.Should().Be(VersiPortEventType.DigitalInChange);
|
|
||||||
changes[2].value.Should().Be(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Xunit;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Moq;
|
|
||||||
using PepperDash.Essentials.Core.Devices;
|
|
||||||
using PepperDash.Essentials.Core.Abstractions;
|
|
||||||
using PepperDash.Essentials.Core.Factory;
|
|
||||||
using PepperDash.Essentials.Core.CrestronIO;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Tests.Devices
|
|
||||||
{
|
|
||||||
public class CrestronProcessorTestableTests : IDisposable
|
|
||||||
{
|
|
||||||
public CrestronProcessorTestableTests()
|
|
||||||
{
|
|
||||||
// Enable test mode for all tests in this class
|
|
||||||
CrestronEnvironmentFactory.EnableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Restore runtime mode after tests
|
|
||||||
CrestronEnvironmentFactory.DisableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_WithNullProcessor_UsesFactoryToGetControlSystem()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.Should().NotBeNull();
|
|
||||||
processor.Processor.Should().NotBeNull();
|
|
||||||
processor.Key.Should().Be("test-processor");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_WithProvidedProcessor_UsesProvidedProcessor()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(false);
|
|
||||||
mockProcessor.Setup(p => p.RelayPorts).Returns(new Dictionary<uint, IRelayPort>());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.Processor.Should().BeSameAs(mockProcessor.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetRelays_WhenProcessorSupportsRelays_CreatesRelayDevices()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProvider = new CrestronMockProvider();
|
|
||||||
mockProvider.ConfigureMockSystem(system =>
|
|
||||||
{
|
|
||||||
system.SupportsRelay = true;
|
|
||||||
system.NumberOfRelayPorts = 4;
|
|
||||||
// Ensure relay ports are initialized
|
|
||||||
for (uint i = 1; i <= 4; i++)
|
|
||||||
{
|
|
||||||
system.RelayPorts[i] = new MockRelayPort();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
CrestronEnvironmentFactory.SetProvider(mockProvider);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.SwitchedOutputs.Should().HaveCount(4);
|
|
||||||
processor.SwitchedOutputs.Should().ContainKeys(1, 2, 3, 4);
|
|
||||||
|
|
||||||
foreach (var kvp in processor.SwitchedOutputs)
|
|
||||||
{
|
|
||||||
kvp.Value.Should().BeOfType<GenericRelayDeviceTestable>();
|
|
||||||
var relayDevice = kvp.Value as GenericRelayDeviceTestable;
|
|
||||||
relayDevice.Key.Should().Be($"test-processor-relay-{kvp.Key}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetRelays_WhenProcessorDoesNotSupportRelays_CreatesNoDevices()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(false);
|
|
||||||
mockProcessor.Setup(p => p.RelayPorts).Returns(new Dictionary<uint, IRelayPort>());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.SwitchedOutputs.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetRelays_HandlesExceptionGracefully()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(true);
|
|
||||||
mockProcessor.Setup(p => p.NumberOfRelayPorts).Throws(new Exception("Test exception"));
|
|
||||||
mockProcessor.Setup(p => p.RelayPorts).Returns(new Dictionary<uint, IRelayPort>());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
Action act = () => new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
act.Should().NotThrow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GenericRelayDeviceTestableTests : IDisposable
|
|
||||||
{
|
|
||||||
public GenericRelayDeviceTestableTests()
|
|
||||||
{
|
|
||||||
CrestronEnvironmentFactory.EnableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
CrestronEnvironmentFactory.DisableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_WithNullRelayPort_ThrowsArgumentNullException()
|
|
||||||
{
|
|
||||||
// Act & Assert
|
|
||||||
Action act = () => new GenericRelayDeviceTestable("test-relay", null);
|
|
||||||
act.Should().Throw<ArgumentNullException>()
|
|
||||||
.WithParameterName("relayPort");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_WithValidRelayPort_InitializesCorrectly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
device.Should().NotBeNull();
|
|
||||||
device.Key.Should().Be("test-relay");
|
|
||||||
device.OutputIsOnFeedback.Should().NotBeNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void OpenRelay_OpensRelayAndUpdatesFeedback()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
mockRelayPort.SetState(true); // Start with closed relay
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
bool feedbackFired = false;
|
|
||||||
device.OutputIsOnFeedback.OutputChange += (sender, args) => feedbackFired = true;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.OpenRelay();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.State.Should().BeFalse();
|
|
||||||
device.IsOn.Should().BeFalse();
|
|
||||||
feedbackFired.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CloseRelay_ClosesRelayAndUpdatesFeedback()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
mockRelayPort.SetState(false); // Start with open relay
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
bool feedbackFired = false;
|
|
||||||
device.OutputIsOnFeedback.OutputChange += (sender, args) => feedbackFired = true;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.CloseRelay();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.State.Should().BeTrue();
|
|
||||||
device.IsOn.Should().BeTrue();
|
|
||||||
feedbackFired.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PulseRelay_CallsPulseOnRelayPort()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.PulseRelay(500);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Pulse(500), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void On_ClosesRelay()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.On();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.State.Should().BeTrue();
|
|
||||||
device.IsOn.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Off_OpensRelay()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
mockRelayPort.SetState(true); // Start with closed relay
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.Off();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.State.Should().BeFalse();
|
|
||||||
device.IsOn.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PowerToggle_TogglesRelayState()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
// Act & Assert - First toggle (off to on)
|
|
||||||
device.PowerToggle();
|
|
||||||
mockRelayPort.State.Should().BeTrue();
|
|
||||||
device.IsOn.Should().BeTrue();
|
|
||||||
|
|
||||||
// Act & Assert - Second toggle (on to off)
|
|
||||||
device.PowerToggle();
|
|
||||||
mockRelayPort.State.Should().BeFalse();
|
|
||||||
device.IsOn.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void IsOn_ReflectsRelayPortState()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
|
|
||||||
// Act & Assert - Initially off
|
|
||||||
device.IsOn.Should().BeFalse();
|
|
||||||
|
|
||||||
// Act & Assert - After closing
|
|
||||||
mockRelayPort.Close();
|
|
||||||
device.IsOn.Should().BeTrue();
|
|
||||||
|
|
||||||
// Act & Assert - After opening
|
|
||||||
mockRelayPort.Open();
|
|
||||||
device.IsOn.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CustomActivate_ReinitializesFeedback()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new MockRelayPort();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort);
|
|
||||||
var originalFeedback = device.OutputIsOnFeedback;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = device.CustomActivate();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().BeTrue();
|
|
||||||
device.OutputIsOnFeedback.Should().NotBeNull();
|
|
||||||
device.OutputIsOnFeedback.Should().NotBeSameAs(originalFeedback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IntegrationTests : IDisposable
|
|
||||||
{
|
|
||||||
public IntegrationTests()
|
|
||||||
{
|
|
||||||
CrestronEnvironmentFactory.EnableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
CrestronEnvironmentFactory.DisableTestMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FullSystemIntegration_CreatesAndControlsRelays()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProvider = new CrestronMockProvider();
|
|
||||||
mockProvider.ConfigureMockSystem(system =>
|
|
||||||
{
|
|
||||||
system.SupportsRelay = true;
|
|
||||||
system.NumberOfRelayPorts = 2;
|
|
||||||
system.ProgramIdTag = "INTEGRATION_TEST";
|
|
||||||
});
|
|
||||||
|
|
||||||
CrestronEnvironmentFactory.SetProvider(mockProvider);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("integration-processor");
|
|
||||||
|
|
||||||
// Assert processor creation
|
|
||||||
processor.Processor.ProgramIdTag.Should().Be("INTEGRATION_TEST");
|
|
||||||
processor.SwitchedOutputs.Should().HaveCount(2);
|
|
||||||
|
|
||||||
// Test relay control
|
|
||||||
var relay1 = processor.SwitchedOutputs[1] as GenericRelayDeviceTestable;
|
|
||||||
relay1.Should().NotBeNull();
|
|
||||||
|
|
||||||
// Test On/Off operations
|
|
||||||
relay1.On();
|
|
||||||
relay1.IsOn.Should().BeTrue();
|
|
||||||
|
|
||||||
relay1.Off();
|
|
||||||
relay1.IsOn.Should().BeFalse();
|
|
||||||
|
|
||||||
// Test toggle
|
|
||||||
relay1.PowerToggle();
|
|
||||||
relay1.IsOn.Should().BeTrue();
|
|
||||||
|
|
||||||
relay1.PowerToggle();
|
|
||||||
relay1.IsOn.Should().BeFalse();
|
|
||||||
|
|
||||||
// Test feedback
|
|
||||||
int feedbackCount = 0;
|
|
||||||
relay1.OutputIsOnFeedback.OutputChange += (sender, args) => feedbackCount++;
|
|
||||||
|
|
||||||
relay1.On();
|
|
||||||
feedbackCount.Should().Be(1);
|
|
||||||
|
|
||||||
relay1.Off();
|
|
||||||
feedbackCount.Should().Be(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void FactoryPattern_AllowsSwitchingBetweenProviders()
|
|
||||||
{
|
|
||||||
// Arrange - Start with test mode
|
|
||||||
CrestronEnvironmentFactory.EnableTestMode();
|
|
||||||
CrestronEnvironmentFactory.IsTestMode.Should().BeTrue();
|
|
||||||
|
|
||||||
var testProcessor = new CrestronProcessorTestable("test-mode");
|
|
||||||
testProcessor.Processor.Should().BeOfType<MockControlSystem>();
|
|
||||||
|
|
||||||
// Act - Switch to runtime mode
|
|
||||||
CrestronEnvironmentFactory.DisableTestMode();
|
|
||||||
CrestronEnvironmentFactory.IsTestMode.Should().BeFalse();
|
|
||||||
|
|
||||||
// Note: In runtime mode without actual Crestron hardware,
|
|
||||||
// it will use the NullControlSystem implementation
|
|
||||||
var runtimeProcessor = new CrestronProcessorTestable("runtime-mode");
|
|
||||||
runtimeProcessor.Processor.Should().NotBeNull();
|
|
||||||
|
|
||||||
// Act - Switch back to test mode
|
|
||||||
CrestronEnvironmentFactory.EnableTestMode();
|
|
||||||
CrestronEnvironmentFactory.IsTestMode.Should().BeTrue();
|
|
||||||
|
|
||||||
var testProcessor2 = new CrestronProcessorTestable("test-mode-2");
|
|
||||||
testProcessor2.Processor.Should().BeOfType<MockControlSystem>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Moq;
|
|
||||||
using PepperDash.Essentials.Core.Abstractions;
|
|
||||||
using PepperDash.Essentials.Core.Devices;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Tests.Devices
|
|
||||||
{
|
|
||||||
public class CrestronProcessorTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_WithValidProcessor_InitializesSwitchedOutputs()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.Should().NotBeNull();
|
|
||||||
processor.Key.Should().Be("test-processor");
|
|
||||||
processor.SwitchedOutputs.Should().NotBeNull();
|
|
||||||
processor.SwitchedOutputs.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetRelays_WhenProcessorSupportsRelays_CreatesRelayDevices()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
var mockRelayPort1 = new Mock<IRelayPort>();
|
|
||||||
var mockRelayPort2 = new Mock<IRelayPort>();
|
|
||||||
|
|
||||||
var relayPorts = new Dictionary<uint, IRelayPort>
|
|
||||||
{
|
|
||||||
{ 1, mockRelayPort1.Object },
|
|
||||||
{ 2, mockRelayPort2.Object }
|
|
||||||
};
|
|
||||||
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(true);
|
|
||||||
mockProcessor.Setup(p => p.NumberOfRelayPorts).Returns(2);
|
|
||||||
mockProcessor.Setup(p => p.RelayPorts).Returns(relayPorts);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.SwitchedOutputs.Should().HaveCount(2);
|
|
||||||
processor.SwitchedOutputs.Should().ContainKey(1);
|
|
||||||
processor.SwitchedOutputs.Should().ContainKey(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetRelays_WhenProcessorDoesNotSupportRelays_DoesNotCreateRelayDevices()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockProcessor = new Mock<ICrestronControlSystem>();
|
|
||||||
mockProcessor.Setup(p => p.SupportsRelay).Returns(false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var processor = new CrestronProcessorTestable("test-processor", mockProcessor.Object);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
processor.SwitchedOutputs.Should().BeEmpty();
|
|
||||||
mockProcessor.Verify(p => p.NumberOfRelayPorts, Times.Never);
|
|
||||||
mockProcessor.Verify(p => p.RelayPorts, Times.Never);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GenericRelayDeviceTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void OpenRelay_CallsRelayPortOpen()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.OpenRelay();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Open(), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CloseRelay_CallsRelayPortClose()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.CloseRelay();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Close(), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PulseRelay_CallsRelayPortPulseWithCorrectDelay()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
const int delayMs = 500;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.PulseRelay(delayMs);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Pulse(delayMs), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void On_CallsCloseRelay()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.On();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Close(), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Off_CallsOpenRelay()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.Off();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Open(), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PowerToggle_WhenRelayIsOn_CallsOff()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
mockRelayPort.Setup(r => r.State).Returns(true); // Relay is ON
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.PowerToggle();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Open(), Times.Once);
|
|
||||||
mockRelayPort.Verify(r => r.Close(), Times.Never);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PowerToggle_WhenRelayIsOff_CallsOn()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
mockRelayPort.Setup(r => r.State).Returns(false); // Relay is OFF
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.PowerToggle();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Close(), Times.Once);
|
|
||||||
mockRelayPort.Verify(r => r.Open(), Times.Never);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void IsOn_ReturnsRelayPortState()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
mockRelayPort.Setup(r => r.State).Returns(true);
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var isOn = device.IsOn;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
isOn.Should().BeTrue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<IsTestProject>true</IsTestProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
|
||||||
<PackageReference Include="xunit" Version="2.6.5" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Moq" Version="4.20.70" />
|
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\..\src\PepperDash.Core\PepperDash.Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
170
tests/README.md
170
tests/README.md
@@ -1,170 +0,0 @@
|
|||||||
# PepperDash Essentials Unit Testing Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This guide demonstrates how to write unit tests for PepperDash Essentials despite the Crestron hardware dependencies. The key approach is to use **abstraction layers** and **dependency injection** to isolate Crestron-specific functionality.
|
|
||||||
|
|
||||||
## Architecture Pattern
|
|
||||||
|
|
||||||
### 1. Abstraction Layer
|
|
||||||
We create interfaces that abstract Crestron hardware components:
|
|
||||||
- `ICrestronControlSystem` - Abstracts the control system
|
|
||||||
- `IRelayPort` - Abstracts relay functionality
|
|
||||||
- `IDigitalInput` - Abstracts digital inputs
|
|
||||||
- `IVersiPort` - Abstracts VersiPorts
|
|
||||||
|
|
||||||
### 2. Adapters
|
|
||||||
Adapter classes wrap actual Crestron objects in production:
|
|
||||||
- `CrestronControlSystemAdapter` - Wraps `CrestronControlSystem`
|
|
||||||
- `RelayPortAdapter` - Wraps Crestron `Relay`
|
|
||||||
- `DigitalInputAdapter` - Wraps Crestron `DigitalInput`
|
|
||||||
- `VersiPortAdapter` - Wraps Crestron `Versiport`
|
|
||||||
|
|
||||||
### 3. Testable Classes
|
|
||||||
Create testable versions of classes that accept abstractions:
|
|
||||||
- `CrestronProcessorTestable` - Accepts `ICrestronControlSystem` instead of concrete type
|
|
||||||
- `GenericRelayDeviceTestable` - Accepts `IRelayPort` instead of concrete type
|
|
||||||
|
|
||||||
## Writing Tests
|
|
||||||
|
|
||||||
### Basic Test Example
|
|
||||||
```csharp
|
|
||||||
[Fact]
|
|
||||||
public void OpenRelay_CallsRelayPortOpen()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockRelayPort = new Mock<IRelayPort>();
|
|
||||||
var device = new GenericRelayDeviceTestable("test-relay", mockRelayPort.Object);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
device.OpenRelay();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
mockRelayPort.Verify(r => r.Open(), Times.Once);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Events
|
|
||||||
```csharp
|
|
||||||
[Fact]
|
|
||||||
public void StateChange_WhenDigitalInputChanges_RaisesEvent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mockDigitalInput = new Mock<IDigitalInput>();
|
|
||||||
var eventRaised = false;
|
|
||||||
|
|
||||||
mockDigitalInput.Object.StateChange += (sender, args) => eventRaised = true;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
mockDigitalInput.Raise(d => d.StateChange += null,
|
|
||||||
mockDigitalInput.Object,
|
|
||||||
new DigitalInputEventArgs(true));
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
eventRaised.Should().BeTrue();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
### Locally
|
|
||||||
```bash
|
|
||||||
# Run all tests
|
|
||||||
dotnet test
|
|
||||||
|
|
||||||
# Run with coverage
|
|
||||||
dotnet test --collect:"XPlat Code Coverage"
|
|
||||||
|
|
||||||
# Run specific test project
|
|
||||||
dotnet test tests/PepperDash.Essentials.Core.Tests
|
|
||||||
```
|
|
||||||
|
|
||||||
### CI Pipeline
|
|
||||||
Tests run automatically on:
|
|
||||||
- Push to main/develop/net8-updates branches
|
|
||||||
- Pull requests
|
|
||||||
- GitHub Actions workflow generates coverage reports
|
|
||||||
|
|
||||||
## Migration Strategy
|
|
||||||
|
|
||||||
To migrate existing code to be testable:
|
|
||||||
|
|
||||||
1. **Identify Crestron Dependencies**
|
|
||||||
- Search for `using Crestron` statements
|
|
||||||
- Find direct hardware interactions
|
|
||||||
|
|
||||||
2. **Create Abstractions**
|
|
||||||
- Define interfaces for hardware components
|
|
||||||
- Keep interfaces focused and simple
|
|
||||||
|
|
||||||
3. **Implement Adapters**
|
|
||||||
- Wrap Crestron objects with adapters
|
|
||||||
- Map Crestron events to abstraction events
|
|
||||||
|
|
||||||
4. **Refactor Classes**
|
|
||||||
- Accept abstractions via constructor injection
|
|
||||||
- Create factory methods for production use
|
|
||||||
|
|
||||||
5. **Write Tests**
|
|
||||||
- Mock abstractions using Moq
|
|
||||||
- Test business logic independently
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### DO:
|
|
||||||
- Keep abstractions simple and focused
|
|
||||||
- Test business logic, not Crestron SDK behavior
|
|
||||||
- Use dependency injection consistently
|
|
||||||
- Mock at the abstraction boundary
|
|
||||||
- Test event handling and state changes
|
|
||||||
|
|
||||||
### DON'T:
|
|
||||||
- Try to mock Crestron types directly
|
|
||||||
- Include hardware-dependent code in tests
|
|
||||||
- Mix business logic with hardware interaction
|
|
||||||
- Create overly complex abstractions
|
|
||||||
|
|
||||||
## Tools Used
|
|
||||||
|
|
||||||
- **xUnit** - Test framework
|
|
||||||
- **Moq** - Mocking framework
|
|
||||||
- **FluentAssertions** - Assertion library
|
|
||||||
- **Coverlet** - Code coverage
|
|
||||||
- **GitHub Actions** - CI/CD
|
|
||||||
|
|
||||||
## Adding New Tests
|
|
||||||
|
|
||||||
1. Create test file in appropriate folder
|
|
||||||
2. Follow naming convention: `[ClassName]Tests.cs`
|
|
||||||
3. Use Arrange-Act-Assert pattern
|
|
||||||
4. Include both positive and negative test cases
|
|
||||||
5. Test edge cases and error conditions
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues:
|
|
||||||
|
|
||||||
**Tests fail with "Type not found" errors**
|
|
||||||
- Ensure abstractions are properly defined
|
|
||||||
- Check project references
|
|
||||||
|
|
||||||
**Mocked events not firing**
|
|
||||||
- Use `Mock.Raise()` to trigger events
|
|
||||||
- Verify event subscription syntax
|
|
||||||
|
|
||||||
**Coverage not generating**
|
|
||||||
- Run with `--collect:"XPlat Code Coverage"`
|
|
||||||
- Check .gitignore isn't excluding coverage files
|
|
||||||
|
|
||||||
## Example Test Project Structure
|
|
||||||
```
|
|
||||||
tests/
|
|
||||||
├── PepperDash.Essentials.Core.Tests/
|
|
||||||
│ ├── Abstractions/
|
|
||||||
│ │ ├── DigitalInputTests.cs
|
|
||||||
│ │ └── VersiPortTests.cs
|
|
||||||
│ ├── Devices/
|
|
||||||
│ │ └── CrestronProcessorTests.cs
|
|
||||||
│ └── PepperDash.Essentials.Core.Tests.csproj
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.Loader;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace CrestronMockGenerator
|
|
||||||
{
|
|
||||||
public class AssemblyAnalyzer
|
|
||||||
{
|
|
||||||
private readonly string _assemblyPath;
|
|
||||||
private readonly string _xmlDocPath;
|
|
||||||
private Dictionary<string, string> _xmlDocumentation = new();
|
|
||||||
|
|
||||||
public AssemblyAnalyzer(string assemblyPath)
|
|
||||||
{
|
|
||||||
_assemblyPath = assemblyPath;
|
|
||||||
_xmlDocPath = Path.ChangeExtension(assemblyPath, ".xml");
|
|
||||||
|
|
||||||
if (File.Exists(_xmlDocPath))
|
|
||||||
{
|
|
||||||
LoadXmlDocumentation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadXmlDocumentation()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var doc = XDocument.Load(_xmlDocPath);
|
|
||||||
var members = doc.Descendants("member");
|
|
||||||
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
var name = member.Attribute("name")?.Value;
|
|
||||||
var summary = member.Element("summary")?.Value?.Trim();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(summary))
|
|
||||||
{
|
|
||||||
_xmlDocumentation[name] = summary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error loading XML documentation: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssemblyInfo AnalyzeAssembly()
|
|
||||||
{
|
|
||||||
var assemblyInfo = new AssemblyInfo
|
|
||||||
{
|
|
||||||
Name = Path.GetFileNameWithoutExtension(_assemblyPath),
|
|
||||||
Types = new List<TypeInfo>()
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Use MetadataLoadContext to load assembly without dependencies
|
|
||||||
var resolver = new PathAssemblyResolver(new[] { _assemblyPath });
|
|
||||||
var mlc = new MetadataLoadContext(resolver);
|
|
||||||
|
|
||||||
var assembly = mlc.LoadFromAssemblyPath(_assemblyPath);
|
|
||||||
|
|
||||||
foreach (var type in assembly.GetExportedTypes())
|
|
||||||
{
|
|
||||||
if (type.IsSpecialName || type.Name.Contains("<>"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var typeInfo = AnalyzeType(type);
|
|
||||||
if (typeInfo != null)
|
|
||||||
{
|
|
||||||
assemblyInfo.Types.Add(typeInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error analyzing assembly: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return assemblyInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeInfo? AnalyzeType(Type type)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var typeInfo = new TypeInfo
|
|
||||||
{
|
|
||||||
Name = type.Name,
|
|
||||||
Namespace = type.Namespace ?? "",
|
|
||||||
FullName = type.FullName ?? "",
|
|
||||||
IsInterface = type.IsInterface,
|
|
||||||
IsAbstract = type.IsAbstract,
|
|
||||||
IsSealed = type.IsSealed,
|
|
||||||
IsEnum = type.IsEnum,
|
|
||||||
IsClass = type.IsClass,
|
|
||||||
BaseType = type.BaseType?.FullName,
|
|
||||||
Documentation = GetDocumentation($"T:{type.FullName}"),
|
|
||||||
Properties = new List<PropertyInfo>(),
|
|
||||||
Methods = new List<MethodInfo>(),
|
|
||||||
Events = new List<EventInfo>(),
|
|
||||||
Fields = new List<FieldInfo>(),
|
|
||||||
Interfaces = type.GetInterfaces().Select(i => i.FullName).Where(n => n != null).ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Analyze properties
|
|
||||||
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
||||||
{
|
|
||||||
typeInfo.Properties.Add(new PropertyInfo
|
|
||||||
{
|
|
||||||
Name = prop.Name,
|
|
||||||
Type = prop.PropertyType.FullName ?? "",
|
|
||||||
CanRead = prop.CanRead,
|
|
||||||
CanWrite = prop.CanWrite,
|
|
||||||
IsStatic = prop.GetMethod?.IsStatic ?? prop.SetMethod?.IsStatic ?? false,
|
|
||||||
Documentation = GetDocumentation($"P:{type.FullName}.{prop.Name}")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze methods
|
|
||||||
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
|
||||||
.Where(m => !m.IsSpecialName))
|
|
||||||
{
|
|
||||||
var parameters = method.GetParameters().Select(p => new ParameterInfo
|
|
||||||
{
|
|
||||||
Name = p.Name ?? "",
|
|
||||||
Type = p.ParameterType.FullName ?? "",
|
|
||||||
IsOut = p.IsOut,
|
|
||||||
IsRef = p.ParameterType.IsByRef && !p.IsOut,
|
|
||||||
HasDefaultValue = p.HasDefaultValue,
|
|
||||||
DefaultValue = p.HasDefaultValue ? p.DefaultValue?.ToString() : null
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
typeInfo.Methods.Add(new MethodInfo
|
|
||||||
{
|
|
||||||
Name = method.Name,
|
|
||||||
ReturnType = method.ReturnType.FullName ?? "",
|
|
||||||
IsStatic = method.IsStatic,
|
|
||||||
IsVirtual = method.IsVirtual,
|
|
||||||
IsAbstract = method.IsAbstract,
|
|
||||||
Parameters = parameters,
|
|
||||||
Documentation = GetDocumentation($"M:{type.FullName}.{method.Name}")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze events
|
|
||||||
foreach (var evt in type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
||||||
{
|
|
||||||
typeInfo.Events.Add(new EventInfo
|
|
||||||
{
|
|
||||||
Name = evt.Name,
|
|
||||||
EventHandlerType = evt.EventHandlerType?.FullName ?? "",
|
|
||||||
Documentation = GetDocumentation($"E:{type.FullName}.{evt.Name}")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze fields (for enums)
|
|
||||||
if (type.IsEnum)
|
|
||||||
{
|
|
||||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
|
|
||||||
{
|
|
||||||
if (field.Name == "value__") continue;
|
|
||||||
|
|
||||||
typeInfo.Fields.Add(new FieldInfo
|
|
||||||
{
|
|
||||||
Name = field.Name,
|
|
||||||
Type = field.FieldType.FullName ?? "",
|
|
||||||
Value = field.GetRawConstantValue()?.ToString(),
|
|
||||||
Documentation = GetDocumentation($"F:{type.FullName}.{field.Name}")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeInfo;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error analyzing type {type.Name}: {ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetDocumentation(string memberName)
|
|
||||||
{
|
|
||||||
return _xmlDocumentation.TryGetValue(memberName, out var doc) ? doc : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AssemblyInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public List<TypeInfo> Types { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TypeInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Namespace { get; set; } = "";
|
|
||||||
public string FullName { get; set; } = "";
|
|
||||||
public bool IsInterface { get; set; }
|
|
||||||
public bool IsAbstract { get; set; }
|
|
||||||
public bool IsSealed { get; set; }
|
|
||||||
public bool IsEnum { get; set; }
|
|
||||||
public bool IsClass { get; set; }
|
|
||||||
public string? BaseType { get; set; }
|
|
||||||
public List<string> Interfaces { get; set; } = new();
|
|
||||||
public string Documentation { get; set; } = "";
|
|
||||||
public List<PropertyInfo> Properties { get; set; } = new();
|
|
||||||
public List<MethodInfo> Methods { get; set; } = new();
|
|
||||||
public List<EventInfo> Events { get; set; } = new();
|
|
||||||
public List<FieldInfo> Fields { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PropertyInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Type { get; set; } = "";
|
|
||||||
public bool CanRead { get; set; }
|
|
||||||
public bool CanWrite { get; set; }
|
|
||||||
public bool IsStatic { get; set; }
|
|
||||||
public string Documentation { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MethodInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string ReturnType { get; set; } = "";
|
|
||||||
public bool IsStatic { get; set; }
|
|
||||||
public bool IsVirtual { get; set; }
|
|
||||||
public bool IsAbstract { get; set; }
|
|
||||||
public List<ParameterInfo> Parameters { get; set; } = new();
|
|
||||||
public string Documentation { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ParameterInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Type { get; set; } = "";
|
|
||||||
public bool IsOut { get; set; }
|
|
||||||
public bool IsRef { get; set; }
|
|
||||||
public bool HasDefaultValue { get; set; }
|
|
||||||
public string? DefaultValue { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EventInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string EventHandlerType { get; set; } = "";
|
|
||||||
public string Documentation { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FieldInfo
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Type { get; set; } = "";
|
|
||||||
public string? Value { get; set; }
|
|
||||||
public string Documentation { get; set; } = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,468 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|
||||||
|
|
||||||
namespace CrestronMockGenerator
|
|
||||||
{
|
|
||||||
public class MockGenerator
|
|
||||||
{
|
|
||||||
public string GenerateMockClass(TypeInfo typeInfo)
|
|
||||||
{
|
|
||||||
var compilationUnit = CompilationUnit();
|
|
||||||
|
|
||||||
// Add usings
|
|
||||||
compilationUnit = compilationUnit.AddUsings(
|
|
||||||
UsingDirective(ParseName("System")),
|
|
||||||
UsingDirective(ParseName("System.Collections.Generic")),
|
|
||||||
UsingDirective(ParseName("System.Linq"))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create namespace
|
|
||||||
var namespaceDeclaration = NamespaceDeclaration(ParseName(typeInfo.Namespace));
|
|
||||||
|
|
||||||
// Add XML documentation if available
|
|
||||||
var trivia = new List<SyntaxTrivia>();
|
|
||||||
if (!string.IsNullOrEmpty(typeInfo.Documentation))
|
|
||||||
{
|
|
||||||
trivia.Add(Comment($"/// <summary>"));
|
|
||||||
trivia.Add(Comment($"/// {typeInfo.Documentation}"));
|
|
||||||
trivia.Add(Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create type declaration
|
|
||||||
MemberDeclarationSyntax typeDeclaration;
|
|
||||||
|
|
||||||
if (typeInfo.IsEnum)
|
|
||||||
{
|
|
||||||
typeDeclaration = GenerateEnum(typeInfo);
|
|
||||||
}
|
|
||||||
else if (typeInfo.IsInterface)
|
|
||||||
{
|
|
||||||
typeDeclaration = GenerateInterface(typeInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
typeDeclaration = GenerateClass(typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trivia.Any())
|
|
||||||
{
|
|
||||||
typeDeclaration = typeDeclaration.WithLeadingTrivia(trivia);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespaceDeclaration = namespaceDeclaration.AddMembers(typeDeclaration);
|
|
||||||
compilationUnit = compilationUnit.AddMembers(namespaceDeclaration);
|
|
||||||
|
|
||||||
// Format the code
|
|
||||||
var workspace = new AdhocWorkspace();
|
|
||||||
var formattedNode = Microsoft.CodeAnalysis.Formatting.Formatter.Format(
|
|
||||||
compilationUnit,
|
|
||||||
workspace);
|
|
||||||
|
|
||||||
return formattedNode.ToFullString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private EnumDeclarationSyntax GenerateEnum(TypeInfo typeInfo)
|
|
||||||
{
|
|
||||||
var enumDeclaration = EnumDeclaration(typeInfo.Name)
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
foreach (var field in typeInfo.Fields)
|
|
||||||
{
|
|
||||||
var member = EnumMemberDeclaration(field.Name);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(field.Value))
|
|
||||||
{
|
|
||||||
member = member.WithEqualsValue(
|
|
||||||
EqualsValueClause(ParseExpression(field.Value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(field.Documentation))
|
|
||||||
{
|
|
||||||
member = member.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {field.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
enumDeclaration = enumDeclaration.AddMembers(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
return enumDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InterfaceDeclarationSyntax GenerateInterface(TypeInfo typeInfo)
|
|
||||||
{
|
|
||||||
var interfaceDeclaration = InterfaceDeclaration(typeInfo.Name)
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
// Add base interfaces
|
|
||||||
if (typeInfo.Interfaces.Any())
|
|
||||||
{
|
|
||||||
var baseList = BaseList();
|
|
||||||
foreach (var baseInterface in typeInfo.Interfaces)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(baseInterface);
|
|
||||||
baseList = baseList.AddTypes(SimpleBaseType(ParseTypeName(typeName)));
|
|
||||||
}
|
|
||||||
interfaceDeclaration = interfaceDeclaration.WithBaseList(baseList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add properties
|
|
||||||
foreach (var property in typeInfo.Properties)
|
|
||||||
{
|
|
||||||
var propertyDeclaration = GenerateInterfaceProperty(property);
|
|
||||||
interfaceDeclaration = interfaceDeclaration.AddMembers(propertyDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add methods
|
|
||||||
foreach (var method in typeInfo.Methods)
|
|
||||||
{
|
|
||||||
var methodDeclaration = GenerateInterfaceMethod(method);
|
|
||||||
interfaceDeclaration = interfaceDeclaration.AddMembers(methodDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add events
|
|
||||||
foreach (var evt in typeInfo.Events)
|
|
||||||
{
|
|
||||||
var eventDeclaration = GenerateInterfaceEvent(evt);
|
|
||||||
interfaceDeclaration = interfaceDeclaration.AddMembers(eventDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return interfaceDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassDeclarationSyntax GenerateClass(TypeInfo typeInfo)
|
|
||||||
{
|
|
||||||
var classDeclaration = ClassDeclaration(typeInfo.Name)
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
if (typeInfo.IsAbstract)
|
|
||||||
{
|
|
||||||
classDeclaration = classDeclaration.AddModifiers(Token(SyntaxKind.AbstractKeyword));
|
|
||||||
}
|
|
||||||
else if (typeInfo.IsSealed)
|
|
||||||
{
|
|
||||||
classDeclaration = classDeclaration.AddModifiers(Token(SyntaxKind.SealedKeyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add base class and interfaces
|
|
||||||
var baseTypes = new List<string>();
|
|
||||||
if (!string.IsNullOrEmpty(typeInfo.BaseType) && typeInfo.BaseType != "System.Object")
|
|
||||||
{
|
|
||||||
baseTypes.Add(typeInfo.BaseType);
|
|
||||||
}
|
|
||||||
baseTypes.AddRange(typeInfo.Interfaces);
|
|
||||||
|
|
||||||
if (baseTypes.Any())
|
|
||||||
{
|
|
||||||
var baseList = BaseList();
|
|
||||||
foreach (var baseType in baseTypes)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(baseType);
|
|
||||||
baseList = baseList.AddTypes(SimpleBaseType(ParseTypeName(typeName)));
|
|
||||||
}
|
|
||||||
classDeclaration = classDeclaration.WithBaseList(baseList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add properties
|
|
||||||
foreach (var property in typeInfo.Properties)
|
|
||||||
{
|
|
||||||
var propertyDeclaration = GenerateProperty(property);
|
|
||||||
classDeclaration = classDeclaration.AddMembers(propertyDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add methods
|
|
||||||
foreach (var method in typeInfo.Methods)
|
|
||||||
{
|
|
||||||
var methodDeclaration = GenerateMethod(method, typeInfo.IsAbstract);
|
|
||||||
classDeclaration = classDeclaration.AddMembers(methodDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add events
|
|
||||||
foreach (var evt in typeInfo.Events)
|
|
||||||
{
|
|
||||||
var eventDeclaration = GenerateEvent(evt);
|
|
||||||
classDeclaration = classDeclaration.AddMembers(eventDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return classDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyDeclarationSyntax GenerateProperty(PropertyInfo property)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(property.Type);
|
|
||||||
var propertyDeclaration = PropertyDeclaration(ParseTypeName(typeName), property.Name)
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
if (property.IsStatic)
|
|
||||||
{
|
|
||||||
propertyDeclaration = propertyDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
var accessors = new List<AccessorDeclarationSyntax>();
|
|
||||||
|
|
||||||
if (property.CanRead)
|
|
||||||
{
|
|
||||||
accessors.Add(AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
|
||||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property.CanWrite)
|
|
||||||
{
|
|
||||||
accessors.Add(AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
|
|
||||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
|
|
||||||
}
|
|
||||||
|
|
||||||
propertyDeclaration = propertyDeclaration.WithAccessorList(
|
|
||||||
AccessorList(List(accessors)));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(property.Documentation))
|
|
||||||
{
|
|
||||||
propertyDeclaration = propertyDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {property.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return propertyDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyDeclarationSyntax GenerateInterfaceProperty(PropertyInfo property)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(property.Type);
|
|
||||||
var propertyDeclaration = PropertyDeclaration(ParseTypeName(typeName), property.Name);
|
|
||||||
|
|
||||||
var accessors = new List<AccessorDeclarationSyntax>();
|
|
||||||
|
|
||||||
if (property.CanRead)
|
|
||||||
{
|
|
||||||
accessors.Add(AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
|
||||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property.CanWrite)
|
|
||||||
{
|
|
||||||
accessors.Add(AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
|
|
||||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
|
|
||||||
}
|
|
||||||
|
|
||||||
propertyDeclaration = propertyDeclaration.WithAccessorList(
|
|
||||||
AccessorList(List(accessors)));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(property.Documentation))
|
|
||||||
{
|
|
||||||
propertyDeclaration = propertyDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {property.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return propertyDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodDeclarationSyntax GenerateMethod(MethodInfo method, bool isAbstractClass)
|
|
||||||
{
|
|
||||||
var returnTypeName = GetSimpleTypeName(method.ReturnType);
|
|
||||||
var methodDeclaration = MethodDeclaration(ParseTypeName(returnTypeName), method.Name)
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
if (method.IsStatic)
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword));
|
|
||||||
}
|
|
||||||
else if (method.IsAbstract && isAbstractClass)
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.AbstractKeyword));
|
|
||||||
}
|
|
||||||
else if (method.IsVirtual)
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.VirtualKeyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add parameters
|
|
||||||
var parameters = new List<ParameterSyntax>();
|
|
||||||
foreach (var param in method.Parameters)
|
|
||||||
{
|
|
||||||
var paramTypeName = GetSimpleTypeName(param.Type);
|
|
||||||
var parameter = Parameter(Identifier(param.Name))
|
|
||||||
.WithType(ParseTypeName(paramTypeName));
|
|
||||||
|
|
||||||
if (param.IsOut)
|
|
||||||
{
|
|
||||||
parameter = parameter.AddModifiers(Token(SyntaxKind.OutKeyword));
|
|
||||||
}
|
|
||||||
else if (param.IsRef)
|
|
||||||
{
|
|
||||||
parameter = parameter.AddModifiers(Token(SyntaxKind.RefKeyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.HasDefaultValue && !string.IsNullOrEmpty(param.DefaultValue))
|
|
||||||
{
|
|
||||||
parameter = parameter.WithDefault(
|
|
||||||
EqualsValueClause(ParseExpression(param.DefaultValue)));
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters.Add(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
methodDeclaration = methodDeclaration.WithParameterList(
|
|
||||||
ParameterList(SeparatedList(parameters)));
|
|
||||||
|
|
||||||
// Add body or semicolon
|
|
||||||
if (method.IsAbstract && isAbstractClass)
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var statements = new List<StatementSyntax>();
|
|
||||||
|
|
||||||
// Add default implementation
|
|
||||||
if (returnTypeName != "void")
|
|
||||||
{
|
|
||||||
statements.Add(ParseStatement($"throw new NotImplementedException();"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statements.Add(ParseStatement($"// Mock implementation"));
|
|
||||||
}
|
|
||||||
|
|
||||||
methodDeclaration = methodDeclaration.WithBody(Block(statements));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(method.Documentation))
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {method.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodDeclarationSyntax GenerateInterfaceMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
var returnTypeName = GetSimpleTypeName(method.ReturnType);
|
|
||||||
var methodDeclaration = MethodDeclaration(ParseTypeName(returnTypeName), method.Name);
|
|
||||||
|
|
||||||
// Add parameters
|
|
||||||
var parameters = new List<ParameterSyntax>();
|
|
||||||
foreach (var param in method.Parameters)
|
|
||||||
{
|
|
||||||
var paramTypeName = GetSimpleTypeName(param.Type);
|
|
||||||
var parameter = Parameter(Identifier(param.Name))
|
|
||||||
.WithType(ParseTypeName(paramTypeName));
|
|
||||||
|
|
||||||
if (param.IsOut)
|
|
||||||
{
|
|
||||||
parameter = parameter.AddModifiers(Token(SyntaxKind.OutKeyword));
|
|
||||||
}
|
|
||||||
else if (param.IsRef)
|
|
||||||
{
|
|
||||||
parameter = parameter.AddModifiers(Token(SyntaxKind.RefKeyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters.Add(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
methodDeclaration = methodDeclaration
|
|
||||||
.WithParameterList(ParameterList(SeparatedList(parameters)))
|
|
||||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(method.Documentation))
|
|
||||||
{
|
|
||||||
methodDeclaration = methodDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {method.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EventFieldDeclarationSyntax GenerateEvent(EventInfo evt)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(evt.EventHandlerType);
|
|
||||||
var eventDeclaration = EventFieldDeclaration(
|
|
||||||
VariableDeclaration(ParseTypeName(typeName))
|
|
||||||
.AddVariables(VariableDeclarator(evt.Name)))
|
|
||||||
.AddModifiers(Token(SyntaxKind.PublicKeyword));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(evt.Documentation))
|
|
||||||
{
|
|
||||||
eventDeclaration = eventDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {evt.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EventFieldDeclarationSyntax GenerateInterfaceEvent(EventInfo evt)
|
|
||||||
{
|
|
||||||
var typeName = GetSimpleTypeName(evt.EventHandlerType);
|
|
||||||
var eventDeclaration = EventFieldDeclaration(
|
|
||||||
VariableDeclaration(ParseTypeName(typeName))
|
|
||||||
.AddVariables(VariableDeclarator(evt.Name)));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(evt.Documentation))
|
|
||||||
{
|
|
||||||
eventDeclaration = eventDeclaration.WithLeadingTrivia(
|
|
||||||
Comment($"/// <summary>"),
|
|
||||||
Comment($"/// {evt.Documentation}"),
|
|
||||||
Comment($"/// </summary>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetSimpleTypeName(string fullTypeName)
|
|
||||||
{
|
|
||||||
// Convert full type names to simple names
|
|
||||||
var typeMappings = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["System.Void"] = "void",
|
|
||||||
["System.String"] = "string",
|
|
||||||
["System.Int32"] = "int",
|
|
||||||
["System.Int64"] = "long",
|
|
||||||
["System.Int16"] = "short",
|
|
||||||
["System.UInt32"] = "uint",
|
|
||||||
["System.UInt64"] = "ulong",
|
|
||||||
["System.UInt16"] = "ushort",
|
|
||||||
["System.Byte"] = "byte",
|
|
||||||
["System.SByte"] = "sbyte",
|
|
||||||
["System.Boolean"] = "bool",
|
|
||||||
["System.Single"] = "float",
|
|
||||||
["System.Double"] = "double",
|
|
||||||
["System.Decimal"] = "decimal",
|
|
||||||
["System.Object"] = "object",
|
|
||||||
["System.Char"] = "char"
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeMappings.TryGetValue(fullTypeName, out var simpleName))
|
|
||||||
{
|
|
||||||
return simpleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle generic types
|
|
||||||
if (fullTypeName.Contains('`'))
|
|
||||||
{
|
|
||||||
// Simplified generic handling
|
|
||||||
return "object";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return last part of the type name
|
|
||||||
var lastDot = fullTypeName.LastIndexOf('.');
|
|
||||||
return lastDot >= 0 ? fullTypeName.Substring(lastDot + 1) : fullTypeName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace CrestronMockGenerator
|
|
||||||
{
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Crestron Mock Library Generator");
|
|
||||||
Console.WriteLine("================================");
|
|
||||||
|
|
||||||
if (args.Length < 2)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Usage: CrestronMockGenerator <input-assembly-path> <output-directory>");
|
|
||||||
Console.WriteLine("Example: CrestronMockGenerator /path/to/SimplSharp.dll /output/mocks");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inputPath = args[0];
|
|
||||||
var outputDir = args[1];
|
|
||||||
|
|
||||||
if (!File.Exists(inputPath))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error: Assembly file not found: {inputPath}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(outputDir);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Analyze the assembly
|
|
||||||
Console.WriteLine($"Analyzing assembly: {inputPath}");
|
|
||||||
var analyzer = new AssemblyAnalyzer(inputPath);
|
|
||||||
var assemblyInfo = analyzer.AnalyzeAssembly();
|
|
||||||
|
|
||||||
Console.WriteLine($"Found {assemblyInfo.Types.Count} types");
|
|
||||||
|
|
||||||
// Save assembly metadata as JSON
|
|
||||||
var metadataPath = Path.Combine(outputDir, $"{assemblyInfo.Name}.metadata.json");
|
|
||||||
var json = JsonConvert.SerializeObject(assemblyInfo, Formatting.Indented);
|
|
||||||
File.WriteAllText(metadataPath, json);
|
|
||||||
Console.WriteLine($"Saved metadata to: {metadataPath}");
|
|
||||||
|
|
||||||
// Generate mock classes
|
|
||||||
var generator = new MockGenerator();
|
|
||||||
var generatedFiles = new List<string>();
|
|
||||||
|
|
||||||
// Group types by namespace
|
|
||||||
var namespaceGroups = assemblyInfo.Types.GroupBy(t => t.Namespace);
|
|
||||||
|
|
||||||
foreach (var namespaceGroup in namespaceGroups)
|
|
||||||
{
|
|
||||||
var namespacePath = Path.Combine(outputDir, namespaceGroup.Key.Replace('.', Path.DirectorySeparatorChar));
|
|
||||||
Directory.CreateDirectory(namespacePath);
|
|
||||||
|
|
||||||
foreach (var typeInfo in namespaceGroup)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Generating mock for: {typeInfo.FullName}");
|
|
||||||
var mockCode = generator.GenerateMockClass(typeInfo);
|
|
||||||
|
|
||||||
var fileName = $"{typeInfo.Name}.cs";
|
|
||||||
var filePath = Path.Combine(namespacePath, fileName);
|
|
||||||
|
|
||||||
File.WriteAllText(filePath, mockCode);
|
|
||||||
generatedFiles.Add(filePath);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error generating mock for {typeInfo.Name}: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate project file
|
|
||||||
GenerateProjectFile(outputDir, assemblyInfo.Name);
|
|
||||||
|
|
||||||
Console.WriteLine($"\nGeneration complete!");
|
|
||||||
Console.WriteLine($"Generated {generatedFiles.Count} mock files");
|
|
||||||
Console.WriteLine($"Output directory: {outputDir}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error: {ex.Message}");
|
|
||||||
Console.WriteLine(ex.StackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GenerateProjectFile(string outputDir, string assemblyName)
|
|
||||||
{
|
|
||||||
var projectContent = @"<Project Sdk=""Microsoft.NET.Sdk"">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AssemblyName>" + assemblyName + @".Mock</AssemblyName>
|
|
||||||
<RootNamespace>" + assemblyName + @"</RootNamespace>
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<PackageId>" + assemblyName + @".Mock</PackageId>
|
|
||||||
<Version>1.0.0</Version>
|
|
||||||
<Description>Mock implementation of " + assemblyName + @" for testing</Description>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>";
|
|
||||||
|
|
||||||
var projectPath = Path.Combine(outputDir, $"{assemblyName}.Mock.csproj");
|
|
||||||
File.WriteAllText(projectPath, projectContent);
|
|
||||||
Console.WriteLine($"Generated project file: {projectPath}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user