/*
* Copyright 2015 Fabio Collini.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.cosenonjaviste.demomv2m.core;
import android.os.BadParcelableException;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.google.gson.Gson;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyByte;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
public class ParcelableTester {
public static <T extends Parcelable> void check(T parcelable, Parcelable.Creator<T> creator) {
Parcel parcel = createParcel();
parcelable.writeToParcel(parcel, 0);
T loadedData = creator.createFromParcel(parcel);
Gson gson = new Gson();
String s1 = gson.toJson(parcelable);
String s2 = gson.toJson(loadedData);
assertThat(s2).isEqualTo(s1);
assertThat(parcelable.describeContents()).isEqualTo(0);
assertThat(creator.newArray(2)).hasSize(2);
}
@NonNull public static Parcel createParcel() {
Parcel parcel = Mockito.mock(Parcel.class);
final LinkedList<Object> parcelData = new LinkedList<>();
Answer writeAnswer = new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
parcelData.add(invocation.getArguments()[0]);
return null;
}
};
Answer<Object> readAnswer = new Answer<Object>() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
return parcelData.removeFirst();
}
};
doAnswer(writeAnswer).when(parcel).writeInt(anyInt());
when(parcel.readInt()).thenAnswer(readAnswer);
doAnswer(writeAnswer).when(parcel).writeLong(anyLong());
when(parcel.readLong()).thenAnswer(readAnswer);
doAnswer(writeAnswer).when(parcel).writeString(anyString());
when(parcel.readString()).thenAnswer(readAnswer);
doAnswer(writeAnswer).when(parcel).writeByte(anyByte());
when(parcel.readByte()).thenAnswer(readAnswer);
doAnswer(new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
List<Parcelable> list = (List<Parcelable>) invocation.getArguments()[0];
writeList(parcelData, invocation, list);
return null;
}
}).when(parcel).writeList(anyList());
doAnswer(new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
List<Parcelable> list = (List<Parcelable>) invocation.getArguments()[0];
readList(parcelData, invocation, list);
return null;
}
}).when(parcel).readList(anyList(), any(ClassLoader.class));
doAnswer(new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
Parcelable[] parcelables = (Parcelable[]) invocation.getArguments()[0];
writeList(parcelData, invocation, parcelables == null ? null : Arrays.asList(parcelables));
return null;
}
}).when(parcel).writeParcelableArray(any(Parcelable[].class), anyInt());
doAnswer(new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
List<Parcelable> list = new ArrayList<>();
readList(parcelData, invocation, list);
return list.toArray(new Parcelable[list.size()]);
}
}).when(parcel).readParcelableArray(any(ClassLoader.class));
doAnswer(new Answer() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
Parcelable parcelable = (Parcelable) invocation.getArguments()[0];
writeParcelable(parcelData, parcelable, (Parcel) invocation.getMock());
return null;
}
}).when(parcel).writeParcelable(any(Parcelable.class), anyInt());
when(parcel.readParcelable(any(ClassLoader.class))).thenAnswer(new Answer<Object>() {
@Override public Object answer(InvocationOnMock invocation) throws Throwable {
return readParcelable(parcelData, (Parcel) invocation.getMock());
}
});
return parcel;
}
private static void readList(LinkedList<Object> parcelData, InvocationOnMock invocation, List<Parcelable> list) {
int size = (int) parcelData.removeFirst();
if (size > 0) {
for (int i = 0; i < size; i++) {
list.add(readParcelable(parcelData, (Parcel) invocation.getMock()));
}
}
}
private static void writeList(LinkedList<Object> parcelData, InvocationOnMock invocation, List<Parcelable> list) {
if (list == null) {
parcelData.add(-1);
} else {
parcelData.add(list.size());
for (Parcelable item : list) {
writeParcelable(parcelData, item, (Parcel) invocation.getMock());
}
}
}
private static Parcelable readParcelable(LinkedList<Object> parcelData, Parcel parcel1) {
Class<? extends Parcelable> parcelableClass = (Class<? extends Parcelable>) parcelData.removeFirst();
if (parcelableClass == null) {
return null;
} else {
Parcelable.Creator<Parcelable> parcelableCreator = readParcelableCreator(parcelableClass);
parcelableCreator.newArray(2);
return parcelableCreator.createFromParcel(parcel1);
}
}
private static void writeParcelable(LinkedList<Object> parcelData, Parcelable parcelable, Parcel parcel1) {
if (parcelable == null) {
parcelData.add(null);
} else {
parcelable.describeContents();
parcelData.add(parcelable.getClass());
parcelable.writeToParcel(parcel1, 0);
}
}
private static <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator(Class c) {
Parcelable.Creator<T> creator;
try {
Field f = c.getField("CREATOR");
creator = (Parcelable.Creator) f.get(null);
} catch (IllegalAccessException e) {
throw new BadParcelableException(
"IllegalAccessException when unmarshalling: " + c.getName());
} catch (ClassCastException e) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ " CREATOR on class " + c.getName());
} catch (NoSuchFieldException e) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ " CREATOR on class " + c.getName());
} catch (NullPointerException e) {
throw new BadParcelableException("Parcelable protocol requires "
+ "the CREATOR object to be static on class " + c.getName());
}
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ " CREATOR on class " + c.getName());
}
return creator;
}
}