Skip to content

Commit 259f0a3

Browse files
committed
Updated test to save/retrieve large values
The test no longer creates large strings (neither before writing nor after reading). That is however insufficient to avoid the OOM since the underlying implementation read all the bytes in memory for encryption/decryption. So I lowered the size of the test data. NB: I did attempt to implement a memory-efficient kv-store using CipherOutputStream and CipherInputStream (see wmathurin@047def6) - it was noticeably slower - it also caused OOMs ! Maybe Android's Conscrypt AES-GCM cipher implementation has internal buffering !! ??
1 parent c9f0586 commit 259f0a3

1 file changed

Lines changed: 92 additions & 8 deletions

File tree

libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/KeyValueEncryptedFileStoreTest.java

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,25 @@ public void testSaveStreamGetValue() {
270270
}
271271
}
272272

273+
/**
274+
* Test saving and getting large streams to verify memory efficiency
275+
*/
273276
@Test
274-
public void testSaveStreamGetLargeValue() {
275-
for (int i = 0; i < 24; i++) {
276-
String key = "key" + i;
277-
String value = getLargeString((int) Math.pow(2, i));
278-
InputStream stream = stringToStream(value);
279-
keyValueStore.saveStream(key, stream);
280-
Assert.assertEquals(
281-
"Wrong value for key: " + key, value, keyValueStore.getValue(key));
277+
public void testSaveLargeStreamGetLargeStream() throws IOException {
278+
final String key = "largeStreamKey";
279+
final int dataSize = 5 * 1024 * 1024; // 16MB
280+
281+
// Generate and save large stream
282+
try (InputStream largeStream = getLargeStringStream(dataSize)) {
283+
keyValueStore.saveStream(key, largeStream);
284+
}
285+
286+
// Retrieve and verify the stream
287+
try (InputStream retrievedStream = keyValueStore.getStream(key);
288+
InputStream expectedStream = getLargeStringStream(dataSize)) {
289+
290+
Assert.assertNotNull("Retrieved stream should not be null", retrievedStream);
291+
Assert.assertTrue("Streams should be equal", streamsEqual(expectedStream, retrievedStream));
282292
}
283293
}
284294

@@ -875,4 +885,78 @@ private String streamToString(InputStream inputStream) {
875885
}
876886
}
877887
}
888+
889+
/**
890+
* Helper method to create a large string stream without loading all data into memory
891+
* @param size Size in bytes
892+
* @return InputStream containing the large string data
893+
*/
894+
private InputStream getLargeStringStream(int size) {
895+
return new InputStream() {
896+
private int bytesRead = 0;
897+
private final String pattern = "0123456789ABCDEF";
898+
private final byte[] patternBytes = pattern.getBytes(StandardCharsets.UTF_8);
899+
private final int patternLength = patternBytes.length;
900+
901+
@Override
902+
public int read() throws IOException {
903+
if (bytesRead >= size) {
904+
return -1; // End of stream
905+
}
906+
byte b = patternBytes[bytesRead % patternLength];
907+
bytesRead++;
908+
return b & 0xFF;
909+
}
910+
911+
@Override
912+
public int read(byte[] buffer, int offset, int length) throws IOException {
913+
if (bytesRead >= size) {
914+
return -1; // End of stream
915+
}
916+
917+
int bytesToRead = Math.min(length, size - bytesRead);
918+
for (int i = 0; i < bytesToRead; i++) {
919+
buffer[offset + i] = patternBytes[(bytesRead + i) % patternLength];
920+
}
921+
bytesRead += bytesToRead;
922+
return bytesToRead;
923+
}
924+
};
925+
}
926+
927+
/**
928+
* Helper method to compare two streams for equality without loading all data into memory
929+
* @param stream1 First stream
930+
* @param stream2 Second stream
931+
* @return true if streams contain identical data
932+
* @throws IOException if there's an error reading the streams
933+
*/
934+
private boolean streamsEqual(InputStream stream1, InputStream stream2) throws IOException {
935+
byte[] buffer1 = new byte[8192];
936+
byte[] buffer2 = new byte[8192];
937+
938+
int bytesRead1, bytesRead2;
939+
940+
while (true) {
941+
bytesRead1 = stream1.read(buffer1);
942+
bytesRead2 = stream2.read(buffer2);
943+
944+
// Check if both streams reached end
945+
if (bytesRead1 == -1 && bytesRead2 == -1) {
946+
return true;
947+
}
948+
949+
// Check if only one stream reached end
950+
if (bytesRead1 != bytesRead2) {
951+
return false;
952+
}
953+
954+
// Compare the read bytes
955+
for (int i = 0; i < bytesRead1; i++) {
956+
if (buffer1[i] != buffer2[i]) {
957+
return false;
958+
}
959+
}
960+
}
961+
}
878962
}

0 commit comments

Comments
 (0)