From 4cc0bff821fc9123161b5ae71068044949706488 Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 8 Feb 2024 11:59:47 +0100 Subject: [PATCH] fix test --- .../integration-tests/lib/setup-harness.ts | 21 ++++- packages/@glimmer/validator/lib/meta.ts | 56 +++++------- packages/@glimmer/validator/lib/validators.ts | 2 +- packages/@glimmer/validator/test/meta-test.ts | 87 ++++++++++++++++++- 4 files changed, 129 insertions(+), 37 deletions(-) diff --git a/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts b/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts index b05c2a7fd3..485f7539f9 100644 --- a/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts +++ b/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts @@ -63,8 +63,25 @@ export async function setupQunit() { qunit.moduleDone(pause); } - qunit.done(() => { - console.log('[HARNESS] done'); + // @ts-expect-error missing in types, does exist: https://api.qunitjs.com/callbacks/QUnit.on/#the-testend-event + QUnit.on('testEnd', (testEnd) => { + if (testEnd.status === 'failed') { + testEnd.errors.forEach((assertion: any) => { + console.error(assertion.stack); + // message: speedometer + // actual: 75 + // expected: 88 + // stack: at dmc.test.js:12 + }); + } + }); + + qunit.done(({ failed }) => { + if (failed > 0) { + console.log('[HARNESS] fail'); + } else { + console.log('[HARNESS] done'); + } }); return { diff --git a/packages/@glimmer/validator/lib/meta.ts b/packages/@glimmer/validator/lib/meta.ts index f9454c2738..ad0cd54b86 100644 --- a/packages/@glimmer/validator/lib/meta.ts +++ b/packages/@glimmer/validator/lib/meta.ts @@ -1,11 +1,11 @@ import type { ConstantTag, Tag, UpdatableTag } from '@glimmer/interfaces'; import type { Indexable } from './utils'; -import { unwrap } from './utils'; +import type { MonomorphicTagImpl } from './validators'; import { debug } from './debug'; +import { unwrap } from './utils'; import { createUpdatableTag, DIRTY_TAG, validateTag } from './validators'; -import { track } from './tracking'; function isObjectLike(u: T): u is Indexable & T { return (typeof u === 'object' && u !== null) || typeof u === 'function'; @@ -16,13 +16,7 @@ function isObjectLike(u: T): u is Indexable & T { export type TagMeta = Map; const TRACKED_TAGS = new WeakMap(); -const TAG_INFO = new WeakMap< - Tag, - { - object: Object; - propertyKey: string | number | symbol; - } ->(); +const TAG_META = Symbol('TAG_META'); export function dirtyTagFor( obj: T, @@ -75,42 +69,39 @@ export function tagFor( tags.set(key, tag); } - TAG_INFO.set(tag, { + (tag as any)[TAG_META] = { propertyKey: key, object: obj, - }); + }; return tag; } export function infoForTag(tag: Tag) { - return TAG_INFO.get(tag); + return (tag as any)[TAG_META]; } - -function getTrackedDependencies(obj: object, property: string) { - const tag = tagFor(obj, property); +export function getTrackedDependencies(obj: object, property: string, tag: Tag) { + const ownTag = tagFor(obj, property); const dependencies = []; // do not include tracked properties from dependencies - const subtags = (Array.isArray(tag.subtag) ? tag.subtag : [tag.subtag]).filter(t => !!t) as Tag[]; + const subtags = (Array.isArray(tag.subtag) ? [tag, ...tag.subtag] : [tag, tag.subtag]).filter( + (t) => !!t + ) as Tag[]; for (const subtag of subtags) { if (subtag === tag) continue; dependencies.push({ ...infoForTag(subtag), tag: subtag }); if (subtag.subtag && !Array.isArray(subtag.subtag)) { - dependencies.push({ ...infoForTag(subtag.subtag), tag: subtag.subtag }); + dependencies.push({ ...infoForTag(subtag.subtag) }); } } - return dependencies; -} -function getChangedDependencies(obj: object, property: string, revision: number) { - const dependencies = getTrackedDependencies(obj, property); - let maxRevision = 0; + let maxRevision = (ownTag as MonomorphicTagImpl)['revision'] ?? 0; let minRevision = Infinity; dependencies.forEach((t) => { - maxRevision = revision || Math.max(maxRevision, (t.tag as any).lastValue); - minRevision = Math.min(minRevision, (t.tag as any).lastValue); + maxRevision = Math.max(maxRevision, (t.tag as MonomorphicTagImpl)['revision']); + minRevision = Math.min(minRevision, (t.tag as MonomorphicTagImpl)['revision']); }); const hasChange = maxRevision !== minRevision; @@ -119,22 +110,21 @@ function getChangedDependencies(obj: object, property: string, revision: number) return { latestValue, dependencies: dependencies.map((t) => { - if ((t.tag as any).lastValue > latestValue) { - latestValue = (t.tag as any).lastValue; + if (t.tag.revision > latestValue) { + latestValue = t.tag.revision; } - const changed = hasChange && (t.tag as any).lastValue >= maxRevision; - return { ...t, changed }; - }) - } + const changed = hasChange && t.tag.revision >= maxRevision; + return { object: t.object, propertyKey: t.propertyKey, changed }; + }), + }; } - type TrackedInfo = { changed: string[]; propertyInfo: Record; -} +}; -function getChangedProperties(obj: object, trackedInfo: TrackedInfo) { +export function getChangedProperties(obj: object, trackedInfo: TrackedInfo) { trackedInfo['changed'] = []; trackedInfo.propertyInfo = trackedInfo.propertyInfo || {}; for (const name of Object.keys(obj)) { diff --git a/packages/@glimmer/validator/lib/validators.ts b/packages/@glimmer/validator/lib/validators.ts index da94e38069..f01cbfa786 100644 --- a/packages/@glimmer/validator/lib/validators.ts +++ b/packages/@glimmer/validator/lib/validators.ts @@ -90,7 +90,7 @@ function allowsCycles(tag: Tag): boolean { } } -class MonomorphicTagImpl { +export class MonomorphicTagImpl { static combine(this: void, tags: Tag[]): Tag { switch (tags.length) { case 0: diff --git a/packages/@glimmer/validator/test/meta-test.ts b/packages/@glimmer/validator/test/meta-test.ts index f826fd60ec..c402b7e5d7 100644 --- a/packages/@glimmer/validator/test/meta-test.ts +++ b/packages/@glimmer/validator/test/meta-test.ts @@ -1,4 +1,13 @@ -import { dirtyTagFor, infoForTag, tagFor, validateTag, valueForTag } from '@glimmer/validator'; +import { + dirtyTagFor, + infoForTag, + tagFor, + track, + trackedData, + validateTag, + valueForTag, +} from '@glimmer/validator'; +import { getTrackedDependencies } from '@glimmer/validator/lib/meta'; import { module, test } from './-utils'; @@ -27,4 +36,80 @@ module('@glimmer/validator: meta', () => { assert.strictEqual(info.object, obj); assert.strictEqual(info.propertyKey, 'foo'); }); + + test('it can detect tracked dependencies', (assert) => { + class TestObject { + declare item1: string; + declare item2: string; + constructor() {} + + get getterWithTracked() { + return this.item1 + ' world' + this.item2; + } + } + + { + const { getter, setter } = trackedData('item1', () => ''); + Object.defineProperty(TestObject.prototype, 'item1', { + get(this) { + return getter(this); + }, + set(this, v) { + return setter(this, v); + }, + }); + } + { + const { getter, setter } = trackedData('item2', () => ''); + Object.defineProperty(TestObject.prototype, 'item2', { + get(this) { + return getter(this); + }, + set(this, v) { + return setter(this, v); + }, + }); + } + + const obj = new TestObject(); + let tag = track(() => { + assert.strictEqual(obj.getterWithTracked, ' world'); + }); + + assert.deepEqual(getTrackedDependencies(obj, 'getterWithTracked', tag), { + latestValue: 0, + dependencies: [ + { + changed: false, + object: obj, + propertyKey: 'item1', + }, + { + changed: false, + object: obj, + propertyKey: 'item2', + }, + ], + }); + + obj.item1 = 'hello'; + tag = track(() => { + assert.strictEqual(obj.getterWithTracked, 'hello world'); + }); + assert.deepEqual(getTrackedDependencies(obj, 'getterWithTracked', tag), { + latestValue: 0, + dependencies: [ + { + changed: true, + object: obj, + propertyKey: 'item1', + }, + { + changed: false, + object: obj, + propertyKey: 'item2', + }, + ], + }); + }); });