Android: Sort modules by ID when serializing delta bundle

Summary:
Fixes redbox/yellowbox symbolication when the Java delta client is enabled. Previously the modules would get concatenated in a nondeterministic order (owing to Metro's parallelism) which differed from their order in the source map, where they're explicitly sorted by module ID.

This diff changes the data structure holding modules in memory from a `LinkedHashMap` (which iterates in insertion order) to a `TreeMap` (which iterates in key order).

NOTE: Similar to this change in the Chrome debugger's delta client: https://github.com/react-native-community/cli/pull/279

Reviewed By: dcaspi

Differential Revision: D15301927

fbshipit-source-id: 27bdecfb3d6963aa358e4d542c8b7663fd9eb437
This commit is contained in:
Moti Zilberman
2019-05-14 10:24:57 -07:00
committed by Facebook Github Bot
parent 4105450c7e
commit a05e9f8e09
2 changed files with 145 additions and 4 deletions
@@ -0,0 +1,141 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.facebook.react.devsupport;
import static org.fest.assertions.api.Assertions.assertThat;
import com.facebook.react.common.StandardCharsets;
import com.facebook.react.devsupport.BundleDeltaClient;
import org.junit.Test;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import okio.BufferedSource;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import okio.Okio;
import java.io.ByteArrayInputStream;
import java.nio.file.Files;
import java.io.File;
import java.io.IOException;
@RunWith(RobolectricTestRunner.class)
public class BundleDeltaClientTest {
private BundleDeltaClient mClient;
@Rule public TemporaryFolder mFolder = new TemporaryFolder();
@Before
public void setUp() {
mClient = BundleDeltaClient.create(BundleDeltaClient.ClientType.DEV_SUPPORT);
}
@Test
public void testAcceptsSimpleInitialBundle() throws IOException {
File file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"pre\": \"console.log('Hello World!');\","
+ "\"post\": \"console.log('That is all folks!');\","
+ "\"modules\": [[0, \"console.log('Best module.');\"]]"
+ "}"),
file);
assertThat(contentOf(file))
.isEqualTo(
"console.log('Hello World!');\n"
+ "console.log('Best module.');\n"
+ "console.log('That is all folks!');\n");
}
@Test
public void testPatchesInitialBundleWithDeltaBundle() throws IOException {
File file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"pre\": \"pre\","
+ "\"post\": \"post\","
+ "\"modules\": [[0, \"0\"], [1, \"1\"]]"
+ "}"),
file);
file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"added\": [[2, \"2\"]],"
+ "\"modified\": [[0, \"0.1\"]],"
+ "\"deleted\": [1]"
+ "}"),
file);
assertThat(contentOf(file))
.isEqualTo(
"pre\n"
+ "0.1\n"
+ "2\n"
+ "post\n");
}
@Test
public void testSortsModulesByIdInInitialBundle() throws IOException {
File file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"pre\": \"console.log('Hello World!');\","
+ "\"post\": \"console.log('That is all folks!');\","
+ "\"modules\": [[3, \"3\"], [0, \"0\"], [2, \"2\"], [1, \"1\"]]"
+ "}"),
file);
assertThat(contentOf(file))
.isEqualTo(
"console.log('Hello World!');\n"
+ "0\n"
+ "1\n"
+ "2\n"
+ "3\n"
+ "console.log('That is all folks!');\n");
}
@Test
public void testSortsModulesByIdInPatchedBundle() throws IOException {
File file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"pre\": \"console.log('Hello World!');\","
+ "\"post\": \"console.log('That is all folks!');\","
+ "\"modules\": [[3, \"3\"], [0, \"0\"], [1, \"1\"]]"
+ "}"),
file);
file = mFolder.newFile();
mClient.processDelta(
bufferedSource(
"{"
+ "\"added\": [[2, \"2\"]],"
+ "\"modified\": [[0, \"0.1\"]],"
+ "\"deleted\": [1]"
+ "}"),
file);
assertThat(contentOf(file))
.isEqualTo(
"console.log('Hello World!');\n"
+ "0.1\n"
+ "2\n"
+ "3\n"
+ "console.log('That is all folks!');\n");
}
private static BufferedSource bufferedSource(String string) {
return Okio.buffer(
Okio.source(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8))));
}
private static String contentOf(File file) throws IOException {
return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
}
}