Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
24dc40d
Prepare to rewrite in modern TypeScript
novemberborn Feb 20, 2025
037d59e
Reimplement deep comparison and (de)serialization
novemberborn Feb 21, 2025
b219c6d
Test BoxedPrimiteRepresentation
novemberborn Apr 13, 2025
3df9a40
Pack annotations to enable compression optimizations
novemberborn Apr 13, 2025
fc2094c
fixup! Prepare to rewrite in modern TypeScript
novemberborn Apr 15, 2025
d945880
Test DateRepresentation
novemberborn Apr 15, 2025
27699be
Test ErrorRepresentation
novemberborn Apr 15, 2025
7f54a24
Test FunctionRepresentation
novemberborn Apr 15, 2025
8765028
Test MapRepresentation
novemberborn May 2, 2025
fa277d4
Fix and test ModuleNamespaceObjectRepresentation
novemberborn May 2, 2025
cc5d9ca
fixup! Reimplement deep comparison and (de)serialization
novemberborn May 2, 2025
47b432c
Test PromiseRepresentation
novemberborn May 3, 2025
bb2ad5f
More efficient serialization of string tags that are the same as the …
novemberborn May 3, 2025
88872d3
Test RegExpRepresentation
novemberborn May 3, 2025
a0142ed
fixup! Reimplement deep comparison and (de)serialization
novemberborn May 3, 2025
e200dff
Test SetRepresentation
novemberborn May 3, 2025
e114ae5
Test WeakMapRepresentation
novemberborn May 3, 2025
c3a8e17
Test WeakSetRepresentation
novemberborn May 3, 2025
bd46a0e
Test ExternalRepresentation; improve deserialized comparisons
novemberborn May 5, 2025
aae0641
Further optimize serialization of string tags that are the same as co…
novemberborn May 5, 2025
7380d51
Test CryptoKeyRepresentation
novemberborn May 5, 2025
375af03
Test BytesAccessor
novemberborn May 5, 2025
9e29018
Test ElementAccessor and SparseValueRepresentation
novemberborn May 5, 2025
f2afba0
Test IteratorValueAccessor
novemberborn May 5, 2025
bd9d358
Test MapEntryAccessor
novemberborn May 5, 2025
79fc20e
Test property accessors
novemberborn May 6, 2025
1887afd
Refactor compare() tests focusing on algorithmic accuracy not behavio…
novemberborn May 6, 2025
025055e
Test describe()
novemberborn May 6, 2025
2a65532
Test serialization types
novemberborn May 7, 2025
7f75486
Test serialization
novemberborn May 7, 2025
0a7cdd9
Test deserialization
novemberborn May 16, 2025
78437fb
c8: Exclude *.d.ts files from report
novemberborn May 18, 2025
8d15d93
Split DescriptionContext and isPrimitive/representPrimitive into sepa…
novemberborn May 18, 2025
1377318
Split Encoder into its own file
novemberborn May 19, 2025
45b521d
Use cbor2 directly
novemberborn May 19, 2025
9fee1b3
Target Node.js 24 and update related dependencies & configuration
novemberborn May 19, 2025
8c88547
Support integers larger than int64
novemberborn May 19, 2025
9cf3312
Encode / decode non-wellformed strings
novemberborn May 19, 2025
0fe1c0a
Implement flags to control behavior
novemberborn May 19, 2025
1c444a5
Remove exports test file
novemberborn May 19, 2025
90d0471
Make comparing objects with null prototypes to regular object prototy…
novemberborn May 21, 2025
6526af5
Stop using interfaces
novemberborn May 21, 2025
e07ae2d
Add missing tests for flags getter in context implementations
novemberborn Jun 3, 2025
aa3c0c8
Treat empty constructor names as such
novemberborn May 24, 2025
6f25269
Refactor so property groups are not value representations
novemberborn May 31, 2025
bfb271a
Change BytesAccessor to be shallow
novemberborn May 29, 2025
a1e5fc2
Implement formatting for accessors and values
novemberborn May 21, 2025
d5e2c79
Upgrade XO
novemberborn Jun 9, 2025
fc40ba8
Update dependencies
novemberborn Jun 9, 2025
0d1d49d
Rename concepts
novemberborn Jun 9, 2025
1b2dc8a
Add method to fully (recursively) deserialize a representation
novemberborn Jun 9, 2025
aa46eb4
Change stack iteration to return undefined when done
novemberborn Jun 10, 2025
41ab11d
Simplify symbol property serialization
novemberborn Jun 10, 2025
a569ba5
Move iterator out of stack entry into internal state
novemberborn Jun 15, 2025
f206ec5
Implement Stack#peekNext() to access the next iterated value
novemberborn Jun 15, 2025
a6445fc
Implement Stack#takeWhile() to iterate over values while a condition …
novemberborn Jun 15, 2025
9a97460
Group accessors depending on applied algorithm, not in the context
novemberborn Jun 15, 2025
f2d0c84
Improve comparison logic for Error, RegExp, WeakMap, WeakSet and Cryp…
novemberborn Jun 16, 2025
19d124d
Exclude Error#stack from the iterated properties
novemberborn Jun 18, 2025
6f85dc6
Always include Error#code
novemberborn Jun 18, 2025
94aa64d
Implement fuzzy comparison mode
novemberborn Jun 16, 2025
79570f6
Refactor array-like handling
novemberborn Aug 17, 2025
00e5da1
Clean up unnecessary static is() from mocks
novemberborn Aug 17, 2025
05c8cc5
Give RHS control over being compared
novemberborn Aug 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Test SetRepresentation
  • Loading branch information
