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>
135 lines
3.4 KiB
C#
135 lines
3.4 KiB
C#
using FluentAssertions;
|
|
using HihaArvio.Models;
|
|
using HihaArvio.Services;
|
|
using HihaArvio.Services.Interfaces;
|
|
|
|
namespace HihaArvio.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// Tests for desktop accelerometer service implementation.
|
|
/// Desktop uses simulated accelerometer data for testing purposes.
|
|
/// </summary>
|
|
public class DesktopAccelerometerServiceTests : AccelerometerServiceContractTestsBase
|
|
{
|
|
protected override IAccelerometerService CreateService()
|
|
{
|
|
return new DesktopAccelerometerService();
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ShouldInitializeWithStoppedState()
|
|
{
|
|
// Arrange & Act
|
|
var service = new DesktopAccelerometerService();
|
|
|
|
// Assert
|
|
// Service should be in stopped state initially
|
|
}
|
|
|
|
[Fact]
|
|
public void IsSupported_ShouldReturnTrue()
|
|
{
|
|
// Arrange
|
|
var service = new DesktopAccelerometerService();
|
|
|
|
// Act
|
|
var isSupported = service.IsSupported;
|
|
|
|
// Assert
|
|
// Desktop accelerometer service is always supported (simulated)
|
|
isSupported.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Start_Multiple_ShouldNotThrow()
|
|
{
|
|
// Arrange
|
|
var service = new DesktopAccelerometerService();
|
|
|
|
// Act
|
|
var act = () =>
|
|
{
|
|
service.Start();
|
|
service.Start(); // Call twice
|
|
};
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
|
|
// Cleanup
|
|
service.Stop();
|
|
}
|
|
|
|
[Fact]
|
|
public void Stop_Multiple_ShouldNotThrow()
|
|
{
|
|
// Arrange
|
|
var service = new DesktopAccelerometerService();
|
|
service.Start();
|
|
|
|
// Act
|
|
var act = () =>
|
|
{
|
|
service.Stop();
|
|
service.Stop(); // Call twice
|
|
};
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadingChanged_AfterStart_ShouldProvideSimulatedReadings()
|
|
{
|
|
// Arrange
|
|
var service = new DesktopAccelerometerService();
|
|
SensorReading? receivedReading = null;
|
|
var eventFired = false;
|
|
|
|
service.ReadingChanged += (sender, reading) =>
|
|
{
|
|
receivedReading = reading;
|
|
eventFired = true;
|
|
};
|
|
|
|
// Act
|
|
service.Start();
|
|
await Task.Delay(200); // Wait for simulated readings to fire
|
|
|
|
// Assert
|
|
// Desktop service provides simulated readings periodically
|
|
eventFired.Should().BeTrue();
|
|
receivedReading.Should().NotBeNull();
|
|
receivedReading!.X.Should().BeInRange(-2.0, 2.0);
|
|
receivedReading.Y.Should().BeInRange(-2.0, 2.0);
|
|
receivedReading.Z.Should().BeInRange(-2.0, 2.0);
|
|
|
|
// Cleanup
|
|
service.Stop();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Stop_ShouldStopGeneratingReadings()
|
|
{
|
|
// Arrange
|
|
var service = new DesktopAccelerometerService();
|
|
var readingCount = 0;
|
|
|
|
service.ReadingChanged += (sender, reading) => readingCount++;
|
|
|
|
// Act
|
|
service.Start();
|
|
await Task.Delay(100); // Wait for several readings
|
|
var countBeforeStop = readingCount;
|
|
|
|
service.Stop();
|
|
await Task.Delay(100); // Wait to verify no more readings
|
|
|
|
// Assert
|
|
// Should have received some readings before stop
|
|
countBeforeStop.Should().BeGreaterThan(0);
|
|
// Count should not increase significantly after stop (allow 1-2 in-flight events)
|
|
readingCount.Should().BeLessThanOrEqualTo(countBeforeStop + 2);
|
|
}
|
|
}
|