// Copyright 2016 Google, Inc.
//
// 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 com.firebase.jobdispatcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import com.firebase.jobdispatcher.Job.Builder;
import com.google.android.gms.gcm.INetworkTaskCallback;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Provides common utilities helpful for testing.
*/
public class TestUtil {
private static final String[] TAG_COMBINATIONS = {"tag", "foobar", "fooooooo", "bz", "100"};
private static final int[] LIFETIME_COMBINATIONS = {
Lifetime.UNTIL_NEXT_BOOT,
Lifetime.FOREVER};
private static final JobTrigger[] TRIGGER_COMBINATIONS = {
Trigger.executionWindow(0, 30),
Trigger.executionWindow(300, 600),
Trigger.executionWindow(86400, 86400 * 2),
Trigger.NOW
};
@SuppressWarnings("unchecked")
private static final List<Class<TestJobService>> SERVICE_COMBINATIONS =
Arrays.asList(TestJobService.class);
private static final RetryStrategy[] RETRY_STRATEGY_COMBINATIONS = {
RetryStrategy.DEFAULT_LINEAR,
new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 60, 300),
RetryStrategy.DEFAULT_EXPONENTIAL,
new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 300, 600),
};
public static void assertHasSinglePrivateConstructor(Class<?> cls) throws Exception {
Constructor<?>[] constructors = cls.getDeclaredConstructors();
assertEquals("expected number of constructors to be == 1", 1, constructors.length);
Constructor<?> constructor = constructors[0];
assertFalse("expected constructor to be inaccessible", constructor.isAccessible());
constructor.setAccessible(true);
constructor.newInstance();
}
static List<List<Integer>> getAllConstraintCombinations() {
List<List<Integer>> combos = new LinkedList<>();
combos.add(Collections.<Integer>emptyList());
for (Integer cur : Constraint.ALL_CONSTRAINTS) {
for (int l = combos.size() - 1; l >= 0; l--) {
List<Integer> oldCombo = combos.get(l);
List<Integer> newCombo = Arrays.asList(new Integer[oldCombo.size() + 1]);
Collections.copy(newCombo, oldCombo);
newCombo.set(oldCombo.size(), cur);
combos.add(newCombo);
}
combos.add(Collections.singletonList(cur));
}
return combos;
}
static int[] toIntArray(List<Integer> list) {
int[] input = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
input[i] = list.get(i);
}
return input;
}
static List<Job> getJobCombinations(Builder builder) {
return getCombination(new JobBuilder(builder));
}
static List<JobInvocation> getJobInvocationCombinations() {
return getCombination(new JobInvocationBuilder());
}
private static <T extends JobParameters> List<T> getCombination(
JobParameterBuilder<T> buildJobParam) {
List<T> result = new ArrayList<>();
for (String tag : TAG_COMBINATIONS) {
for (List<Integer> constraintList : getAllConstraintCombinations()) {
for (boolean recurring : new boolean[]{true, false}) {
for (boolean replaceCurrent : new boolean[]{true, false}) {
for (int lifetime : LIFETIME_COMBINATIONS) {
for (JobTrigger trigger : TRIGGER_COMBINATIONS) {
for (Class<TestJobService> service : SERVICE_COMBINATIONS) {
for (Bundle extras : getBundleCombinations()) {
for (RetryStrategy rs : RETRY_STRATEGY_COMBINATIONS) {
result.add(buildJobParam.build(
tag,
replaceCurrent,
constraintList,
recurring,
lifetime,
trigger,
service,
extras,
rs));
}
}
}
}
}
}
}
}
}
return result;
}
private static Bundle[] getBundleCombinations() {
List<Bundle> bundles = new LinkedList<>();
bundles.add(new Bundle());
Bundle b = new Bundle();
b.putString("foo", "bar");
b.putInt("bar", 1);
b.putLong("baz", 3L);
bundles.add(b);
return bundles.toArray(new Bundle[bundles.size()]);
}
static void assertJobsEqual(JobParameters input, JobParameters output) {
assertNotNull("input", input);
assertNotNull("output", output);
assertEquals("isRecurring()", input.isRecurring(), output.isRecurring());
assertEquals("shouldReplaceCurrent()", input.shouldReplaceCurrent(), output.shouldReplaceCurrent());
assertEquals("getLifetime()", input.getLifetime(), output.getLifetime());
assertEquals("getTag()", input.getTag(), output.getTag());
assertEquals("getService()", input.getService(), output.getService());
assertEquals("getConstraints()",
Constraint.compact(input.getConstraints()),
Constraint.compact(output.getConstraints()));
assertTriggersEqual(input.getTrigger(), output.getTrigger());
assertBundlesEqual(input.getExtras(), output.getExtras());
assertRetryStrategiesEqual(input.getRetryStrategy(), output.getRetryStrategy());
}
static void assertRetryStrategiesEqual(RetryStrategy in, RetryStrategy out) {
String prefix = "getRetryStrategy().";
assertEquals(prefix + "getPolicy()",
in.getPolicy(), out.getPolicy());
assertEquals(prefix + "getInitialBackoff()",
in.getInitialBackoff(), out.getInitialBackoff());
assertEquals(prefix + "getMaximumBackoff()",
in.getMaximumBackoff(), out.getMaximumBackoff());
}
static void assertBundlesEqual(Bundle inExtras, Bundle outExtras) {
if (inExtras == null || outExtras == null) {
assertNull(inExtras);
assertNull(outExtras);
return;
}
assertEquals("getExtras().size()", inExtras.size(), outExtras.size());
final Set<String> inKeys = inExtras.keySet();
for (String key : inKeys) {
assertTrue("getExtras().containsKey(\"" + key + "\")", outExtras.containsKey(key));
assertEquals("getExtras().get(\"" + key + "\")", inExtras.get(key), outExtras.get(key));
}
}
static void assertTriggersEqual(JobTrigger inTrigger, JobTrigger outTrigger) {
assertEquals("", inTrigger.getClass(), outTrigger.getClass());
if (inTrigger instanceof JobTrigger.ExecutionWindowTrigger) {
assertEquals("getTrigger().getWindowStart()",
((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowStart(),
((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowStart());
assertEquals("getTrigger().getWindowEnd()",
((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowEnd(),
((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowEnd());
} else if (inTrigger == Trigger.NOW) {
assertEquals(inTrigger, outTrigger);
} else {
fail("Unknown Trigger class: " + inTrigger.getClass());
}
}
@NonNull
public static Builder getBuilderWithNoopValidator() {
return new Builder(new ValidationEnforcer(new NoopJobValidator()));
}
/**
* Callback for testing.
*/
public static final class NopCallback extends INetworkTaskCallback.Stub {
@Override
public void taskFinished(int result) throws RemoteException {
// nop
}
}
private static class JobInvocationBuilder implements
JobParameterBuilder<JobInvocation> {
@Override
public JobInvocation build(String tag, boolean replaceCurrent, List<Integer> constraintList,
boolean recurring, int lifetime, JobTrigger trigger, Class<TestJobService> service,
Bundle extras, RetryStrategy rs) {
//noinspection WrongConstant
return new JobInvocation.Builder()
.setTag(tag)
.setReplaceCurrent(replaceCurrent)
.setRecurring(recurring)
.setConstraints(toIntArray(constraintList))
.setLifetime(lifetime)
.setTrigger(trigger)
.setService(service.getName())
.addExtras(extras)
.setRetryStrategy(rs)
.build();
}
}
private static class JobBuilder implements JobParameterBuilder<Job> {
private final Builder builder;
public JobBuilder(Builder builder){
this.builder = builder;
}
@Override
public Job build(String tag, boolean replaceCurrent, List<Integer> constraintList,
boolean recurring, int lifetime, JobTrigger trigger, Class<TestJobService> service,
Bundle extras, RetryStrategy rs) {
//noinspection WrongConstant
return builder
.setTag(tag)
.setReplaceCurrent(replaceCurrent)
.setRecurring(recurring)
.setConstraints(toIntArray(constraintList))
.setLifetime(lifetime)
.setTrigger(trigger)
.setService(service)
.setExtras(extras)
.setRetryStrategy(rs)
.build();
}
}
private interface JobParameterBuilder<T extends JobParameters> {
T build(String tag, boolean replaceCurrent, List<Integer> constraintList, boolean recurring,
int lifetime, JobTrigger trigger, Class<TestJobService> service, Bundle extras,
RetryStrategy rs);
}
}