mirror of
https://github.com/status-im/react-native.git
synced 2025-02-26 16:10:58 +00:00
Moved React Native Android unit tests to open source
Reviewed By: mkonicek Differential Revision: D2685799 fb-gh-sync-id: 56f061df58641c8cb13fc16bad5f87039f0c49fb
This commit is contained in:
parent
a7e2059c15
commit
c0f60d2018
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import com.facebook.react.bridge.queue.CatalystQueueConfiguration;
|
||||
import com.facebook.react.bridge.queue.CatalystQueueConfigurationImpl;
|
||||
import com.facebook.react.bridge.queue.CatalystQueueConfigurationSpec;
|
||||
import com.facebook.react.bridge.queue.MessageQueueThreadSpec;
|
||||
import com.facebook.react.bridge.queue.QueueThreadExceptionHandler;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Utility for creating pre-configured instances of core react components for tests.
|
||||
*/
|
||||
public class CatalystTestHelper {
|
||||
|
||||
/**
|
||||
* @return a ReactApplicationContext that has a CatalystInstance mock returned by
|
||||
* {@link #createMockCatalystInstance}
|
||||
*/
|
||||
public static ReactApplicationContext createCatalystContextForTest() {
|
||||
ReactApplicationContext context =
|
||||
new ReactApplicationContext(RuntimeEnvironment.application);
|
||||
context.initializeWithInstance(createMockCatalystInstance());
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a CatalystInstance mock that has a default working CatalystQueueConfiguration.
|
||||
*/
|
||||
public static CatalystInstance createMockCatalystInstance() {
|
||||
CatalystQueueConfigurationSpec spec = CatalystQueueConfigurationSpec.builder()
|
||||
.setJSQueueThreadSpec(MessageQueueThreadSpec.mainThreadSpec())
|
||||
.setNativeModulesQueueThreadSpec(MessageQueueThreadSpec.mainThreadSpec())
|
||||
.build();
|
||||
CatalystQueueConfiguration catalystQueueConfiguration = CatalystQueueConfigurationImpl.create(
|
||||
spec,
|
||||
new QueueThreadExceptionHandler() {
|
||||
@Override
|
||||
public void handleException(Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
CatalystInstance reactInstance = mock(CatalystInstance.class);
|
||||
when(reactInstance.getCatalystQueueConfiguration()).thenReturn(catalystQueueConfiguration);
|
||||
when(reactInstance.getNativeModule(UIManagerModule.class))
|
||||
.thenReturn(mock(UIManagerModule.class));
|
||||
|
||||
return reactInstance;
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple read/write array that can be used in tests in place of {@link WritableNativeArray}.
|
||||
*/
|
||||
public class SimpleArray implements ReadableArray, WritableArray {
|
||||
|
||||
private final List mBackingList;
|
||||
|
||||
public static SimpleArray from(List list) {
|
||||
return new SimpleArray(list);
|
||||
}
|
||||
|
||||
public static SimpleArray of(Object... values) {
|
||||
return new SimpleArray(values);
|
||||
}
|
||||
|
||||
private SimpleArray(Object... values) {
|
||||
mBackingList = Arrays.asList(values);
|
||||
}
|
||||
|
||||
private SimpleArray(List list) {
|
||||
mBackingList = new ArrayList(list);
|
||||
}
|
||||
|
||||
public SimpleArray() {
|
||||
mBackingList = new ArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return mBackingList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int index) {
|
||||
return mBackingList.get(index) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return (Double) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return (Integer) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int index) {
|
||||
return (String) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleArray getArray(int index) {
|
||||
return (SimpleArray) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(int index) {
|
||||
return (Boolean) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleMap getMap(int index) {
|
||||
return (SimpleMap) mBackingList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableType getType(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushBoolean(boolean value) {
|
||||
mBackingList.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushDouble(double value) {
|
||||
mBackingList.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushInt(int value) {
|
||||
mBackingList.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushString(String value) {
|
||||
mBackingList.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushArray(WritableArray array) {
|
||||
mBackingList.add(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushMap(WritableMap map) {
|
||||
mBackingList.add(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushNull() {
|
||||
mBackingList.add(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mBackingList.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SimpleArray that = (SimpleArray) o;
|
||||
|
||||
if (mBackingList != null ? !mBackingList.equals(that.mBackingList) : that.mBackingList != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mBackingList != null ? mBackingList.hashCode() : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A simple read/write map that can be used in tests in place of {@link WritableNativeMap}.
|
||||
*/
|
||||
public class SimpleMap implements ReadableMap, WritableMap {
|
||||
|
||||
private final Map mBackingMap;
|
||||
|
||||
public static SimpleMap of(Object... keysAndValues) {
|
||||
return new SimpleMap(keysAndValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keysAndValues keys and values, interleaved
|
||||
*/
|
||||
private SimpleMap(Object... keysAndValues) {
|
||||
if (keysAndValues.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("You must provide the same number of keys and values");
|
||||
}
|
||||
mBackingMap = new HashMap();
|
||||
for (int i = 0; i < keysAndValues.length; i += 2) {
|
||||
mBackingMap.put(keysAndValues[i], keysAndValues[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleMap() {
|
||||
mBackingMap = new HashMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(String name) {
|
||||
return mBackingMap.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(String name) {
|
||||
return mBackingMap.get(name) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String name) {
|
||||
return (Boolean) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String name) {
|
||||
return (Double) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String name) {
|
||||
return (Integer) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String name) {
|
||||
return (String) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleMap getMap(String name) {
|
||||
return (SimpleMap) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleArray getArray(String name) {
|
||||
return (SimpleArray) mBackingMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableType getType(String name) {
|
||||
throw new UnsupportedOperationException("Method not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableMapKeySetIterator keySetIterator() {
|
||||
return new ReadableMapKeySetIterator() {
|
||||
Iterator<String> mIterator = mBackingMap.keySet().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNextKey() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nextKey() {
|
||||
return mIterator.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBoolean(String key, boolean value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDouble(String key, double value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInt(String key, int value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(String key, String value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putNull(String key) {
|
||||
mBackingMap.put(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putMap(String key, WritableMap value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(ReadableMap source) {
|
||||
mBackingMap.putAll(((SimpleMap) source).mBackingMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putArray(String key, WritableArray value) {
|
||||
mBackingMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mBackingMap.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SimpleMap that = (SimpleMap) o;
|
||||
|
||||
if (mBackingMap != null ? !mBackingMap.equals(that.mBackingMap) : that.mBackingMap != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mBackingMap != null ? mBackingMap.hashCode() : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,466 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.network;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.SimpleArray;
|
||||
import com.facebook.react.bridge.SimpleMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
import com.squareup.okhttp.Call;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.MultipartBuilder;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import okio.Buffer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.modules.junit4.rule.PowerMockRule;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link NetworkingModule}.
|
||||
*/
|
||||
@PrepareForTest({
|
||||
Arguments.class,
|
||||
Call.class,
|
||||
RequestBodyUtil.class,
|
||||
MultipartBuilder.class,
|
||||
NetworkingModule.class,
|
||||
OkHttpClient.class})
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
|
||||
public class NetworkingModuleTest {
|
||||
|
||||
@Rule
|
||||
public PowerMockRule rule = new PowerMockRule();
|
||||
|
||||
@Test
|
||||
public void testGetWithoutHeaders() throws Exception {
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"GET",
|
||||
"http://somedomain/foo",
|
||||
0,
|
||||
SimpleArray.of(),
|
||||
null,
|
||||
true);
|
||||
|
||||
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
|
||||
verify(httpClient).newCall(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().urlString()).isEqualTo("http://somedomain/foo");
|
||||
// We set the User-Agent header by default
|
||||
assertThat(argumentCaptor.getValue().headers().size()).isEqualTo(1);
|
||||
assertThat(argumentCaptor.getValue().method()).isEqualTo("GET");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailGetWithInvalidHeadersStruct() throws Exception {
|
||||
RCTDeviceEventEmitter emitter = mock(RCTDeviceEventEmitter.class);
|
||||
ReactApplicationContext context = mock(ReactApplicationContext.class);
|
||||
when(context.getJSModule(any(Class.class))).thenReturn(emitter);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
NetworkingModule networkingModule = new NetworkingModule(context, "", httpClient);
|
||||
|
||||
List<SimpleArray> invalidHeaders = Arrays.asList(SimpleArray.of("foo"));
|
||||
|
||||
mockEvents();
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"GET",
|
||||
"http://somedoman/foo",
|
||||
0,
|
||||
SimpleArray.from(invalidHeaders),
|
||||
null,
|
||||
true);
|
||||
|
||||
verifyErrorEmit(emitter, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailPostWithoutContentType() throws Exception {
|
||||
RCTDeviceEventEmitter emitter = mock(RCTDeviceEventEmitter.class);
|
||||
ReactApplicationContext context = mock(ReactApplicationContext.class);
|
||||
when(context.getJSModule(any(Class.class))).thenReturn(emitter);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
NetworkingModule networkingModule = new NetworkingModule(context, "", httpClient);
|
||||
|
||||
SimpleMap body = new SimpleMap();
|
||||
body.putString("string", "This is request body");
|
||||
|
||||
mockEvents();
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://somedomain/bar",
|
||||
0,
|
||||
SimpleArray.of(),
|
||||
body,
|
||||
true);
|
||||
|
||||
verifyErrorEmit(emitter, 0);
|
||||
}
|
||||
|
||||
private static void verifyErrorEmit(RCTDeviceEventEmitter emitter, int requestId) {
|
||||
ArgumentCaptor<WritableArray> captor = ArgumentCaptor.forClass(WritableArray.class);
|
||||
verify(emitter).emit(eq("didCompleteNetworkResponse"), captor.capture());
|
||||
|
||||
WritableArray array = captor.getValue();
|
||||
assertThat(array.getInt(0)).isEqualTo(requestId);
|
||||
assertThat(array.getString(1)).isNotNull();
|
||||
}
|
||||
|
||||
private static void mockEvents() {
|
||||
PowerMockito.mockStatic(Arguments.class);
|
||||
Mockito.when(Arguments.createArray()).thenAnswer(
|
||||
new Answer<WritableArray>() {
|
||||
@Override
|
||||
public WritableArray answer(InvocationOnMock invocation) throws Throwable {
|
||||
return new SimpleArray();
|
||||
}
|
||||
});
|
||||
|
||||
Mockito.when(Arguments.createMap()).thenAnswer(
|
||||
new Answer<WritableMap>() {
|
||||
@Override
|
||||
public WritableMap answer(InvocationOnMock invocation) throws Throwable {
|
||||
return new SimpleMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfullPostRequest() throws Exception {
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
|
||||
SimpleMap body = new SimpleMap();
|
||||
body.putString("string", "This is request body");
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://somedomain/bar",
|
||||
0,
|
||||
SimpleArray.of(SimpleArray.of("Content-Type", "text/plain")),
|
||||
body,
|
||||
true);
|
||||
|
||||
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
|
||||
verify(httpClient).newCall(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().urlString()).isEqualTo("http://somedomain/bar");
|
||||
assertThat(argumentCaptor.getValue().headers().size()).isEqualTo(2);
|
||||
assertThat(argumentCaptor.getValue().method()).isEqualTo("POST");
|
||||
assertThat(argumentCaptor.getValue().body().contentType().type()).isEqualTo("text");
|
||||
assertThat(argumentCaptor.getValue().body().contentType().subtype()).isEqualTo("plain");
|
||||
Buffer contentBuffer = new Buffer();
|
||||
argumentCaptor.getValue().body().writeTo(contentBuffer);
|
||||
assertThat(contentBuffer.readUtf8()).isEqualTo("This is request body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaders() throws Exception {
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
|
||||
List<SimpleArray> headers = Arrays.asList(
|
||||
SimpleArray.of("Accept", "text/plain"),
|
||||
SimpleArray.of("User-Agent", "React test agent/1.0"));
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"GET",
|
||||
"http://someurl/baz",
|
||||
0,
|
||||
SimpleArray.from(headers),
|
||||
null,
|
||||
true);
|
||||
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
|
||||
verify(httpClient).newCall(argumentCaptor.capture());
|
||||
Headers requestHeaders = argumentCaptor.getValue().headers();
|
||||
assertThat(requestHeaders.size()).isEqualTo(2);
|
||||
assertThat(requestHeaders.get("Accept")).isEqualTo("text/plain");
|
||||
assertThat(requestHeaders.get("User-Agent")).isEqualTo("React test agent/1.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipartPostRequestSimple() throws Exception {
|
||||
PowerMockito.mockStatic(RequestBodyUtil.class);
|
||||
when(RequestBodyUtil.getFileInputStream(any(ReactContext.class), any(String.class)))
|
||||
.thenReturn(mock(InputStream.class));
|
||||
when(RequestBodyUtil.create(any(MediaType.class), any(InputStream.class)))
|
||||
.thenReturn(mock(RequestBody.class));
|
||||
|
||||
SimpleMap body = new SimpleMap();
|
||||
SimpleArray formData = new SimpleArray();
|
||||
SimpleMap bodyPart = new SimpleMap();
|
||||
bodyPart.putString("string", "value");
|
||||
bodyPart.putArray(
|
||||
"headers",
|
||||
SimpleArray.from(
|
||||
Arrays.asList(
|
||||
SimpleArray.of("content-disposition", "name"))));
|
||||
formData.pushMap(bodyPart);
|
||||
body.putArray("formData", formData);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://someurl/uploadFoo",
|
||||
0,
|
||||
new SimpleArray(),
|
||||
body,
|
||||
true);
|
||||
|
||||
// verify url, method, headers
|
||||
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
|
||||
verify(httpClient).newCall(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().urlString()).isEqualTo("http://someurl/uploadFoo");
|
||||
assertThat(argumentCaptor.getValue().method()).isEqualTo("POST");
|
||||
assertThat(argumentCaptor.getValue().body().contentType().type()).
|
||||
isEqualTo(MultipartBuilder.FORM.type());
|
||||
assertThat(argumentCaptor.getValue().body().contentType().subtype()).
|
||||
isEqualTo(MultipartBuilder.FORM.subtype());
|
||||
Headers requestHeaders = argumentCaptor.getValue().headers();
|
||||
assertThat(requestHeaders.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipartPostRequestHeaders() throws Exception {
|
||||
PowerMockito.mockStatic(RequestBodyUtil.class);
|
||||
when(RequestBodyUtil.getFileInputStream(any(ReactContext.class), any(String.class)))
|
||||
.thenReturn(mock(InputStream.class));
|
||||
when(RequestBodyUtil.create(any(MediaType.class), any(InputStream.class)))
|
||||
.thenReturn(mock(RequestBody.class));
|
||||
|
||||
List<SimpleArray> headers = Arrays.asList(
|
||||
SimpleArray.of("Accept", "text/plain"),
|
||||
SimpleArray.of("User-Agent", "React test agent/1.0"),
|
||||
SimpleArray.of("content-type", "multipart/form-data"));
|
||||
|
||||
SimpleMap body = new SimpleMap();
|
||||
SimpleArray formData = new SimpleArray();
|
||||
SimpleMap bodyPart = new SimpleMap();
|
||||
bodyPart.putString("string", "value");
|
||||
bodyPart.putArray(
|
||||
"headers",
|
||||
SimpleArray.from(
|
||||
Arrays.asList(
|
||||
SimpleArray.of("content-disposition", "name"))));
|
||||
formData.pushMap(bodyPart);
|
||||
body.putArray("formData", formData);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://someurl/uploadFoo",
|
||||
0,
|
||||
SimpleArray.from(headers),
|
||||
body,
|
||||
true);
|
||||
|
||||
// verify url, method, headers
|
||||
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
|
||||
verify(httpClient).newCall(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().urlString()).isEqualTo("http://someurl/uploadFoo");
|
||||
assertThat(argumentCaptor.getValue().method()).isEqualTo("POST");
|
||||
assertThat(argumentCaptor.getValue().body().contentType().type()).
|
||||
isEqualTo(MultipartBuilder.FORM.type());
|
||||
assertThat(argumentCaptor.getValue().body().contentType().subtype()).
|
||||
isEqualTo(MultipartBuilder.FORM.subtype());
|
||||
Headers requestHeaders = argumentCaptor.getValue().headers();
|
||||
assertThat(requestHeaders.size()).isEqualTo(3);
|
||||
assertThat(requestHeaders.get("Accept")).isEqualTo("text/plain");
|
||||
assertThat(requestHeaders.get("User-Agent")).isEqualTo("React test agent/1.0");
|
||||
assertThat(requestHeaders.get("content-type")).isEqualTo("multipart/form-data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipartPostRequestBody() throws Exception {
|
||||
InputStream inputStream = mock(InputStream.class);
|
||||
PowerMockito.mockStatic(RequestBodyUtil.class);
|
||||
when(RequestBodyUtil.getFileInputStream(any(ReactContext.class), any(String.class)))
|
||||
.thenReturn(inputStream);
|
||||
when(RequestBodyUtil.create(any(MediaType.class), any(InputStream.class))).thenCallRealMethod();
|
||||
when(inputStream.available()).thenReturn("imageUri".length());
|
||||
|
||||
final MultipartBuilder multipartBuilder = mock(MultipartBuilder.class);
|
||||
PowerMockito.whenNew(MultipartBuilder.class).withNoArguments().thenReturn(multipartBuilder);
|
||||
when(multipartBuilder.type(any(MediaType.class))).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return multipartBuilder;
|
||||
}
|
||||
});
|
||||
when(multipartBuilder.addPart(any(Headers.class), any(RequestBody.class))).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return multipartBuilder;
|
||||
}
|
||||
});
|
||||
when(multipartBuilder.build()).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return mock(RequestBody.class);
|
||||
}
|
||||
});
|
||||
|
||||
List<SimpleArray> headers = Arrays.asList(
|
||||
SimpleArray.of("content-type", "multipart/form-data"));
|
||||
|
||||
SimpleMap body = new SimpleMap();
|
||||
SimpleArray formData = new SimpleArray();
|
||||
body.putArray("formData", formData);
|
||||
|
||||
SimpleMap bodyPart = new SimpleMap();
|
||||
bodyPart.putString("string", "locale");
|
||||
bodyPart.putArray(
|
||||
"headers",
|
||||
SimpleArray.from(
|
||||
Arrays.asList(
|
||||
SimpleArray.of("content-disposition", "user"))));
|
||||
formData.pushMap(bodyPart);
|
||||
|
||||
SimpleMap imageBodyPart = new SimpleMap();
|
||||
imageBodyPart.putString("uri", "imageUri");
|
||||
imageBodyPart.putArray(
|
||||
"headers",
|
||||
SimpleArray.from(
|
||||
Arrays.asList(
|
||||
SimpleArray.of("content-type", "image/jpg"),
|
||||
SimpleArray.of("content-disposition", "filename=photo.jpg"))));
|
||||
formData.pushMap(imageBodyPart);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
Call callMock = mock(Call.class);
|
||||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://someurl/uploadFoo",
|
||||
0,
|
||||
SimpleArray.from(headers),
|
||||
body,
|
||||
true);
|
||||
|
||||
// verify RequestBodyPart for image
|
||||
PowerMockito.verifyStatic(times(1));
|
||||
RequestBodyUtil.getFileInputStream(any(ReactContext.class), eq("imageUri"));
|
||||
PowerMockito.verifyStatic(times(1));
|
||||
RequestBodyUtil.create(MediaType.parse("image/jpg"), inputStream);
|
||||
|
||||
// verify body
|
||||
verify(multipartBuilder).build();
|
||||
verify(multipartBuilder).type(MultipartBuilder.FORM);
|
||||
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
|
||||
ArgumentCaptor<RequestBody> bodyArgumentCaptor = ArgumentCaptor.forClass(RequestBody.class);
|
||||
verify(multipartBuilder, times(2)).
|
||||
addPart(headersArgumentCaptor.capture(), bodyArgumentCaptor.capture());
|
||||
|
||||
List<Headers> bodyHeaders = headersArgumentCaptor.getAllValues();
|
||||
assertThat(bodyHeaders.size()).isEqualTo(2);
|
||||
List<RequestBody> bodyRequestBody = bodyArgumentCaptor.getAllValues();
|
||||
assertThat(bodyRequestBody.size()).isEqualTo(2);
|
||||
|
||||
assertThat(bodyHeaders.get(0).get("content-disposition")).isEqualTo("user");
|
||||
assertThat(bodyRequestBody.get(0).contentType()).isNull();
|
||||
assertThat(bodyRequestBody.get(0).contentLength()).isEqualTo("locale".getBytes().length);
|
||||
assertThat(bodyHeaders.get(1).get("content-disposition")).isEqualTo("filename=photo.jpg");
|
||||
assertThat(bodyRequestBody.get(1).contentType()).isEqualTo(MediaType.parse("image/jpg"));
|
||||
assertThat(bodyRequestBody.get(1).contentLength()).isEqualTo("imageUri".getBytes().length);
|
||||
}
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.storage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.SimpleArray;
|
||||
import com.facebook.react.bridge.SimpleMap;
|
||||
import com.facebook.react.modules.storage.AsyncStorageModule;
|
||||
import com.facebook.react.modules.storage.ReactDatabaseSupplier;
|
||||
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.modules.junit4.rule.PowerMockRule;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AsyncStorageModule}.
|
||||
*/
|
||||
@PrepareForTest({Arguments.class})
|
||||
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AsyncStorageModuleTest {
|
||||
|
||||
private AsyncStorageModule mStorage;
|
||||
private SimpleArray mEmptyArray;
|
||||
|
||||
private static class FakeFragmentContext extends ContextWrapper {
|
||||
|
||||
public FakeFragmentContext(Context base) {
|
||||
super(base);
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PowerMockRule rule = new PowerMockRule();
|
||||
|
||||
@Before
|
||||
public void prepareModules() {
|
||||
PowerMockito.mockStatic(Arguments.class);
|
||||
Mockito.when(Arguments.createArray()).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return new SimpleArray();
|
||||
}
|
||||
});
|
||||
|
||||
Mockito.when(Arguments.createMap()).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return new SimpleMap();
|
||||
}
|
||||
});
|
||||
|
||||
// don't use Robolectric before initializing mocks
|
||||
mStorage = new AsyncStorageModule(new ReactApplicationContext(
|
||||
new FakeFragmentContext(RuntimeEnvironment.application)));
|
||||
mEmptyArray = new SimpleArray();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
RuntimeEnvironment.application.deleteDatabase(ReactDatabaseSupplier.DATABASE_NAME);
|
||||
ReactDatabaseSupplier.deleteInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiSetMultiGet() {
|
||||
final String key1 = "foo1";
|
||||
final String key2 = "foo2";
|
||||
final String fakeKey = "fakeKey";
|
||||
final String value1 = "bar1";
|
||||
final String value2 = "bar2";
|
||||
SimpleArray keyValues = new SimpleArray();
|
||||
keyValues.pushArray(getArray(key1, value1));
|
||||
keyValues.pushArray(getArray(key2, value2));
|
||||
|
||||
Callback setCallback = mock(Callback.class);
|
||||
mStorage.multiSet(keyValues, setCallback);
|
||||
Mockito.verify(setCallback, Mockito.times(1)).invoke();
|
||||
|
||||
SimpleArray keys = new SimpleArray();
|
||||
keys.pushString(key1);
|
||||
keys.pushString(key2);
|
||||
|
||||
Callback getCallback = mock(Callback.class);
|
||||
mStorage.multiGet(keys, getCallback);
|
||||
Mockito.verify(getCallback, Mockito.times(1)).invoke(null, keyValues);
|
||||
|
||||
keys.pushString(fakeKey);
|
||||
SimpleArray row3 = new SimpleArray();
|
||||
row3.pushString(fakeKey);
|
||||
row3.pushString(null);
|
||||
keyValues.pushArray(row3);
|
||||
|
||||
Callback getCallback2 = mock(Callback.class);
|
||||
mStorage.multiGet(keys, getCallback2);
|
||||
Mockito.verify(getCallback2, Mockito.times(1)).invoke(null, keyValues);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRemove() {
|
||||
final String key1 = "foo1";
|
||||
final String key2 = "foo2";
|
||||
final String value1 = "bar1";
|
||||
final String value2 = "bar2";
|
||||
|
||||
SimpleArray keyValues = new SimpleArray();
|
||||
keyValues.pushArray(getArray(key1, value1));
|
||||
keyValues.pushArray(getArray(key2, value2));
|
||||
mStorage.multiSet(keyValues, mock(Callback.class));
|
||||
|
||||
SimpleArray keys = new SimpleArray();
|
||||
keys.pushString(key1);
|
||||
keys.pushString(key2);
|
||||
|
||||
Callback getCallback = mock(Callback.class);
|
||||
mStorage.multiRemove(keys, getCallback);
|
||||
Mockito.verify(getCallback, Mockito.times(1)).invoke();
|
||||
|
||||
Callback getAllCallback = mock(Callback.class);
|
||||
mStorage.getAllKeys(getAllCallback);
|
||||
Mockito.verify(getAllCallback, Mockito.times(1)).invoke(null, mEmptyArray);
|
||||
|
||||
mStorage.multiSet(keyValues, mock(Callback.class));
|
||||
|
||||
keys.pushString("fakeKey");
|
||||
Callback getCallback2 = mock(Callback.class);
|
||||
mStorage.multiRemove(keys, getCallback2);
|
||||
Mockito.verify(getCallback2, Mockito.times(1)).invoke();
|
||||
|
||||
Callback getAllCallback2 = mock(Callback.class);
|
||||
mStorage.getAllKeys(getAllCallback2);
|
||||
Mockito.verify(getAllCallback2, Mockito.times(1)).invoke(null, mEmptyArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiMerge() throws Exception {
|
||||
final String mergeKey = "mergeTest";
|
||||
|
||||
JSONObject value = new JSONObject();
|
||||
value.put("foo1", "bar1");
|
||||
value.put("foo2", createJSONArray("val1", "val2", 3));
|
||||
value.put("foo3", 1001);
|
||||
value.put("foo4", createJSONObject("key1", "randomValueThatWillNeverBeUsed"));
|
||||
|
||||
mStorage.multiSet(SimpleArray.of(getArray(mergeKey, value.toString())), mock(Callback.class));
|
||||
{
|
||||
Callback callback = mock(Callback.class);
|
||||
mStorage.multiGet(getArray(mergeKey), callback);
|
||||
Mockito.verify(callback, Mockito.times(1))
|
||||
.invoke(null, SimpleArray.of(getArray(mergeKey, value.toString())));
|
||||
}
|
||||
|
||||
value.put("foo1", 1001);
|
||||
value.put("foo2", createJSONObject("key1", "val1"));
|
||||
value.put("foo3", "bar1");
|
||||
value.put("foo4", createJSONArray("val1", "val2", 3));
|
||||
|
||||
JSONObject newValue = new JSONObject();
|
||||
newValue.put("foo2", createJSONObject("key2", "val2"));
|
||||
|
||||
JSONObject newValue2 = new JSONObject();
|
||||
newValue2.put("foo2", createJSONObject("key1", "val3"));
|
||||
|
||||
mStorage.multiMerge(
|
||||
SimpleArray.of(
|
||||
SimpleArray.of(mergeKey, value.toString()),
|
||||
SimpleArray.of(mergeKey, newValue.toString()),
|
||||
SimpleArray.of(mergeKey, newValue2.toString())),
|
||||
mock(Callback.class));
|
||||
|
||||
value.put("foo2", createJSONObject("key1", "val3", "key2", "val2"));
|
||||
Callback callback = mock(Callback.class);
|
||||
mStorage.multiGet(getArray(mergeKey), callback);
|
||||
Mockito.verify(callback, Mockito.times(1))
|
||||
.invoke(null, SimpleArray.of(getArray(mergeKey, value.toString())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllKeys() {
|
||||
final String[] keys = {"foo", "foo2"};
|
||||
final String[] values = {"bar", "bar2"};
|
||||
SimpleArray keyValues = new SimpleArray();
|
||||
keyValues.pushArray(getArray(keys[0], values[0]));
|
||||
keyValues.pushArray(getArray(keys[1], values[1]));
|
||||
mStorage.multiSet(keyValues, mock(Callback.class));
|
||||
|
||||
SimpleArray storedKeys = new SimpleArray();
|
||||
storedKeys.pushString(keys[0]);
|
||||
storedKeys.pushString(keys[1]);
|
||||
|
||||
Callback getAllCallback = mock(Callback.class);
|
||||
mStorage.getAllKeys(getAllCallback);
|
||||
Mockito.verify(getAllCallback, Mockito.times(1)).invoke(null, storedKeys);
|
||||
|
||||
Callback getAllCallback2 = mock(Callback.class);
|
||||
mStorage.multiRemove(getArray(keys[0]), mock(Callback.class));
|
||||
|
||||
mStorage.getAllKeys(getAllCallback2);
|
||||
Mockito.verify(getAllCallback2, Mockito.times(1)).invoke(null, getArray(keys[1]));
|
||||
|
||||
mStorage.multiRemove(getArray(keys[1]), mock(Callback.class));
|
||||
Callback getAllCallback3 = mock(Callback.class);
|
||||
mStorage.getAllKeys(getAllCallback3);
|
||||
Mockito.verify(getAllCallback3, Mockito.times(1)).invoke(null, mEmptyArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() {
|
||||
SimpleArray keyValues = new SimpleArray();
|
||||
keyValues.pushArray(getArray("foo", "foo2"));
|
||||
keyValues.pushArray(getArray("bar", "bar2"));
|
||||
mStorage.multiSet(keyValues, mock(Callback.class));
|
||||
|
||||
Callback clearCallback2 = mock(Callback.class);
|
||||
mStorage.clear(clearCallback2);
|
||||
Mockito.verify(clearCallback2, Mockito.times(1)).invoke();
|
||||
|
||||
Callback getAllCallback2 = mock(Callback.class);
|
||||
mStorage.getAllKeys(getAllCallback2);
|
||||
Mockito.verify(getAllCallback2, Mockito.times(1)).invoke(null, mEmptyArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeMultiGetMultiGet() {
|
||||
// Test with many keys, so that it's above the 999 limit per batch imposed by SQLite.
|
||||
final int keyCount = 1001;
|
||||
// don't set keys that divide by this magical number, so that we can check that multiGet works,
|
||||
// and returns null for missing keys
|
||||
final int magicalNumber = 343;
|
||||
|
||||
SimpleArray keyValues = new SimpleArray();
|
||||
for (int i = 0; i < keyCount; i++) {
|
||||
if (i % magicalNumber > 0) {
|
||||
keyValues.pushArray(getArray("key" + i, "value" + i));
|
||||
}
|
||||
}
|
||||
mStorage.multiSet(keyValues, mock(Callback.class));
|
||||
SimpleArray keys = new SimpleArray();
|
||||
for (int i = 0; i < keyCount; i++) {
|
||||
keys.pushString("key" + i);
|
||||
}
|
||||
mStorage.multiGet(
|
||||
keys, new Callback() {
|
||||
@Override
|
||||
public void invoke(Object... args) {
|
||||
assertThat(args.length).isEqualTo(2);
|
||||
SimpleArray resultArray = (SimpleArray) args[1];
|
||||
|
||||
assertThat(resultArray.size()).isEqualTo(keyCount);
|
||||
boolean keyReceived[] = new boolean[keyCount];
|
||||
for (int i = 0; i < keyCount; i++) {
|
||||
String key = resultArray.getArray(i).getString(0).substring(3);
|
||||
int idx = Integer.parseInt(key);
|
||||
assertThat(keyReceived[idx]).isFalse();
|
||||
keyReceived[idx] = true;
|
||||
|
||||
if (idx % magicalNumber > 0) {
|
||||
String value = resultArray.getArray(i).getString(1).substring(5);
|
||||
assertThat(key).isEqualTo(value);
|
||||
} else {
|
||||
assertThat(resultArray.getArray(i).isNull(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Test removal in same test, since it's costly to set up the test again.
|
||||
// Remove only odd keys
|
||||
SimpleArray keyRemoves = new SimpleArray();
|
||||
for (int i = 0; i < keyCount; i++) {
|
||||
if (i % 2 > 0) {
|
||||
keyRemoves.pushString("key" + i);
|
||||
}
|
||||
}
|
||||
mStorage.multiRemove(keyRemoves, mock(Callback.class));
|
||||
mStorage.getAllKeys(
|
||||
new Callback() {
|
||||
@Override
|
||||
public void invoke(Object... args) {
|
||||
SimpleArray resultArray = (SimpleArray) args[1];
|
||||
assertThat(resultArray.size()).isEqualTo(499);
|
||||
for (int i = 0; i < resultArray.size(); i++) {
|
||||
String key = resultArray.getString(i).substring(3);
|
||||
int idx = Integer.parseInt(key);
|
||||
assertThat(idx % 2).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static JSONArray createJSONArray(Object... objects) {
|
||||
return new JSONArray(Arrays.asList(objects));
|
||||
}
|
||||
|
||||
private static JSONObject createJSONObject(Object... keysAndValues) {
|
||||
if (keysAndValues.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("You must provide the same number of keys and values");
|
||||
}
|
||||
Map map = new HashMap();
|
||||
for (int i = 0; i < keysAndValues.length; i += 2) {
|
||||
map.put(keysAndValues[i], keysAndValues[i + 1]);
|
||||
}
|
||||
return new JSONObject(map);
|
||||
}
|
||||
|
||||
private SimpleArray getArray(String... values) {
|
||||
SimpleArray array = new SimpleArray();
|
||||
for (String value : values) {
|
||||
array.pushString(value);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.timing;
|
||||
|
||||
import android.view.Choreographer;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.CatalystInstance;
|
||||
import com.facebook.react.bridge.SimpleArray;
|
||||
import com.facebook.react.uimanager.ReactChoreographer;
|
||||
import com.facebook.react.common.SystemClock;
|
||||
import com.facebook.react.modules.core.JSTimersExecution;
|
||||
import com.facebook.react.modules.core.Timing;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.modules.junit4.rule.PowerMockRule;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link Timing}.
|
||||
*/
|
||||
@PrepareForTest({Arguments.class, SystemClock.class, ReactChoreographer.class})
|
||||
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class TimingModuleTest {
|
||||
|
||||
private static final long FRAME_TIME_NS = 17 * 1000 * 1000; // 17 ms
|
||||
|
||||
private Timing mTiming;
|
||||
private ReactChoreographer mChoreographerMock;
|
||||
private PostFrameCallbackHandler mPostFrameCallbackHandler;
|
||||
private long mCurrentTimeNs;
|
||||
private JSTimersExecution mJSTimersMock;
|
||||
|
||||
@Rule
|
||||
public PowerMockRule rule = new PowerMockRule();
|
||||
|
||||
@Before
|
||||
public void prepareModules() {
|
||||
PowerMockito.mockStatic(Arguments.class);
|
||||
when(Arguments.createArray()).thenAnswer(
|
||||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return new SimpleArray();
|
||||
}
|
||||
});
|
||||
|
||||
PowerMockito.mockStatic(SystemClock.class);
|
||||
when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
|
||||
when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs);
|
||||
|
||||
mChoreographerMock = mock(ReactChoreographer.class);
|
||||
PowerMockito.mockStatic(ReactChoreographer.class);
|
||||
when(ReactChoreographer.getInstance()).thenReturn(mChoreographerMock);
|
||||
|
||||
CatalystInstance catalystInstance = mock(CatalystInstance.class);
|
||||
ReactApplicationContext reactContext = mock(ReactApplicationContext.class);
|
||||
when(reactContext.getCatalystInstance()).thenReturn(catalystInstance);
|
||||
|
||||
mCurrentTimeNs = 0;
|
||||
mPostFrameCallbackHandler = new PostFrameCallbackHandler();
|
||||
|
||||
doAnswer(mPostFrameCallbackHandler)
|
||||
.when(mChoreographerMock)
|
||||
.postFrameCallback(
|
||||
eq(ReactChoreographer.CallbackType.TIMERS_EVENTS),
|
||||
any(Choreographer.FrameCallback.class));
|
||||
|
||||
mTiming = new Timing(reactContext);
|
||||
mJSTimersMock = mock(JSTimersExecution.class);
|
||||
when(catalystInstance.getJSModule(JSTimersExecution.class)).thenReturn(mJSTimersMock);
|
||||
mTiming.initialize();
|
||||
}
|
||||
|
||||
private void stepChoreographerFrame() {
|
||||
Choreographer.FrameCallback callback = mPostFrameCallbackHandler.getAndResetFrameCallback();
|
||||
mCurrentTimeNs += FRAME_TIME_NS;
|
||||
if (callback != null) {
|
||||
callback.doFrame(mCurrentTimeNs);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleTimer() {
|
||||
mTiming.onHostResume();
|
||||
mTiming.createTimer(1, 0, 0, false);
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(1));
|
||||
reset(mJSTimersMock);
|
||||
stepChoreographerFrame();
|
||||
verifyNoMoreInteractions(mJSTimersMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRecurringTimer() {
|
||||
mTiming.createTimer(100, 0, 0, true);
|
||||
mTiming.onHostResume();
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(100));
|
||||
|
||||
reset(mJSTimersMock);
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelRecurringTimer() {
|
||||
mTiming.onHostResume();
|
||||
mTiming.createTimer(105, 0, 0, true);
|
||||
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(105));
|
||||
|
||||
reset(mJSTimersMock);
|
||||
mTiming.deleteTimer(105);
|
||||
stepChoreographerFrame();
|
||||
verifyNoMoreInteractions(mJSTimersMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPausingAndResuming() {
|
||||
mTiming.onHostResume();
|
||||
mTiming.createTimer(41, 0, 0, true);
|
||||
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(41));
|
||||
|
||||
reset(mJSTimersMock);
|
||||
mTiming.onHostPause();
|
||||
stepChoreographerFrame();
|
||||
verifyNoMoreInteractions(mJSTimersMock);
|
||||
|
||||
reset(mJSTimersMock);
|
||||
mTiming.onHostResume();
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callTimers(SimpleArray.of(41));
|
||||
}
|
||||
|
||||
private static class PostFrameCallbackHandler implements Answer<Void> {
|
||||
|
||||
private Choreographer.FrameCallback mFrameCallback;
|
||||
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
Object[] args = invocation.getArguments();
|
||||
mFrameCallback = (Choreographer.FrameCallback) args[1];
|
||||
return null;
|
||||
}
|
||||
|
||||
public Choreographer.FrameCallback getAndResetFrameCallback() {
|
||||
Choreographer.FrameCallback callback = mFrameCallback;
|
||||
mFrameCallback = null;
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user