/* eslint-disable no-magic-numbers */ import { jest, describe, test, expect } from '@jest/globals'; import { callsTo, anyUnexpectedCalls } from '../testing/awaitCalls.js'; import Controller from './controller.js'; function controllerWithMocks(engineURL, engineThreadURL, gameIdentifier) { globalThis.Worker = jest.fn().mockName('Worker').mockReturnValue({ addEventListener: jest.fn().mockName('addEventListener'), postMessage: jest.fn().mockName('postMessage'), }); const result = new Controller( engineURL, engineThreadURL, gameIdentifier, jest.fn().mockName('propertyHandler'), jest.fn().mockName('moveHandler'), ); return result; } describe('the controller\'s automatic behaviors', () => { test('connect as needed when setting strength', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.setStrength(999); controller.setStrength(9999); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['setoption strength 999'], ['isready'], ]); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['setoption strength 999'], ['isready'], ['setoption strength 9999'], ['isready'], ]); }); test('connect and start a game as needed when setting a line', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.setLine({ serialization: 'jkl' }, [{ serialization: 'mno' }, { serialization: 'pqr' }]); controller.setLine({ serialization: 'stu' }, []); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ]); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['setline jkl mno pqr'], ['isready'], ]); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['setline jkl mno pqr'], ['isready'], ['setline stu'], ['isready'], ]); }); test('connect and start a game as needed when starting a search', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); controller.stop(); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ]); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['go'], ['isready'], ]); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['go'], ['isready'], ['stop'], ['isready'], ]); }); }); describe('the controller\'s communication checks', () => { test('block communication when the engine protocol is too new', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.setLine({ serialization: 'jkl' }, []); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 9999.9.9'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await anyUnexpectedCalls(); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); }); test('block a game line if the engine does not know the game', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.setLine({ serialization: 'jkl' }, []); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ]); controller.receiveMessage('unknown'); controller.receiveMessage('readyok'); await anyUnexpectedCalls(); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ]); }); test('block starting an already started search', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); controller.go(); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ]); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ]); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['go'], ['isready'], ]); controller.receiveMessage('readyok'); await anyUnexpectedCalls(); expect(controller.worker.postMessage.mock.calls).toEqual([ ['bei def'], ['isready'], ['newgame ghi'], ['isready'], ['go'], ['isready'], ]); }); test('block stopping an already stopped search', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.stop(); await anyUnexpectedCalls(); expect(controller.worker.postMessage.mock.calls).toEqual([]); }); }); describe('the controller\'s callbacks', () => { test('report engine properties', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.setStrength(999); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('id name jkl'); controller.receiveMessage('id author mno'); controller.receiveMessage('id version pqr'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); expect(controller.propertyHandler.mock.calls).toEqual([ ['name', 'jkl'], ['author', 'mno'], ['version', 'pqr'], ]); }); test('report single moves', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move jkl'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ]); }); test('report consecutive moves', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move jkl'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ]); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move mno'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ['mno'], ]); }); test('do not report unwanted moves from stopped searches', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.stop(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move jkl'); expect(controller.moveHandler.mock.calls).toEqual([]); controller.stop(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move mno'); expect(controller.moveHandler.mock.calls).toEqual([]); controller.receiveMessage('move pqr'); expect(controller.moveHandler.mock.calls).toEqual([ ['pqr'], ]); }); test('do report wanted moves from stopped searches', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.stop(true); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move jkl'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ]); controller.stop(true); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move mno'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ['mno'], ]); controller.receiveMessage('move pqr'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ['mno'], ['pqr'], ]); }); test('do report late-arriving wanted moves from stopped searches', async() => { const controller = controllerWithMocks('abc', 'def', 'ghi'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('protocol 1.0.0'); controller.receiveMessage('beiok'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('known'); controller.receiveMessage('readyok'); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.stop(true); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.stop(false); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.go(); await callsTo(controller.worker.postMessage); controller.receiveMessage('readyok'); controller.receiveMessage('move jkl'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ]); controller.receiveMessage('move mno'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ]); controller.receiveMessage('move pqr'); expect(controller.moveHandler.mock.calls).toEqual([ ['jkl'], ['pqr'], ]); }); });