Skip to content

Commit 5ae7133

Browse files
committed
add OnyxSnapshotCache perf tests
1 parent 0d994e8 commit 5ae7133

1 file changed

Lines changed: 266 additions & 0 deletions

File tree

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
import {measureFunction} from 'reassure';
2+
import {OnyxSnapshotCache} from '../../lib/OnyxSnapshotCache';
3+
import type {UseOnyxOptions, UseOnyxResult} from '../../lib/useOnyx';
4+
5+
// Define types for test data
6+
type MockData = {
7+
id: number;
8+
name: string;
9+
value: number;
10+
field?: string;
11+
};
12+
13+
const ONYXKEYS = {
14+
TEST_KEY: 'test',
15+
TEST_KEY_2: 'test2',
16+
COLLECTION: {
17+
TEST_KEY: 'test_',
18+
TEST_KEY_2: 'test2_',
19+
REPORTS: 'reports_',
20+
},
21+
};
22+
23+
// Mock selector functions
24+
const simpleSelector = (data: unknown) => (data as MockData | undefined)?.value;
25+
26+
const complexSelector = (data: unknown) => {
27+
const mockData = data as MockData | undefined;
28+
return {
29+
id: mockData?.id,
30+
name: mockData?.name,
31+
computed: mockData?.value ? mockData.value * 2 : 0,
32+
formatted: `${mockData?.name}: ${mockData?.value}`,
33+
};
34+
};
35+
36+
const selectorOptions: UseOnyxOptions<string, number | undefined> = {
37+
selector: simpleSelector,
38+
initWithStoredValues: true,
39+
allowStaleData: false,
40+
canBeMissing: true,
41+
};
42+
43+
const complexSelectorOptions: UseOnyxOptions<string, {id?: number; name?: string; computed: number; formatted: string}> = {
44+
selector: complexSelector,
45+
initWithStoredValues: true,
46+
allowStaleData: false,
47+
canBeMissing: true,
48+
};
49+
50+
// Mock results
51+
const mockResult: UseOnyxResult<MockData> = [
52+
{id: 1, name: 'Test', value: 42},
53+
{status: 'loaded' as const, sourceValue: {id: 1, name: 'Test', value: 42}},
54+
];
55+
56+
const mockResults = Array.from(
57+
{length: 1000},
58+
(_, i) =>
59+
[
60+
{id: i, name: `Test${i}`, value: i * 10},
61+
{status: 'loaded' as const, sourceValue: {id: i, name: `Test${i}`, value: i * 10}},
62+
] as UseOnyxResult<MockData>,
63+
);
64+
65+
describe('OnyxSnapshotCache', () => {
66+
let cache: OnyxSnapshotCache;
67+
68+
const resetCacheBeforeEachMeasure = () => {
69+
cache = new OnyxSnapshotCache();
70+
};
71+
72+
describe('getSelectorId', () => {
73+
test('getting ID for new selector', async () => {
74+
await measureFunction(
75+
() => {
76+
cache.getSelectorId(simpleSelector);
77+
},
78+
{
79+
beforeEach: resetCacheBeforeEachMeasure,
80+
},
81+
);
82+
});
83+
84+
test('getting ID for cached selector (1000 existing selectors)', async () => {
85+
await measureFunction(
86+
() => {
87+
cache.getSelectorId(simpleSelector);
88+
},
89+
{
90+
beforeEach: () => {
91+
resetCacheBeforeEachMeasure();
92+
// Pre-populate with 1000 selectors
93+
for (let i = 0; i < 1000; i++) {
94+
const selector = (data: unknown) => ((data as MockData | undefined)?.field ?? '') + i;
95+
cache.getSelectorId(selector);
96+
}
97+
},
98+
},
99+
);
100+
});
101+
});
102+
103+
describe('generateCacheKey', () => {
104+
test('generating key for selector options', async () => {
105+
await measureFunction(
106+
() => {
107+
cache.generateCacheKey(selectorOptions);
108+
},
109+
{
110+
beforeEach: resetCacheBeforeEachMeasure,
111+
},
112+
);
113+
});
114+
115+
test('generating key for complex selector options', async () => {
116+
await measureFunction(
117+
() => {
118+
cache.generateCacheKey(complexSelectorOptions);
119+
},
120+
{
121+
beforeEach: resetCacheBeforeEachMeasure,
122+
},
123+
);
124+
});
125+
126+
test('generating 1000 cache keys with different selectors', async () => {
127+
await measureFunction(
128+
() => {
129+
for (let i = 0; i < 1000; i++) {
130+
const selector = (data: unknown) => ((data as MockData | undefined)?.field ?? '') + i;
131+
const options: UseOnyxOptions<string, string> = {...selectorOptions, selector};
132+
cache.generateCacheKey(options);
133+
}
134+
},
135+
{
136+
beforeEach: resetCacheBeforeEachMeasure,
137+
},
138+
);
139+
});
140+
});
141+
142+
describe('getCachedResult', () => {
143+
test('getting cached result (cache hit)', async () => {
144+
const cacheKey = cache.generateCacheKey(selectorOptions);
145+
await measureFunction(
146+
() => {
147+
cache.getCachedResult(ONYXKEYS.TEST_KEY, cacheKey);
148+
},
149+
{
150+
beforeEach: () => {
151+
resetCacheBeforeEachMeasure();
152+
const key = cache.generateCacheKey(selectorOptions);
153+
cache.setCachedResult(ONYXKEYS.TEST_KEY, key, mockResult);
154+
},
155+
},
156+
);
157+
});
158+
159+
test('getting cached result with complex selector (cache hit)', async () => {
160+
const cacheKey = cache.generateCacheKey(complexSelectorOptions);
161+
const complexResult: UseOnyxResult<{id?: number; name?: string; computed: number; formatted: string}> = [
162+
{id: 1, name: 'Test', computed: 84, formatted: 'Test: 42'},
163+
{status: 'loaded' as const, sourceValue: {id: 1, name: 'Test', computed: 84, formatted: 'Test: 42'}},
164+
];
165+
await measureFunction(
166+
() => {
167+
cache.getCachedResult(ONYXKEYS.TEST_KEY, cacheKey);
168+
},
169+
{
170+
beforeEach: () => {
171+
resetCacheBeforeEachMeasure();
172+
const key = cache.generateCacheKey(complexSelectorOptions);
173+
cache.setCachedResult(ONYXKEYS.TEST_KEY, key, complexResult);
174+
},
175+
},
176+
);
177+
});
178+
179+
test('getting cached result with 1000 keys in cache', async () => {
180+
const cacheKey = cache.generateCacheKey(selectorOptions);
181+
await measureFunction(
182+
() => {
183+
cache.getCachedResult(ONYXKEYS.TEST_KEY, cacheKey);
184+
},
185+
{
186+
beforeEach: () => {
187+
resetCacheBeforeEachMeasure();
188+
// Pre-populate cache with 1000 entries
189+
for (let i = 0; i < 1000; i++) {
190+
const key = `test_key_${i}`;
191+
const result = mockResults[i];
192+
cache.setCachedResult(key, `cache_key_${i}`, result);
193+
}
194+
// Set our target entry
195+
cache.setCachedResult(ONYXKEYS.TEST_KEY, cacheKey, mockResult);
196+
},
197+
},
198+
);
199+
});
200+
});
201+
202+
describe('setCachedResult', () => {
203+
test('setting cached result for new key', async () => {
204+
const cacheKey = cache.generateCacheKey(selectorOptions);
205+
await measureFunction(
206+
() => {
207+
cache.setCachedResult(ONYXKEYS.TEST_KEY, cacheKey, mockResult);
208+
},
209+
{
210+
beforeEach: resetCacheBeforeEachMeasure,
211+
},
212+
);
213+
});
214+
215+
test('setting cached result for existing key', async () => {
216+
const cacheKey = cache.generateCacheKey(selectorOptions);
217+
await measureFunction(
218+
() => {
219+
cache.setCachedResult(ONYXKEYS.TEST_KEY, cacheKey, mockResult);
220+
},
221+
{
222+
beforeEach: () => {
223+
resetCacheBeforeEachMeasure();
224+
// Pre-create the key cache
225+
cache.setCachedResult(ONYXKEYS.TEST_KEY, 'other_cache_key', mockResult);
226+
},
227+
},
228+
);
229+
});
230+
});
231+
232+
describe('invalidateForKey', () => {
233+
test('invalidating single key (cache hit)', async () => {
234+
await measureFunction(
235+
() => {
236+
cache.invalidateForKey(ONYXKEYS.TEST_KEY);
237+
},
238+
{
239+
beforeEach: () => {
240+
resetCacheBeforeEachMeasure();
241+
const cacheKey = cache.generateCacheKey(selectorOptions);
242+
cache.setCachedResult(ONYXKEYS.TEST_KEY, cacheKey, mockResult);
243+
},
244+
},
245+
);
246+
});
247+
248+
test('invalidating collection member key', async () => {
249+
const collectionMemberKey = `${ONYXKEYS.COLLECTION.REPORTS}123`;
250+
await measureFunction(
251+
() => {
252+
cache.invalidateForKey(collectionMemberKey);
253+
},
254+
{
255+
beforeEach: () => {
256+
resetCacheBeforeEachMeasure();
257+
const cacheKey = cache.generateCacheKey(selectorOptions);
258+
// Cache both collection and member
259+
cache.setCachedResult(ONYXKEYS.COLLECTION.REPORTS, cacheKey, mockResult);
260+
cache.setCachedResult(collectionMemberKey, cacheKey, mockResult);
261+
},
262+
},
263+
);
264+
});
265+
});
266+
});

0 commit comments

Comments
 (0)