diff --git a/examples/lox/test/lox-type-checking.test.ts b/examples/lox/test/lox-type-checking.test.ts index d682fa8..7dd38a1 100644 --- a/examples/lox/test/lox-type-checking.test.ts +++ b/examples/lox/test/lox-type-checking.test.ts @@ -313,7 +313,7 @@ describe('Explicitly test type checking for LOX', () => { }); describe('Cyclic type definitions where a Class is declared and already used', () => { - test('Class with field', async () => { + test('Class with field of its own type', async () => { await validate(` class Node { children: Node @@ -334,7 +334,7 @@ describe('Cyclic type definitions where a Class is declared and already used', ( expectTypirTypes(loxServices, isClassType, 'A', 'B'); }); - test('Three Classes with fields with the other Class as type', async () => { + test('Three Classes with fields with one of the other Classes as type', async () => { await validate(` class A { prop1: B @@ -349,14 +349,49 @@ describe('Cyclic type definitions where a Class is declared and already used', ( expectTypirTypes(loxServices, isClassType, 'A', 'B', 'C'); }); - test.todo('Class with method', async () => { + test('Three Classes with fields with two of the other Classes as type', async () => { + await validate(` + class A { + prop1: B + prop2: C + } + class B { + prop3: C + prop4: A + } + class C { + prop5: A + prop6: B + } + `, []); + expectTypirTypes(loxServices, isClassType, 'A', 'B', 'C'); + }); + + test('Class with field of its own type and another dependency', async () => { await validate(` class Node { - myMethod(input: number): Node {} + children: Node + other: Another + } + class Another { + children: Node } `, []); - expectTypirTypes(loxServices, isClassType, 'Node'); - expectTypirTypes(loxServices, isFunctionType, 'myMethod'); + expectTypirTypes(loxServices, isClassType, 'Node', 'Another'); + }); + + test('Two Classes with a field of its own type and cyclic dependencies to each other', async () => { + await validate(` + class Node { + own: Node + other: Another + } + class Another { + own: Another + another: Node + } + `, []); + expectTypirTypes(loxServices, isClassType, 'Node', 'Another'); }); test('Having two declarations for the delayed class A, but only one type A in the type system', async () => { @@ -376,6 +411,56 @@ describe('Cyclic type definitions where a Class is declared and already used', ( expectTypirTypes(loxServices, isClassType, 'A', 'B'); }); + test('Having three declarations for the delayed class A, but only one type A in the type system', async () => { + await validate(` + class A { + property1: B // needs to wait for B, since B is defined below + } + class A { + property2: B // needs to wait for B, since B is defined below + } + class A { + property3: B // needs to wait for B, since B is defined below + } + class B { } + `, [ // Typir works with this, but for LOX these validation errors are produced: + 'Declared classes need to be unique (A).', + 'Declared classes need to be unique (A).', + 'Declared classes need to be unique (A).', + ]); + // check, that there is only one class type A in the type graph: + expectTypirTypes(loxServices, isClassType, 'A', 'B'); + }); + + test('Having two declarations for class A waiting for B, while B itself depends on A', async () => { + await validate(` + class A { + property1: B // needs to wait for B, since B is defined below + } + class A { + property2: B // needs to wait for B, since B is defined below + } + class B { + property3: A // should be the valid A and not the invalid A + } + `, [ // Typir works with this, but for LOX these validation errors are produced: + 'Declared classes need to be unique (A).', + 'Declared classes need to be unique (A).', + ]); + // check, that there is only one class type A in the type graph: + expectTypirTypes(loxServices, isClassType, 'A', 'B'); + }); + + test.todo('Class with method', async () => { + await validate(` + class Node { + myMethod(input: number): Node {} + } + `, []); + expectTypirTypes(loxServices, isClassType, 'Node'); + expectTypirTypes(loxServices, isFunctionType, 'myMethod'); + }); + }); describe('Test internal validation of Typir for cycles in the class inheritance hierarchy', () => { diff --git a/packages/typir/src/utils/test-utils.ts b/packages/typir/src/utils/test-utils.ts index b4fcc00..23f29a5 100644 --- a/packages/typir/src/utils/test-utils.ts +++ b/packages/typir/src/utils/test-utils.ts @@ -8,10 +8,16 @@ import { expect } from 'vitest'; import { Type } from '../graph/type-node.js'; import { TypirServices } from '../typir.js'; -export function expectTypirTypes(services: TypirServices, filterTypes: (type: Type) => boolean, ...namesOfExpectedTypes: string[]): void { - const typeNames = services.graph.getAllRegisteredTypes().filter(filterTypes).map(t => t.getName()); +export function expectTypirTypes(services: TypirServices, filterTypes: (type: Type) => boolean, ...namesOfExpectedTypes: string[]): Type[] { + const types = services.graph.getAllRegisteredTypes().filter(filterTypes); + types.forEach(type => expect(type.getInitializationState()).toBe('Completed')); + const typeNames = types.map(t => t.getName()); expect(typeNames, typeNames.join(', ')).toHaveLength(namesOfExpectedTypes.length); for (const name of namesOfExpectedTypes) { - expect(typeNames).includes(name); + const index = typeNames.indexOf(name); + expect(index >= 0).toBeTruthy(); + typeNames.splice(index, 1); // removing elements is needed to probably support duplicated entries } + expect(typeNames).toHaveLength(0); + return types; }