Skip to content

Commit

Permalink
more test cases for cyclic classes
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannesMeierSE committed Nov 20, 2024
1 parent 005c37f commit 072de08
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 9 deletions.
97 changes: 91 additions & 6 deletions examples/lox/test/lox-type-checking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 () => {
Expand All @@ -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', () => {
Expand Down
12 changes: 9 additions & 3 deletions packages/typir/src/utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit 072de08

Please sign in to comment.