Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm committed Dec 20, 2024
1 parent 3221d65 commit 89780e3
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 8 deletions.
17 changes: 10 additions & 7 deletions packages/svelte/src/internal/client/reactivity/sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,10 @@ export function internal_set(source, value) {
/**
* @param {Value} signal
* @param {number} status should be DIRTY or MAYBE_DIRTY
* @param {boolean} [unsafe] mark all reactions for unsafe mutations
* @returns {void}
*/
export function mark_reactions(signal, status) {
export function mark_reactions(signal, status, unsafe = false) {
var reactions = signal.reactions;
if (reactions === null) return;

Expand All @@ -253,23 +254,25 @@ export function mark_reactions(signal, status) {
var flags = reaction.f;

// Skip any effects that are already dirty
if ((flags & DIRTY) !== 0) continue;
if ((flags & DIRTY) !== 0 && !unsafe) continue;

// In legacy mode, skip the current effect to prevent infinite loops
if (!runes && reaction === active_effect) continue;

// Inspect effects need to run immediately, so that the stack trace makes sense
if (DEV && (flags & INSPECT_EFFECT) !== 0) {
// Inspect effects need to run immediately, so that the stack trace makes sense.
// Skip doing this for the unsafe mutations as they will have already been added
// in the unsafe() wrapper
if (DEV && !unsafe && (flags & INSPECT_EFFECT) !== 0) {
inspect_effects.add(reaction);
continue;
}

set_signal_status(reaction, status);

// If the signal a) was previously clean or b) is an unowned derived, then mark it
if ((flags & (CLEAN | UNOWNED)) !== 0) {
// If the signal a) was previously clean or b) is an unowned derived then mark it
if ((flags & (CLEAN | UNOWNED)) !== 0 || unsafe) {
if ((flags & DERIVED) !== 0) {
mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY);
mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY, unsafe);
} else {
schedule_effect(/** @type {Effect} */ (reaction));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ export function update_effect(effect) {
// to ensure consistency of the graph
if (unsafe_sources !== null && (effect.f & CLEAN) !== 0) {
for (let i = 0; i < /** @type {Source[]} */ (unsafe_sources).length; i++) {
mark_reactions(unsafe_sources[i], DIRTY);
mark_reactions(unsafe_sources[i], DIRTY, true);
}
}

Expand Down
111 changes: 111 additions & 0 deletions packages/svelte/tests/signals/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,4 +781,115 @@ describe('signals', () => {
assert.equal($.get(count), 0n);
};
});

test('unsafe() correctly ensures graph consistency', () => {
return () => {
const output: any[] = [];

const destroy = effect_root(() => {
const a = state(0);
const b = state(0);
const c = derived(() => {
$.unsafe(() => {
set(b, $.get(a) + 1);
});
return $.get(a);
});

render_effect(() => {
output.push('b' + $.get(b));
});

render_effect(() => {
output.push('b' + $.get(b), 'c' + $.get(c));
});

flushSync();

set(a, 1);

flushSync();
});

destroy();

assert.deepEqual(output, ['b0', 'b0', 'c0', 'b1', 'b1', 'c0', 'b2', 'c1', 'b2']);
};
});

test('unsafe() correctly ensures graph consistency #2', () => {
return () => {
const output: any[] = [];

const destroy = effect_root(() => {
const a = state(0);
const b = state(0);
const c = derived(() => {
$.unsafe(() => {
set(b, $.get(a) + 1);
});
return $.get(a);
});
let d = derived(() => $.get(b));

render_effect(() => {
output.push('d' + $.get(d));
});

render_effect(() => {
output.push('d' + $.get(d), 'c' + $.get(c));
});

flushSync();

set(a, 1);

flushSync();
});

destroy();

assert.deepEqual(output, ['d0', 'd0', 'c0', 'd1', 'd1', 'c0', 'd2', 'c1', 'd2']);
};
});

test('unsafe() correctly ensures graph consistency #3', () => {
return () => {
const output: any[] = [];

const destroy = effect_root(() => {
const a = state(0);
const b = state(0);
const c = derived(() => {
$.unsafe(() => {
set(b, $.get(a) + 1);
});
return $.get(a);
});
let d = state(true);
let e = derived(() => $.get(b));

render_effect(() => {
if ($.get(d)) {
return;
}
output.push('e' + $.get(e), 'c' + $.get(c));
});

flushSync();

set(d, false);

flushSync();

set(a, 1);

flushSync();
});

destroy();

assert.deepEqual(output, ['e0', 'c0', 'e1', 'c0', 'e2', 'c1']);
};
});
});

0 comments on commit 89780e3

Please sign in to comment.