novemberborn committed Jun 5, 2025
commit e200dff2a2e6482fb96a0e7217ff93f7df546ff8
256 changes: 256 additions & 0 deletions src/values/objects/test/set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import test from 'ava'
import { DescriptionContext } from '../../../describe.ts'
import { Encoder } from '../../../serialize.ts'
import { Decoder, DeserializationContext } from '../../../deserialize.ts'
import { SetRepresentation } from '../set.ts'
import { comparable, strictlyEqual, unequal } from '../../../comparison.ts'
import { staticTypeTable } from '../../../serialization-types.ts'
import { snapshotEncoded } from '../../test/helpers/snapshot-encoded.ts'

// Deserialize method test
test('deserialize creates a comparable SetRepresentation', (t) => {
const originalContext = new DescriptionContext()
const set = new Set(['value1', 'value2'])
const original = originalContext.represent(set) as SetRepresentation

const encoder = new Encoder()
original.serialize(encoder)

const decoder = new Decoder(encoder.bytes)
decoder.staticType() // Consume the type
const deserializationContext = new DeserializationContext(decoder)
const deserialized = SetRepresentation.deserialize(deserializationContext, decoder)

// The deserialized representation should be comparable to the original
t.is(original.compare(deserialized), comparable)
})

// Compare method tests
test('compare returns strictlyEqual when comparing the same set instance', (t) => {
const context = new DescriptionContext()
const set = new Set(['value1', 'value2'])

const setRep1 = context.represent(set) as SetRepresentation
const setRep2 = context.represent(set) as SetRepresentation

t.is(setRep1.compare(setRep2), strictlyEqual)
})

test('compare returns unequal when comparing to non-SetRepresentation', (t) => {
const context = new DescriptionContext()
const set = new Set(['value1', 'value2'])
const obj = {}

const setRep = context.represent(set) as SetRepresentation
const objRep = context.represent(obj)

t.is(setRep.compare(objRep), unequal)
})

test('compare returns unequal when comparing sets of different sizes', (t) => {
const context = new DescriptionContext()
const set1 = new Set(['value1', 'value2'])
const set2 = new Set(['value1'])

const setRep1 = context.represent(set1) as SetRepresentation
const setRep2 = context.represent(set2) as SetRepresentation

t.is(setRep1.compare(setRep2), unequal)
})

test('compare returns comparable when comparing different set instances with same values', (t) => {
const context = new DescriptionContext()
const set1 = new Set(['value1', 'value2'])
const set2 = new Set(['value1', 'value2'])

const setRep1 = context.represent(set1) as SetRepresentation
const setRep2 = context.represent(set2) as SetRepresentation

// Should be comparable, not strictly equal, as they are different instances
t.is(setRep1.compare(setRep2), comparable)
})

test('compare returns comparable when comparing sets with different values', (t) => {
const context = new DescriptionContext()
const set1 = new Set(['value1', 'value2'])
const set2 = new Set(['value1', 'different'])

const setRep1 = context.represent(set1) as SetRepresentation
const setRep2 = context.represent(set2) as SetRepresentation

// Further iteration is required to compare values
t.is(setRep1.compare(setRep2), comparable)
})

test('compare handles sets with complex values', (t) => {
const context = new DescriptionContext()

// Create sets with objects and arrays as values
const value1 = { id: 1 }
const value2 = { id: 2 }

const set1 = new Set([value1, value2])

// Same structure but different object instances
const set2 = new Set([{ id: 1 }, { id: 2 }])

const setRep1 = context.represent(set1) as SetRepresentation
const setRep2 = context.represent(set2) as SetRepresentation

// Further iteration is required to compare values
t.is(setRep1.compare(setRep2), comparable)
})

// iterateArrayLike test
test('iterateArrayLike yields no elements for sets', (t) => {
const context = new DescriptionContext()
const set = new Set(['value1', 'value2'])
const setRep = context.represent(set) as SetRepresentation

const elements = [...setRep.iterateArrayLike()]

t.is(elements.length, 0)
})

// iterateIterable test
test('iterateIterable yields values for sets', (t) => {
const context = new DescriptionContext()
const set = new Set(['value1', 'value2'])
const setRep = context.represent(set) as SetRepresentation

const iteratorValues = [...setRep.iterateIterable()]

// Should have two values
t.is(iteratorValues.length, 2)

// We can't compare directly to string representations
// Instead, extract the actual value representations from the iterators
const values = iteratorValues.map((iteratorValue) => [...iteratorValue][0]!)

// Each value should be a representation of the corresponding set value
const stringValue1 = context.represent('value1')
const stringValue2 = context.represent('value2')

// For string values, we can check for strict equality in the comparison
const hasValue1 = values.some((value) => value.compare(stringValue1) === strictlyEqual)
const hasValue2 = values.some((value) => value.compare(stringValue2) === strictlyEqual)

t.true(hasValue1, 'Set should contain value1')
t.true(hasValue2, 'Set should contain value2')
})

