mirror of
https://github.com/ivuorinen/hiha-arvio.git
synced 2026-01-26 03:14:00 +00:00
Implemented platform-agnostic accelerometer abstraction with iOS and desktop implementations: **IAccelerometerService Interface:** - Platform-agnostic abstraction for sensor input - SensorReading model (renamed from AccelerometerData to avoid MAUI conflict) - X, Y, Z acceleration values in g-force units - ReadingChanged event for continuous data stream - Start/Stop methods for sensor control - IsSupported property for platform capability detection **IosAccelerometerService (10 tests):** - Uses MAUI's Microsoft.Maui.Devices.Sensors.Accelerometer - Works on iOS devices and simulator (provides simulated data) - Conditional compilation (#if IOS || MACCATALYST) - Converts MAUI AccelerometerData to our SensorReading - Graceful failure when sensor unavailable - Pragma directives for conditional warnings **DesktopAccelerometerService (11 tests):** - Timer-based simulated sensor readings (~60Hz) - Generates realistic noise around 1g gravity baseline - Device-at-rest simulation: X≈0, Y≈0, Z≈1 - Includes SimulateShake() method for manual testing - Always reports IsSupported=true (simulation available) **ShakeDetectionService Integration:** - Updated constructor to require IAccelerometerService (breaking change) - StartMonitoring: subscribes to ReadingChanged, calls accelerometer.Start() - StopMonitoring: unsubscribes and calls accelerometer.Stop() - OnAccelerometerReadingChanged: pipes readings to ProcessAccelerometerReading - All existing shake detection logic unchanged **Platform-Specific DI:** - MauiProgram.cs uses conditional compilation - iOS/macOS Catalyst: IosAccelerometerService - Desktop/other platforms: DesktopAccelerometerService - All services registered as Singleton **Test Updates:** - ShakeDetectionServiceTests: now uses NSubstitute mock accelerometer - ServiceIntegrationTests: uses DesktopAccelerometerService - AccelerometerServiceContractTests: base class for implementation tests - IosAccelerometerServiceTests: 10 tests with platform-aware assertions - DesktopAccelerometerServiceTests: 11 tests with async timing **Technical Details:** - SensorReading record type with required init properties - Value equality for SensorReading (record type benefit) - Timer disposal in DesktopAccelerometerService - Event subscription safety (check for null, unsubscribe on stop) - 189 tests passing (165 previous + 24 accelerometer) - 0 warnings, 0 errors across all platforms Build verified: net8.0, iOS (net8.0-ios), macOS Catalyst (net8.0-maccatalyst) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
136 lines
3.5 KiB
C#
136 lines
3.5 KiB
C#
using FluentAssertions;
|
|
using HihaArvio.Models;
|
|
using HihaArvio.Services.Interfaces;
|
|
|
|
namespace HihaArvio.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// Tests for IAccelerometerService interface contract.
|
|
/// Platform-specific implementations must satisfy these tests.
|
|
/// </summary>
|
|
public class AccelerometerServiceContractTests
|
|
{
|
|
[Fact]
|
|
public void SensorReading_ShouldHaveXYZProperties()
|
|
{
|
|
// Arrange & Act
|
|
var data = new SensorReading
|
|
{
|
|
X = 1.5,
|
|
Y = 2.5,
|
|
Z = 3.5
|
|
};
|
|
|
|
// Assert
|
|
data.X.Should().Be(1.5);
|
|
data.Y.Should().Be(2.5);
|
|
data.Z.Should().Be(3.5);
|
|
}
|
|
|
|
[Fact]
|
|
public void SensorReading_ShouldBeInitOnly()
|
|
{
|
|
// Arrange
|
|
var data = new SensorReading { X = 1.0, Y = 2.0, Z = 3.0 };
|
|
|
|
// Assert - if this compiles, init-only properties work
|
|
data.X.Should().Be(1.0);
|
|
}
|
|
|
|
[Fact]
|
|
public void SensorReading_ShouldSupportEqualityComparison()
|
|
{
|
|
// Arrange
|
|
var data1 = new SensorReading { X = 1.0, Y = 2.0, Z = 3.0 };
|
|
var data2 = new SensorReading { X = 1.0, Y = 2.0, Z = 3.0 };
|
|
var data3 = new SensorReading { X = 1.0, Y = 2.0, Z = 4.0 };
|
|
|
|
// Assert
|
|
data1.Equals(data2).Should().BeTrue();
|
|
data1.Equals(data3).Should().BeFalse();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contract tests that all IAccelerometerService implementations must pass.
|
|
/// This ensures platform-specific implementations behave consistently.
|
|
/// </summary>
|
|
public abstract class AccelerometerServiceContractTestsBase
|
|
{
|
|
/// <summary>
|
|
/// Factory method for creating the service instance to test.
|
|
/// Implemented by platform-specific test classes.
|
|
/// </summary>
|
|
protected abstract IAccelerometerService CreateService();
|
|
|
|
[Fact]
|
|
public void Start_ShouldEnableMonitoring()
|
|
{
|
|
// Arrange
|
|
var service = CreateService();
|
|
|
|
// Act
|
|
service.Start();
|
|
|
|
// Assert
|
|
// Service should now be monitoring (implementation-specific verification)
|
|
// This is a smoke test - platform implementations will have more specific tests
|
|
}
|
|
|
|
[Fact]
|
|
public void Stop_ShouldDisableMonitoring()
|
|
{
|
|
// Arrange
|
|
var service = CreateService();
|
|
service.Start();
|
|
|
|
// Act
|
|
service.Stop();
|
|
|
|
// Assert
|
|
// Service should no longer be monitoring (implementation-specific verification)
|
|
// This is a smoke test - platform implementations will have more specific tests
|
|
}
|
|
|
|
[Fact]
|
|
public void Stop_WhenNotStarted_ShouldNotThrow()
|
|
{
|
|
// Arrange
|
|
var service = CreateService();
|
|
|
|
// Act
|
|
var act = () => service.Stop();
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsSupported_ShouldReturnBoolean()
|
|
{
|
|
// Arrange
|
|
var service = CreateService();
|
|
|
|
// Act
|
|
var isSupported = service.IsSupported;
|
|
|
|
// Assert
|
|
isSupported.Should().Be(isSupported); // Just verify it's a boolean value
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadingChanged_ShouldNotBeNull()
|
|
{
|
|
// Arrange
|
|
var service = CreateService();
|
|
|
|
// Act - Subscribe to event
|
|
var eventRaised = false;
|
|
service.ReadingChanged += (sender, data) => eventRaised = true;
|
|
|
|
// Assert - Event subscription should work without throwing
|
|
// Actual event raising is tested in platform-specific tests
|
|
eventRaised.Should().BeFalse(); // Not raised yet
|
|
}
|
|
}
|