Skip to content

Commit

Permalink
fix test
Browse files Browse the repository at this point in the history
  • Loading branch information
patricklx committed Feb 8, 2024
1 parent 2bc2b2c commit e4a3192
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 40 deletions.
21 changes: 19 additions & 2 deletions packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
64 changes: 28 additions & 36 deletions packages/@glimmer/validator/lib/meta.ts
Original file line number Diff line number Diff line change
@@ -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<T>(u: T): u is Indexable & T {
return (typeof u === 'object' && u !== null) || typeof u === 'function';
Expand All @@ -16,13 +16,7 @@ function isObjectLike<T>(u: T): u is Indexable & T {
export type TagMeta = Map<PropertyKey, UpdatableTag>;

const TRACKED_TAGS = new WeakMap<object, TagMeta>();
const TAG_INFO = new WeakMap<
Tag,
{
object: Object;
propertyKey: string | number | symbol;
}
>();
const TAG_META = Symbol('TAG_META');

export function dirtyTagFor<T extends object>(
obj: T,
Expand Down Expand Up @@ -75,42 +69,39 @@ export function tagFor<T extends object>(
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;
Expand All @@ -119,28 +110,29 @@ 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<string, any>;
}
};

function getChangedProperties(obj: object, trackedInfo: TrackedInfo) {
export function getChangedProperties(obj: object, trackedInfo?: TrackedInfo) {
trackedInfo = trackedInfo || {} as TrackedInfo;
trackedInfo['changed'] = [];
trackedInfo.propertyInfo = trackedInfo.propertyInfo || {};
for (const name of Object.keys(obj)) {
for (const name in obj) {
const tag = tagFor(obj, name);
let tagInfo = trackedInfo.propertyInfo?.[name] || {
tag: tagFor(obj, name),
revision: 0,
tag,
revision: (tag as MonomorphicTagImpl)["revision"],
};
trackedInfo.propertyInfo[name] = tagInfo;
if (!tagInfo.tag) return;
Expand Down
2 changes: 1 addition & 1 deletion packages/@glimmer/validator/lib/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function allowsCycles(tag: Tag): boolean {
}
}

class MonomorphicTagImpl<T extends MonomorphicTagId = MonomorphicTagId> {
export class MonomorphicTagImpl<T extends MonomorphicTagId = MonomorphicTagId> {
static combine(this: void, tags: Tag[]): Tag {
switch (tags.length) {
case 0:
Expand Down
94 changes: 93 additions & 1 deletion packages/@glimmer/validator/test/meta-test.ts
Original file line number Diff line number Diff line change
@@ -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 {getChangedProperties, getTrackedDependencies} from '@glimmer/validator/lib/meta';

import { module, test } from './-utils';

Expand Down Expand Up @@ -27,4 +36,87 @@ 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<TestObject, 'item1'>('item1', () => '');
Object.defineProperty(TestObject.prototype, 'item1', {
enumerable: true,
get(this) {
return getter(this);
},
set(this, v) {
return setter(this, v);
},
});
}
{
const { getter, setter } = trackedData<TestObject, 'item2'>('item2', () => '');
Object.defineProperty(TestObject.prototype, 'item2', {
enumerable: true,
get(this) {
return getter(this);
},
set(this, v) {
return setter(this, v);
},
});
}

const obj = new TestObject();
let tag = track(() => {
assert.strictEqual(obj.getterWithTracked, ' world');
});

const trackedInfo = getChangedProperties(obj);
assert.deepEqual(trackedInfo?.changed, []);

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',
},
],
});

assert.deepEqual(getChangedProperties(obj, trackedInfo)?.changed, ['item1']);
});
});

0 comments on commit e4a3192

Please sign in to comment.