test('iterateIterable preserves insertion order', (t) => {
const context = new DescriptionContext()

// Create a set with specific insertion order
const set = new Set()
set.add('value1')
set.add('value2')
set.add('value3')

// Create another set with the same values but different insertion order
const set2 = new Set()
set2.add('value3')
set2.add('value1')
set2.add('value2')

const setRep = context.represent(set) as SetRepresentation
const set2Rep = context.represent(set2) as SetRepresentation

const iteratorValues = [...setRep.iterateIterable()]
const iteratorValues2 = [...set2Rep.iterateIterable()]

// Should have three values in each set
t.is(iteratorValues.length, 3)
t.is(iteratorValues2.length, 3)

// Extract the actual values from the iterators
const values = iteratorValues.map((iteratorValue) => [...iteratorValue][0]!)
const values2 = iteratorValues2.map((iteratorValue) => [...iteratorValue][0]!)

// Create string representations for comparison
const value1Rep = context.represent('value1')
const value2Rep = context.represent('value2')
const value3Rep = context.represent('value3')

// Check first set's order
t.is(values[0]!.compare(value1Rep), strictlyEqual) // First is 'value1'
t.is(values[1]!.compare(value2Rep), strictlyEqual) // Second is 'value2'
t.is(values[2]!.compare(value3Rep), strictlyEqual) // Third is 'value3'

// Check second set's order
t.is(values2[0]!.compare(value3Rep), strictlyEqual) // First is 'value3'
t.is(values2[1]!.compare(value1Rep), strictlyEqual) // Second is 'value1'
t.is(values2[2]!.compare(value2Rep), strictlyEqual) // Third is 'value2'
})

// Serialization tests
test('serialize uses set static type and includes size annotation', (t) => {
const context = new DescriptionContext()
const set = new Set(['value1', 'value2'])
const setRep = context.represent(set) as SetRepresentation

const encoder = new Encoder()
setRep.serialize(encoder)

// Check the overall structure and type
snapshotEncoded(t, encoder, 'set serialization')

// Verify the static type and annotations manually
const decoder = new Decoder(encoder.bytes)
t.is(decoder.staticType(), staticTypeTable.set)
const annotations = decoder.annotations<{ s: number }>()
t.is(annotations.s, 2) // Size should be 2
})

test('serializing and deserializing a Set preserves its structure', (t) => {
const originalContext = new DescriptionContext()

// Create a set with some values
const set = new Set(['value1', 'value2'])

const original = originalContext.represent(set) as SetRepresentation

// Serialize it
const encoder = new Encoder()
original.serialize(encoder)

// Deserialize it
const decoder = new Decoder(encoder.bytes)
decoder.staticType() // Consume the type
const deserializationContext = new DeserializationContext(decoder)
const deserialized = SetRepresentation.deserialize(deserializationContext, decoder)

// The original and deserialized representations should be comparable
t.is(original.compare(deserialized), comparable)

// Create a set with different number of values
const differentSizeSet = new Set(['value1'])
const differentSizeRep = originalContext.represent(differentSizeSet) as SetRepresentation

// The deserialized set should be unequal to a set with a different size
t.is(deserialized.compare(differentSizeRep), unequal)
})

test('handles empty sets correctly', (t) => {
const context = new DescriptionContext()
const emptySet = new Set()
const emptySetRep = context.represent(emptySet) as SetRepresentation

// Empty set should have no iterable values
t.is([...emptySetRep.iterateIterable()].length, 0)

// Serializing should include size 0
const encoder = new Encoder()
emptySetRep.serialize(encoder)
const decoder = new Decoder(encoder.bytes)
decoder.staticType() // Consume the type
const annotations = decoder.annotations<{ s: number }>()
t.is(annotations.s, 0)

// Empty set should be equal to another empty set
const anotherEmptySet = new Set()
const anotherEmptySetRep = context.represent(anotherEmptySet) as SetRepresentation

t.is(emptySetRep.compare(anotherEmptySetRep), comparable)
})
68 changes: 68 additions & 0 deletions src/values/objects/test/snapshots/set.ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Snapshot report for `src/values/objects/test/set.ts`

The actual snapshot is saved in `set.ts.snap`.

Generated by [AVA](https://avajs.dev).

## serialize uses set static type and includes size annotation

> Serialized bytes (set serialization)

Uint8Array [
0ca46163 63536574 6174f561 73026170 01
]

> Decoded bytes (set serialization)

[
[
0,
12,
12,
],
[
5,
4,
4,
],
[
3,
1,
'c',
],
[
3,
3,
'Set',
],
[
3,
1,
't',
],
[
7,
21,
true,
],
[
3,
1,
's',
],
[
0,
2,
2,
],
[
3,
1,
'p',
],
[
0,
1,
1,
],
]
Binary file added src/values/objects/test/snapshots/set.ts.snap
Binary file not shown.