Skip to content

Commit d6e2afe

Browse files
committed
test: add regression test for high contention calls
1 parent d6ca8b5 commit d6e2afe

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

test-app/app/src/main/assets/app/mainpage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ require('./tests/testURLImpl.js');
7272
require('./tests/testURLSearchParamsImpl.js');
7373
require('./tests/testPerformanceNow');
7474
require('./tests/testQueueMicrotask');
75+
require("./tests/testConcurrentAccess");
7576

7677
require("./tests/testESModules.mjs");
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// WARNING: IF THIS TEST FAILS IT COMPLETELY BREAKS ALL OTHER TESTS!
2+
3+
describe("Tests concurrent access to JNI", function () {
4+
// Customizable test parameters
5+
const BACKGROUND_THREADS = 5;
6+
const SYNC_CALLS = 2;
7+
const ITERATIONS_PER_CALL = 100;
8+
const TIMEOUT_MS = 3000;
9+
10+
it("test_high_contention_concurrent_access_with_multiple_objects", (done) => {
11+
console.log('STARTING PROBLEMATIC TEST. THIS MIGHT CRASH OR CAUSE ISSUES IN OTHER TESTS IF IT FAILS. If this is close to the end of the log, check test_high_contention_concurrent_access_with_multiple_objects');
12+
let callbackInvocations = 0;
13+
14+
const callback = new com.tns.tests.ConcurrentAccessTest.Callback({
15+
invoke: (
16+
list1,
17+
list2,
18+
list3,
19+
list4,
20+
list5,
21+
list6,
22+
list7,
23+
list8,
24+
list9,
25+
list10,
26+
) => {
27+
callbackInvocations++;
28+
// Assert that accessing size() on any of the lists doesn't throw
29+
expect(() => list1.size()).not.toThrow();
30+
expect(() => list2.size()).not.toThrow();
31+
expect(() => list3.size()).not.toThrow();
32+
expect(() => list4.size()).not.toThrow();
33+
expect(() => list5.size()).not.toThrow();
34+
expect(() => list6.size()).not.toThrow();
35+
expect(() => list7.size()).not.toThrow();
36+
expect(() => list8.size()).not.toThrow();
37+
expect(() => list9.size()).not.toThrow();
38+
expect(() => list10.size()).not.toThrow();
39+
40+
// Verify that the lists actually have content
41+
expect(list1.size()).toBe(5);
42+
expect(list2.size()).toBe(5);
43+
expect(list3.size()).toBe(5);
44+
expect(list4.size()).toBe(5);
45+
expect(list5.size()).toBe(5);
46+
expect(list6.size()).toBe(5);
47+
expect(list7.size()).toBe(5);
48+
expect(list8.size()).toBe(5);
49+
expect(list9.size()).toBe(5);
50+
expect(list10.size()).toBe(5);
51+
},
52+
});
53+
54+
// Start multiple background threads
55+
for (let i = 0; i < BACKGROUND_THREADS; i++) {
56+
com.tns.tests.ConcurrentAccessTest.callFromBackgroundThread(
57+
callback,
58+
ITERATIONS_PER_CALL,
59+
);
60+
}
61+
62+
// Call synchronously multiple times
63+
for (let i = 0; i < SYNC_CALLS; i++) {
64+
com.tns.tests.ConcurrentAccessTest.callSynchronously(
65+
callback,
66+
ITERATIONS_PER_CALL,
67+
);
68+
}
69+
70+
// Wait for all threads to complete
71+
setTimeout(() => {
72+
const expectedInvocations =
73+
(BACKGROUND_THREADS + SYNC_CALLS) * ITERATIONS_PER_CALL;
74+
expect(callbackInvocations).toBe(expectedInvocations);
75+
done();
76+
}, TIMEOUT_MS);
77+
});
78+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.tns.tests;
2+
3+
import java.util.ArrayList;
4+
5+
public class ConcurrentAccessTest {
6+
7+
public interface Callback {
8+
void invoke(ArrayList list1, ArrayList list2, ArrayList list3, ArrayList list4, ArrayList list5,
9+
ArrayList list6, ArrayList list7, ArrayList list8, ArrayList list9, ArrayList list10);
10+
}
11+
12+
public interface ErrorCallback {
13+
void onError(Throwable error);
14+
}
15+
16+
/**
17+
* Calls the callback from a background thread multiple times.
18+
* @param callback The callback to invoke
19+
* @param times Number of times to call the callback (default 50)
20+
*/
21+
public static void callFromBackgroundThread(final Callback callback, final int times) {
22+
Thread thread = new Thread(new Runnable() {
23+
@Override
24+
public void run() {
25+
for (int i = 0; i < times; i++) {
26+
invokeCallbackWithArrayLists(callback, i);
27+
}
28+
}
29+
});
30+
thread.start();
31+
}
32+
33+
/**
34+
* Calls the callback synchronously from the current thread.
35+
* @param callback The callback to invoke
36+
* @param times Number of times to call the callback (default 50)
37+
*/
38+
public static void callSynchronously(Callback callback, int times) {
39+
for (int i = 0; i < times; i++) {
40+
invokeCallbackWithArrayLists(callback, i);
41+
}
42+
}
43+
44+
/**
45+
* Helper method that creates 10 ArrayLists and invokes the callback with them.
46+
* Each ArrayList contains some data based on the iteration number.
47+
*/
48+
private static void invokeCallbackWithArrayLists(Callback callback, int iteration) {
49+
ArrayList<Integer> list1 = new ArrayList<>();
50+
ArrayList<Integer> list2 = new ArrayList<>();
51+
ArrayList<Integer> list3 = new ArrayList<>();
52+
ArrayList<Integer> list4 = new ArrayList<>();
53+
ArrayList<Integer> list5 = new ArrayList<>();
54+
ArrayList<Integer> list6 = new ArrayList<>();
55+
ArrayList<Integer> list7 = new ArrayList<>();
56+
ArrayList<Integer> list8 = new ArrayList<>();
57+
ArrayList<Integer> list9 = new ArrayList<>();
58+
ArrayList<Integer> list10 = new ArrayList<>();
59+
60+
// Add some data to each list
61+
for (int i = 0; i < 5; i++) {
62+
list1.add(iteration * 10 + i);
63+
list2.add(iteration * 10 + i + 1);
64+
list3.add(iteration * 10 + i + 2);
65+
list4.add(iteration * 10 + i + 3);
66+
list5.add(iteration * 10 + i + 4);
67+
list6.add(iteration * 10 + i + 5);
68+
list7.add(iteration * 10 + i + 6);
69+
list8.add(iteration * 10 + i + 7);
70+
list9.add(iteration * 10 + i + 8);
71+
list10.add(iteration * 10 + i + 9);
72+
}
73+
74+
callback.invoke(list1, list2, list3, list4, list5, list6, list7, list8, list9, list10);
75+
}
76+
}

0 commit comments

Comments
 (0)