Skip to content

Commit

Permalink
update static design (#202)
Browse files Browse the repository at this point in the history
* static-design: update the entire API

* convert-touchstone: comply with new static design api

* convert-touchstone: fix tests
  • Loading branch information
QuentinRoy authored Nov 28, 2023
1 parent 16ab7b5 commit ff4d4ac
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 143 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-forks-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lightmill/static-design': major
---

Rename StaticDesign and Timeline's constructors 'tasks' option to 'timeline', rename StaticDesign's getId method to getRunId, and make every async methods sync (TimelineIterator now implements Iterator instead of AsyncInterator).
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ exports[`convert > accepts a readable stream as input 1`] = `
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"factorFloat": 10.254,
"factorInt": 2,
Expand Down Expand Up @@ -141,7 +141,7 @@ exports[`convert > accepts a readable stream as input 1`] = `
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"factorFloat": 0.5,
"factorInt": 2,
Expand Down Expand Up @@ -281,10 +281,10 @@ exports[`convert > accepts a string as input 1`] = `
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"factorFloat": 10.254,
"factorInt": 2,
Expand Down Expand Up @@ -417,7 +417,7 @@ exports[`convert > accepts a string as input 1`] = `
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"factorFloat": 0.5,
"factorInt": 2,
Expand Down Expand Up @@ -557,10 +557,10 @@ exports[`convert > pres, posts, and trials can be specified as arrays of objects
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"id": "pre-run-1-0",
"type": "pre-run-1",
Expand Down Expand Up @@ -773,7 +773,7 @@ exports[`convert > pres, posts, and trials can be specified as arrays of objects
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"id": "pre-run-1-0",
"type": "pre-run-1",
Expand Down Expand Up @@ -993,10 +993,10 @@ exports[`convert > pres, posts, and trials options can be specified as arrays of
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"id": "pre-run-1-0",
"type": "pre-run-1",
Expand Down Expand Up @@ -1397,7 +1397,7 @@ exports[`convert > pres, posts, and trials options can be specified as arrays of
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"id": "pre-run-1-0",
"type": "pre-run-1",
Expand Down Expand Up @@ -1805,10 +1805,10 @@ exports[`convert > pres, posts, and trials options can be specified as functions
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"data": "data",
"id": "0",
Expand Down Expand Up @@ -1943,7 +1943,7 @@ exports[`convert > pres, posts, and trials options can be specified as functions
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"data": "data",
"id": "26",
Expand Down Expand Up @@ -3474,10 +3474,10 @@ exports[`convert > pres, posts, and trials options can be specified as strings 1
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"id": "mock-pre-run-0",
"type": "mock-pre-run",
Expand Down Expand Up @@ -3680,7 +3680,7 @@ exports[`convert > pres, posts, and trials options can be specified as strings 1
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"id": "mock-pre-run-0",
"type": "mock-pre-run",
Expand Down Expand Up @@ -3890,10 +3890,10 @@ exports[`convert > pres, posts, and trials options can be specified as strings 2
"author": "Quentin Roy",
"description": "Some useless text",
"id": "test",
"timelines": [
"runs": [
{
"id": "S0",
"tasks": [
"timeline": [
{
"id": "mock-pre-run-0",
"type": "mock-pre-run",
Expand Down Expand Up @@ -4096,7 +4096,7 @@ exports[`convert > pres, posts, and trials options can be specified as strings 2
},
{
"id": "S1",
"tasks": [
"timeline": [
{
"id": "mock-pre-run-0",
"type": "mock-pre-run",
Expand Down
38 changes: 19 additions & 19 deletions packages/convert-touchstone/src/convert-touchstone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type Experiment = {
export type WithId = { id: string };
export type Run<T extends MinimalTask> = {
id: string;
tasks: Array<T>;
timeline: Array<T>;
};
export type Trial = (
| { practice: false; number: number; blockNumber: number }
Expand Down Expand Up @@ -156,20 +156,20 @@ export default function convertTouchstone(
if (experiment != null) {
throw new Error('There can only be one experiment tag');
}
experiment = { author, description, id, timelines: [] };
experiment = { author, description, id, runs: [] };
},
run({ attributes: { id } }: sax.Tag) {
if (currentRun != null) throw new Error('Runs cannot be nested');
if (experiment == null) {
throw new Error('Blocks must be inside an experiment');
}
currentRun = { id, tasks: [] };
experiment.timelines.push(currentRun);
currentRun = { id, timeline: [] };
experiment.runs.push(currentRun);
// This is very protective, but it ensures mappers cannot mess with
// our internal state.
let { tasks, ...runBase } = currentRun;
let { timelines, ...experimentBase } = experiment;
currentRun.tasks = getPreRunTasks(runBase, experimentBase);
let { timeline: tasks, ...runBase } = currentRun;
let { runs, ...experimentBase } = experiment;
currentRun.timeline = getPreRunTasks(runBase, experimentBase);
},
block(blockNode: sax.Tag, practice = false) {
if (currentBlock != null) throw new Error('Blocks cannot be nested');
Expand All @@ -189,9 +189,9 @@ export default function convertTouchstone(
};
// This is very protective, but it ensures mappers cannot mess with
// our internal state.
let { tasks, ...runBase } = currentRun;
let { timelines, ...experimentBase } = experiment;
currentRun.tasks.push(
let { timeline: tasks, ...runBase } = currentRun;
let { runs, ...experimentBase } = experiment;
currentRun.timeline.push(
...getPreBlockTasks({ ...currentBlock }, runBase, experimentBase),
);
},
Expand Down Expand Up @@ -225,9 +225,9 @@ export default function convertTouchstone(
};
// This is very protective, but it ensures mappers cannot mess with
// our internal state.
let { tasks, ...runBase } = currentRun;
let { timelines, ...experimentBase } = experiment;
currentRun.tasks.push(
let { timeline, ...runBase } = currentRun;
let { runs, ...experimentBase } = experiment;
currentRun.timeline.push(
...getTrialTasks(trial, { ...currentBlock }, runBase, experimentBase),
);
},
Expand Down Expand Up @@ -267,9 +267,9 @@ export default function convertTouchstone(
}
// This is very protective, but it ensures mappers cannot mess with
// our internal state.
let { timelines, ...experimentBase } = experiment;
let { tasks, ...runBase } = currentRun;
currentRun.tasks.push(...getPostRunTasks(runBase, experimentBase));
let { runs, ...experimentBase } = experiment;
let { timeline, ...runBase } = currentRun;
currentRun.timeline.push(...getPostRunTasks(runBase, experimentBase));
currentRun = null;
currentBlock = null;
taskIdManager.reset();
Expand All @@ -286,9 +286,9 @@ export default function convertTouchstone(
}
// This is very protective, but it ensures mappers cannot mess with
// our internal state.
let { timelines, ...experimentBase } = experiment;
let { tasks, ...runBase } = currentRun;
currentRun.tasks.push(
let { runs, ...experimentBase } = experiment;
let { timeline, ...runBase } = currentRun;
currentRun.timeline.push(
...getPostBlockTasks(currentBlock, runBase, experimentBase),
);
currentBlock = null;
Expand Down
40 changes: 20 additions & 20 deletions packages/static-design/__tests__/static-experiment-design.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
vi,
} from 'vitest';
import StaticExperimentDesign from '../src/static-experiment-design.js';
import UntypedTimelineIterator, { Timeline } from '../src/timeline-iterator.js';
import UntypedTimelineIterator, { Run } from '../src/timeline-iterator.js';

vi.mock('../src/timeline-iterator.js', () => {
return {
Expand All @@ -24,16 +24,16 @@ const TimelineIterator = UntypedTimelineIterator as MockedClass<
let design: StaticExperimentDesign<{ id: string }>;
beforeEach(() => {
// @ts-expect-error This is a mock.
TimelineIterator.mockImplementation((timeline: Timeline<unknown>) => {
TimelineIterator.mockImplementation((timeline: Run<unknown>) => {
return { id: `mock-iterator-${timeline.id}` };
});
design = new StaticExperimentDesign({
id: 'mock-xp',
timelines: [
{ id: 'mock-timeline-1', tasks: [{ id: '1-1' }, { id: '1-2' }] },
{ id: 'mock-timeline-2', tasks: [{ id: '2-1' }, { id: '2-2' }] },
{ id: 'mock-timeline-3', tasks: [{ id: '3-1' }, { id: '3-2' }] },
{ id: 'mock-timeline-4', tasks: [{ id: '4-1' }, { id: '4-2' }] },
runs: [
{ id: 'mock-timeline-1', timeline: [{ id: '1-1' }, { id: '1-2' }] },
{ id: 'mock-timeline-2', timeline: [{ id: '2-1' }, { id: '2-2' }] },
{ id: 'mock-timeline-3', timeline: [{ id: '3-1' }, { id: '3-2' }] },
{ id: 'mock-timeline-4', timeline: [{ id: '4-1' }, { id: '4-2' }] },
],
});
});
Expand All @@ -44,44 +44,44 @@ afterEach(() => {

describe('StaticExperimentDesign#getAvailableTimelines', () => {
it('returns the list of un-started available timelines from the list of started timelines', async () => {
await expect(
design.getAvailableTimelines(['mock-timeline-2', 'mock-timeline-3']),
).resolves.toEqual(['mock-timeline-1', 'mock-timeline-4']);
expect(
design.getAvailableRuns(['mock-timeline-2', 'mock-timeline-3']),
).toEqual(['mock-timeline-1', 'mock-timeline-4']);
});
it('returns an empty list if there is no available timelines', async () => {
await expect(
design.getAvailableTimelines([
expect(
design.getAvailableRuns([
'mock-timeline-2',
'mock-timeline-3',
'mock-timeline-1',
'mock-timeline-4',
]),
).resolves.toEqual([]);
).toEqual([]);
});
});

describe('StaticExperimentDesign#startTimeline', () => {
it('returns the timeline iterator for the specified timeline', async () => {
await expect(
design.startTimeline('mock-timeline-3', {
expect(
design.startRun('mock-timeline-3', {
resumeAfter: '3-1',
resumeWith: { id: 'resumeWith' },
}),
).resolves.toEqual({ id: 'mock-iterator-mock-timeline-3' });
await expect(TimelineIterator.mock.calls).toEqual([
).toEqual({ id: 'mock-iterator-mock-timeline-3' });
expect(TimelineIterator.mock.calls).toEqual([
[
{ id: 'mock-timeline-3', tasks: [{ id: '3-1' }, { id: '3-2' }] },
{ id: 'mock-timeline-3', timeline: [{ id: '3-1' }, { id: '3-2' }] },
{ resumeAfter: '3-1', resumeWith: { id: 'resumeWith' } },
],
]);
});
it('fails if the timeline design does not exists', async () => {
await expect(design.startTimeline('unknown-timeline-id')).rejects.toThrow();
expect(() => design.startRun('unknown-timeline-id')).toThrow();
});
});

describe('StaticExperimentDesign#getId', () => {
it('returns the id of the experiment', async () => {
await expect(design.getId()).resolves.toBe('mock-xp');
expect(design.getId()).toBe('mock-xp');
});
});
Loading

0 comments on commit ff4d4ac

Please sign in to comment.