Skip to content

Commit

Permalink
Add support for specifying mock data size
Browse files Browse the repository at this point in the history
  • Loading branch information
Airsaid committed Sep 9, 2021
1 parent 848a132 commit 3d149fa
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,120 +15,120 @@ public class ArrayMockDataProvider {
@Mock
public boolean[] booleanArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public boolean[][] booleanArrayDimension;

@Mock
public Boolean[] booleanWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Boolean[][] booleanWrapArrayDimension;

@Mock
public char[] charArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public char[][] charArrayDimension;

@Mock
public Character[] charWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Character[][] charWrapArrayDimension;

@Mock
public byte[] byteArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public byte[][] byteArrayDimension;

@Mock
public Byte[] byteWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Byte[][] byteWrapArrayDimension;

@Mock
public short[] shortArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public short[][] shortArrayDimension;

@Mock
public Short[] shortWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Short[][] shortWrapArrayDimension;

@Mock
public int[] intArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public int[][] intArrayDimension;

@Mock
public Integer[] intWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Integer[][] intWrapArrayDimension;

@Mock
public float[] floatArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public float[][] floatArrayDimension;

@Mock
public Float[] floatWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Float[][] floatWrapArrayDimension;

@Mock
public long[] longArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public long[][] longArrayDimension;

@Mock
public Long[] longWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Long[][] longWrapArrayDimension;

@Mock
public double[] doubleArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public double[][] doubleArrayDimension;

@Mock
public Double[] doubleWrapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Double[][] doubleWrapArrayDimension;

@Mock
public Person[] personArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Person[][] personArrayDimension;

@Mock
public Group[] groupArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Group[][] groupArrayDimension;

@Mock
@Mock(randomSizeRange = {1, 10})
public List<Person>[] listArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public List<Person>[][] listArrayDimension;

@Mock
@Mock(randomSizeRange = {1, 10})
public Map<Integer, Person>[] mapArray;

@Mock
@Mock(randomSizeRange = {1, 10})
public Map<Integer, Person>[][] mapArrayDimension;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import com.airsaid.okmock.data.Color
*/
class EnumDataProvider {

@Mock
@Mock(randomSizeRange = [2, 2])
lateinit var color: Color

@Mock
@Mock(randomSizeRange = [10, 20])
lateinit var colorArray: Array<Color>

@Mock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ package com.airsaid.okmock.plugin.constant
object Constants {
const val OK_MOCK_CLASS_NAME = "com/airsaid/okmock/OKMock"

const val OK_MOCK_CONFIG_NAME = "com/airsaid/okmock/OKMockConfig"

const val GET_MOCK_DATA_METHOD_NAME = "getMockData"

const val GET_MOCK_DATA_METHOD_DESCRIPTOR = "(Ljava/lang/String;)Ljava/lang/Object;"
const val GET_MOCK_DATA_METHOD_DESCRIPTOR = "(Ljava/lang/String;Lcom/airsaid/okmock/OKMockConfig;)Ljava/lang/Object;"

const val RANDOM_SIZE_RANGE_NAME = "randomSizeRange"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,41 @@ package com.airsaid.okmock.plugin.model
* generic types.
* @param value the field's value. This parameter, which may be null if the
* field does not have an value.
* @param randomSizeRange the parameter of the [com.airsaid.okmock.OKMock] annotation on the field,
* used to specify random size range for mock data. default size range is 1 until 50.
*
* @author airsaid
*/
data class FieldInfo(
val owner: String, val access: Int, val name: String,
val descriptor: String, val signature: String?, val value: Any?
)
val descriptor: String, val signature: String?, val value: Any?,
val randomSizeRange: IntArray = intArrayOf(1, 50)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as FieldInfo

if (owner != other.owner) return false
if (access != other.access) return false
if (name != other.name) return false
if (descriptor != other.descriptor) return false
if (signature != other.signature) return false
if (value != other.value) return false
if (!randomSizeRange.contentEquals(other.randomSizeRange)) return false

return true
}

override fun hashCode(): Int {
var result = owner.hashCode()
result = 31 * result + access
result = 31 * result + name.hashCode()
result = 31 * result + descriptor.hashCode()
result = 31 * result + (signature?.hashCode() ?: 0)
result = 31 * result + (value?.hashCode() ?: 0)
result = 31 * result + randomSizeRange.contentHashCode()
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import com.airsaid.okmock.plugin.OKMockPlugin
import com.airsaid.okmock.plugin.constant.Constants.GET_MOCK_DATA_METHOD_DESCRIPTOR
import com.airsaid.okmock.plugin.constant.Constants.GET_MOCK_DATA_METHOD_NAME
import com.airsaid.okmock.plugin.constant.Constants.OK_MOCK_CLASS_NAME
import com.airsaid.okmock.plugin.constant.Constants.OK_MOCK_CONFIG_NAME
import com.airsaid.okmock.plugin.constant.Constants.RANDOM_SIZE_RANGE_NAME
import com.airsaid.okmock.plugin.extension.OKMockExtension
import com.airsaid.okmock.plugin.model.FieldInfo
import com.airsaid.okmock.plugin.util.isPrimitiveType
import com.airsaid.okmock.plugin.util.toPrimitiveWrapType
import com.airsaid.okmock.plugin.util.visitIntValue
import com.android.build.api.transform.TransformInvocation
import org.gradle.api.Project
import org.objectweb.asm.*
Expand Down Expand Up @@ -106,12 +109,33 @@ class OKMockTransform(project: Project, extension: OKMockExtension) : AbstractTr
fv: FieldVisitor
) : FieldVisitor(ASM9, fv) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
annotations.forEach { annotation ->
if (Type.getDescriptor(annotation) == descriptor) {
results.add(field)
val av = super.visitAnnotation(descriptor, visible)
if (av != null) {
annotations.forEach { annotation ->
if (Type.getDescriptor(annotation) == descriptor) {
results.add(field)
return AnnotationParamsAdapter(field, av)
}
}
}
return super.visitAnnotation(descriptor, visible)
return av
}

private class AnnotationParamsAdapter(
private val field: FieldInfo,
annotationVisitor: AnnotationVisitor?
) : AnnotationVisitor(ASM9, annotationVisitor) {
override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor {
return super.visitAnnotation(name, descriptor)
}

override fun visit(name: String?, value: Any?) {
if (name == RANDOM_SIZE_RANGE_NAME && value is IntArray) {
field.randomSizeRange[0] = value[0]
field.randomSizeRange[1] = value[1]
}
super.visit(name, value)
}
}
}

Expand Down Expand Up @@ -139,6 +163,19 @@ class OKMockTransform(project: Project, extension: OKMockExtension) : AbstractTr
mv.visitVarInsn(ALOAD, 0)
}
mv.visitLdcInsn(getMethodParams(fieldInfo))
mv.visitTypeInsn(NEW, OK_MOCK_CONFIG_NAME)
mv.visitInsn(DUP)
mv.visitInsn(ICONST_2)
mv.visitIntInsn(NEWARRAY, T_INT)
mv.visitInsn(DUP)
mv.visitInsn(ICONST_0)
mv.visitIntValue(fieldInfo.randomSizeRange[0])
mv.visitInsn(IASTORE)
mv.visitInsn(DUP)
mv.visitInsn(ICONST_1)
mv.visitIntValue(fieldInfo.randomSizeRange[1])
mv.visitInsn(IASTORE)
mv.visitMethodInsn(INVOKESPECIAL, OK_MOCK_CONFIG_NAME, "<init>", "([I)V", false)
mv.visitMethodInsn(
INVOKESTATIC, OK_MOCK_CLASS_NAME,
GET_MOCK_DATA_METHOD_NAME, GET_MOCK_DATA_METHOD_DESCRIPTOR, false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type

fun Project.getExtension(): BaseExtension {
Expand Down Expand Up @@ -106,3 +108,15 @@ fun Type.toPrimitiveWrapType(): Type {
}
throw IllegalArgumentException("$this is not a primitive type.")
}

fun MethodVisitor.visitIntValue(value: Int) {
if (value >= -1 && value <= 5) {
visitInsn(value + 3)
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
visitIntInsn(Opcodes.BIPUSH, value)
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
visitIntInsn(Opcodes.SIPUSH, value)
} else {
visitLdcInsn(value)
}
}
13 changes: 6 additions & 7 deletions okmock/src/main/java/com/airsaid/okmock/ArrayUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@
class ArrayUtils {

/**
* Creates a new array with the specified component type and random length range.
* Creates a new array with the specified component type and size.
*
* @param componentType the {@code Class} object representing the component type of the new array.
* @param dimension dimension of the array.
* @param startInclusive the start size of the random length of the array.
* @param endExclusive the end size of the random length of the array.
* @param componentType the {@code Class} object representing the component type of the new array.
* @param dimension dimension of the array.
* @param size default size of the array.
* @return the array object.
*/
public static Object getArray(Class<?> componentType, int dimension, int startInclusive, int endExclusive) {
public static Object getArray(Class<?> componentType, int dimension, int size) {
int[] arrayDimensions = new int[dimension];
for (int i = 0; i < dimension; i++) {
arrayDimensions[i] = RandomDataProvider.nextInt(startInclusive, endExclusive);
arrayDimensions[i] = size;
}
return Array.newInstance(componentType, arrayDimensions);
}
Expand Down
32 changes: 28 additions & 4 deletions okmock/src/main/java/com/airsaid/okmock/OKMock.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@
import com.airsaid.okmock.api.MockValue;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -43,6 +41,32 @@
*/
public class OKMock {

private static final OKMockConfig DEFAULT_CONFIG = new OKMockConfig(new int[]{1, 50});

private static OKMockConfig sConfig = DEFAULT_CONFIG;

/**
* Returns the corresponding mock object through the specified type signature and config object.
* <p>
* For example, if {@code signature} is {@code Z} descriptor, then return {@link Boolean} mock object.
* if {@code signature} is an {@code [I} descriptor, then return {@link Integer} array mock object.
* if {@code signature} is an {@code Ljava/lang/String;} descriptor, then return {@link String} mock object.
* if {@code signature} is an {@code [Ljava/lang/Boolean;} descriptor, then return {@link Boolean} array mock object.
* if {@code signature} is an {@code Ljava/util/List<Ljava/lang/String;>;} signature,
* then return {@link List} mock object, and contains random {@link String} data.
* <p>
* The data is completely random by default, If you need custom data, you can view the {@link MockValue} annotation.
*
* @param signature the type descriptor or signature.
* @param config the configuration object for generate mock data.
* @return the mock object corresponding to the given type descriptor or signature.
* @see MockValue
*/
public static Object getMockData(String signature, OKMockConfig config) {
sConfig = config;
return getMockData(signature);
}

/**
* Returns the corresponding mock object through the specified type signature.
* <p>
Expand Down Expand Up @@ -110,7 +134,7 @@ private static Object getInstanceRecursively(List<String> formatSignatures, int
Class<?> sourceClass = TypeUtils.getClass(descriptor);
boolean isArray = Objects.requireNonNull(sourceClass).isArray();
Class<?> componentType = TypeUtils.getOriginalComponentType(sourceClass);
Object array = isArray ? ArrayUtils.getArray(componentType, ArrayUtils.getArrayDimension(sourceClass), 1, 50) : null;
Object array = isArray ? ArrayUtils.getArray(componentType, ArrayUtils.getArrayDimension(sourceClass), sConfig.getSize()) : null;

if (Boolean.class.isAssignableFrom(componentType) || Boolean.TYPE == componentType) {
return isArray ? RandomDataProvider.getRandomBooleanArray(array) : RandomDataProvider.getRandomBoolean();
Expand Down Expand Up @@ -220,7 +244,7 @@ private static Map<Object, Object> getMapInstance(Class<?> clazz) {
}

private static void randomForEach(IntConsumer consumer) {
for (int i = 0; i <= RandomDataProvider.nextInt(1, 50); i++) {
for (int i = 0; i <= sConfig.getSize(); i++) {
consumer.accept(i);
}
}
Expand Down
Loading

0 comments on commit 3d149fa

Please sign in to comment.