206 lines
5.5 KiB
TypeScript
206 lines
5.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Frontend Test Suite for Arduino Simulation
|
||
|
|
* Tests AVRSimulator, PinManager, and component integration
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||
|
|
import { AVRSimulator } from '../simulation/AVRSimulator';
|
||
|
|
import { PinManager } from '../simulation/PinManager';
|
||
|
|
|
||
|
|
describe('PinManager', () => {
|
||
|
|
let pinManager: PinManager;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
pinManager = new PinManager();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should create instance successfully', () => {
|
||
|
|
expect(pinManager).toBeDefined();
|
||
|
|
expect(pinManager.getListenersCount()).toBe(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should register pin change listeners', () => {
|
||
|
|
const callback = vi.fn();
|
||
|
|
const unsubscribe = pinManager.onPinChange(13, callback);
|
||
|
|
|
||
|
|
expect(pinManager.getListenersCount()).toBe(1);
|
||
|
|
expect(typeof unsubscribe).toBe('function');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should notify listeners on port change', () => {
|
||
|
|
const callback = vi.fn();
|
||
|
|
pinManager.onPinChange(13, callback);
|
||
|
|
|
||
|
|
// Pin 13 is PORTB bit 5
|
||
|
|
// Change PORTB from 0x00 to 0x20 (bit 5 HIGH)
|
||
|
|
pinManager.updatePort('PORTB', 0x20, 0x00);
|
||
|
|
|
||
|
|
expect(callback).toHaveBeenCalledWith(13, true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should handle multiple listeners on same pin', () => {
|
||
|
|
const callback1 = vi.fn();
|
||
|
|
const callback2 = vi.fn();
|
||
|
|
|
||
|
|
pinManager.onPinChange(13, callback1);
|
||
|
|
pinManager.onPinChange(13, callback2);
|
||
|
|
|
||
|
|
pinManager.updatePort('PORTB', 0x20, 0x00);
|
||
|
|
|
||
|
|
expect(callback1).toHaveBeenCalledWith(13, true);
|
||
|
|
expect(callback2).toHaveBeenCalledWith(13, true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should unsubscribe listeners', () => {
|
||
|
|
const callback = vi.fn();
|
||
|
|
const unsubscribe = pinManager.onPinChange(13, callback);
|
||
|
|
|
||
|
|
unsubscribe();
|
||
|
|
pinManager.updatePort('PORTB', 0x20, 0x00);
|
||
|
|
|
||
|
|
expect(callback).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should track pin states', () => {
|
||
|
|
expect(pinManager.getPinState(13)).toBe(false);
|
||
|
|
|
||
|
|
pinManager.updatePort('PORTB', 0x20, 0x00);
|
||
|
|
expect(pinManager.getPinState(13)).toBe(true);
|
||
|
|
|
||
|
|
pinManager.updatePort('PORTB', 0x00, 0x20);
|
||
|
|
expect(pinManager.getPinState(13)).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should handle PORTC (analog pins)', () => {
|
||
|
|
const callback = vi.fn();
|
||
|
|
pinManager.onPinChange(14, callback); // A0 = pin 14
|
||
|
|
|
||
|
|
pinManager.updatePort('PORTC', 0x01, 0x00);
|
||
|
|
expect(callback).toHaveBeenCalledWith(14, true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should handle PORTD (digital pins 0-7)', () => {
|
||
|
|
const callback = vi.fn();
|
||
|
|
pinManager.onPinChange(7, callback);
|
||
|
|
|
||
|
|
pinManager.updatePort('PORTD', 0x80, 0x00); // bit 7
|
||
|
|
expect(callback).toHaveBeenCalledWith(7, true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('AVRSimulator', () => {
|
||
|
|
let simulator: AVRSimulator;
|
||
|
|
let pinManager: PinManager;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
pinManager = new PinManager();
|
||
|
|
simulator = new AVRSimulator(pinManager);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should create instance successfully', () => {
|
||
|
|
expect(simulator).toBeDefined();
|
||
|
|
expect(simulator.isRunning()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should load HEX file', () => {
|
||
|
|
// Minimal valid Intel HEX (empty program + EOF)
|
||
|
|
const hexContent = ':00000001FF';
|
||
|
|
|
||
|
|
expect(() => simulator.loadHex(hexContent)).not.toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should start and stop simulation', () => {
|
||
|
|
const hexContent = ':00000001FF';
|
||
|
|
simulator.loadHex(hexContent);
|
||
|
|
|
||
|
|
expect(simulator.isRunning()).toBe(false);
|
||
|
|
|
||
|
|
simulator.start();
|
||
|
|
expect(simulator.isRunning()).toBe(true);
|
||
|
|
|
||
|
|
simulator.stop();
|
||
|
|
expect(simulator.isRunning()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should reset simulation', () => {
|
||
|
|
const hexContent = ':00000001FF';
|
||
|
|
simulator.loadHex(hexContent);
|
||
|
|
simulator.start();
|
||
|
|
|
||
|
|
expect(simulator.isRunning()).toBe(true);
|
||
|
|
|
||
|
|
simulator.reset();
|
||
|
|
expect(simulator.isRunning()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should set simulation speed', () => {
|
||
|
|
expect(simulator.getSpeed()).toBe(1.0);
|
||
|
|
|
||
|
|
simulator.setSpeed(2.0);
|
||
|
|
expect(simulator.getSpeed()).toBe(2.0);
|
||
|
|
|
||
|
|
simulator.setSpeed(0.5);
|
||
|
|
expect(simulator.getSpeed()).toBe(0.5);
|
||
|
|
|
||
|
|
// Should clamp to min/max
|
||
|
|
simulator.setSpeed(100);
|
||
|
|
expect(simulator.getSpeed()).toBe(10.0);
|
||
|
|
|
||
|
|
simulator.setSpeed(0.01);
|
||
|
|
expect(simulator.getSpeed()).toBe(0.1);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('Simulator Integration', () => {
|
||
|
|
it('should propagate pin changes from CPU to components', async () => {
|
||
|
|
const pinManager = new PinManager();
|
||
|
|
const simulator = new AVRSimulator(pinManager);
|
||
|
|
|
||
|
|
// Register listener for pin 13 (LED_BUILTIN)
|
||
|
|
const pinChangeCallback = vi.fn();
|
||
|
|
pinManager.onPinChange(13, pinChangeCallback);
|
||
|
|
|
||
|
|
// Load a simple HEX that should toggle pin 13
|
||
|
|
// Note: This is a minimal test - real integration requires full compiled HEX
|
||
|
|
const hexContent = ':00000001FF';
|
||
|
|
simulator.loadHex(hexContent);
|
||
|
|
|
||
|
|
// Manually trigger port change to simulate CPU execution
|
||
|
|
// In real scenario, this would happen via avrInstruction() execution
|
||
|
|
pinManager.updatePort('PORTB', 0x20, 0x00);
|
||
|
|
|
||
|
|
expect(pinChangeCallback).toHaveBeenCalledWith(13, true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Export for documentation
|
||
|
|
export const testDocumentation = `
|
||
|
|
Frontend Test Suite Coverage:
|
||
|
|
|
||
|
|
1. PinManager Tests:
|
||
|
|
- Instance creation
|
||
|
|
- Listener registration and unsubscription
|
||
|
|
- Port change notifications
|
||
|
|
- Multiple listeners on same pin
|
||
|
|
- Pin state tracking
|
||
|
|
- All three ports (PORTB, PORTC, PORTD)
|
||
|
|
|
||
|
|
2. AVRSimulator Tests:
|
||
|
|
- Instance creation
|
||
|
|
- HEX file loading
|
||
|
|
- Start/Stop simulation
|
||
|
|
- Reset functionality
|
||
|
|
- Simulation speed control
|
||
|
|
|
||
|
|
3. Integration Tests:
|
||
|
|
- Pin change propagation from CPU to components
|
||
|
|
- Data flow through the simulation stack
|
||
|
|
|
||
|
|
To run these tests:
|
||
|
|
cd frontend
|
||
|
|
npm test
|
||
|
|
|
||
|
|
To run with coverage:
|
||
|
|
npm test -- --coverage
|
||
|
|
`;
|