Skip to content

Commit 23cd6a2

Browse files
author
VinayGuthal
authored
Add integration tests (#371)
* add tests * add the remaining tests * idea update * address comments * nit * update tests * gjf * change * update tests * rule * fix race conditino * use locker * fix test * address nits
1 parent 36e728a commit 23cd6a2

File tree

20 files changed

+508
-33
lines changed

20 files changed

+508
-33
lines changed

transport/transport-runtime/src/androidTest/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package="com.google.android.datatransport.runtime.androidTest">
33
<!--Although the *SdkVersion is captured in gradle build files, this is required for non gradle builds-->
44
<!--<uses-sdk android:minSdkVersion="14"/>-->
5+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
56
<application>
67
<service
78
android:name="com.google.android.datatransport.runtime.scheduling.TestService$Local"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.android.datatransport.runtime;
16+
17+
import static org.mockito.ArgumentMatchers.any;
18+
import static org.mockito.ArgumentMatchers.anyInt;
19+
import static org.mockito.ArgumentMatchers.anyString;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.doAnswer;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.times;
24+
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
26+
27+
import android.content.Context;
28+
import android.support.test.InstrumentationRegistry;
29+
import android.support.test.runner.AndroidJUnit4;
30+
import com.google.android.datatransport.Event;
31+
import com.google.android.datatransport.Transport;
32+
import com.google.android.datatransport.TransportFactory;
33+
import com.google.android.datatransport.runtime.backends.BackendRegistry;
34+
import com.google.android.datatransport.runtime.backends.TransportBackend;
35+
import com.google.android.datatransport.runtime.scheduling.jobscheduling.SchedulerConfig;
36+
import com.google.android.datatransport.runtime.scheduling.jobscheduling.Uploader;
37+
import com.google.android.datatransport.runtime.scheduling.locking.Locker;
38+
import java.util.UUID;
39+
import org.junit.Before;
40+
import org.junit.Rule;
41+
import org.junit.Test;
42+
import org.junit.runner.RunWith;
43+
import org.mockito.stubbing.Answer;
44+
45+
@RunWith(AndroidJUnit4.class)
46+
public class SchedulerIntegrationTest {
47+
private static final String TEST_KEY = "test";
48+
private static final String TEST_VALUE = "test-value";
49+
private static final String testTransport = "testTransport";
50+
private final TransportInternal transportInternalMock = mock(TransportInternal.class);
51+
private final TransportBackend mockBackend = mock(TransportBackend.class);
52+
private final TransportBackend mockBackend2 = mock(TransportBackend.class);
53+
private final BackendRegistry mockRegistry = mock(BackendRegistry.class);
54+
private final Context context = InstrumentationRegistry.getInstrumentation().getContext();
55+
private final Uploader mockUploader = mock(Uploader.class);
56+
private final Locker<Boolean> locker = new Locker<>();
57+
58+
@Rule
59+
public final TransportRuntimeRule runtimeRule =
60+
new TransportRuntimeRule(
61+
DaggerTestRuntimeComponent.builder()
62+
.setApplicationContext(context)
63+
.setUploader(mockUploader)
64+
.setBackendRegistry(mockRegistry)
65+
.setSchedulerConfig(
66+
SchedulerConfig.builder()
67+
.setDelta(500)
68+
.setMaxAllowedTime(100000)
69+
.setMaximumDelay(500)
70+
.build())
71+
.setEventClock(() -> 3)
72+
.setUptimeClock(() -> 1)
73+
.build());
74+
75+
@Before
76+
public void setUp() {
77+
when(mockBackend.decorate(any()))
78+
.thenAnswer(
79+
(Answer<EventInternal>)
80+
invocation ->
81+
invocation
82+
.<EventInternal>getArgument(0)
83+
.toBuilder()
84+
.addMetadata(TEST_KEY, TEST_VALUE)
85+
.build());
86+
when(mockBackend2.decorate(any()))
87+
.thenAnswer(
88+
(Answer<EventInternal>)
89+
invocation ->
90+
invocation
91+
.<EventInternal>getArgument(0)
92+
.toBuilder()
93+
.addMetadata(TEST_KEY, TEST_VALUE)
94+
.build());
95+
/*
96+
We need the locker to ensure that the job service is called before we continue our testing.
97+
The await present in the tests wait until the uploader's upload is called so that we know
98+
for sure that the service is already run.
99+
Also we would need to make sure that the runnable is being run. Because in
100+
JobInfoSchedulerService if we don't communicate that the job is complete. Then the
101+
JobScheduler will try to reschedule the service and will cause an exception for the retry
102+
though the tests are going to pass. In tests testing multiple uploader calls not running the
103+
runnable is going to cause significant delays.
104+
*/
105+
doAnswer(
106+
(Answer<Void>)
107+
i -> {
108+
locker.setResult(true);
109+
i.<Runnable>getArgument(2).run();
110+
return null;
111+
})
112+
.when(mockUploader)
113+
.upload(anyString(), anyInt(), any());
114+
}
115+
116+
private String generateBackendName() {
117+
return UUID.randomUUID().toString().replace("-", "");
118+
}
119+
120+
@Test
121+
public void scheduler_whenEventScheduledForFirstTime_shouldUpload() {
122+
TransportRuntime runtime = TransportRuntime.getInstance();
123+
String mockBackendName = generateBackendName();
124+
when(mockRegistry.get(mockBackendName)).thenReturn(mockBackend);
125+
TransportFactory factory = runtime.newFactory(mockBackendName);
126+
Transport<String> transport =
127+
factory.getTransport(testTransport, String.class, String::getBytes);
128+
Event<String> stringEvent = Event.ofTelemetry("TelemetryData");
129+
EventInternal expectedEvent =
130+
EventInternal.builder()
131+
.setEventMillis(3)
132+
.setUptimeMillis(1)
133+
.setTransportName(testTransport)
134+
.setPayload("TelemetryData".getBytes())
135+
.build();
136+
transport.send(stringEvent);
137+
verify(mockBackend, times(1)).decorate(eq(expectedEvent));
138+
locker.await();
139+
verify(mockUploader, times(1)).upload(eq(mockBackendName), eq(1), any());
140+
}
141+
142+
@Test
143+
public void scheduler_whenEventsScheduledWithSameBackend_shouldUploadOnce() {
144+
TransportRuntime runtime = TransportRuntime.getInstance();
145+
String mockBackendName = generateBackendName();
146+
when(mockRegistry.get(mockBackendName)).thenReturn(mockBackend);
147+
TransportFactory factory = runtime.newFactory(mockBackendName);
148+
Transport<String> transport =
149+
factory.getTransport(testTransport, String.class, String::getBytes);
150+
Event<String> stringEvent = Event.ofTelemetry("TelemetryData");
151+
EventInternal expectedEvent =
152+
EventInternal.builder()
153+
.setEventMillis(3)
154+
.setUptimeMillis(1)
155+
.setTransportName(testTransport)
156+
.setPayload("TelemetryData".getBytes())
157+
.build();
158+
Event<String> stringEvent2 = Event.ofTelemetry("TelemetryData2");
159+
EventInternal expectedEvent2 =
160+
EventInternal.builder()
161+
.setEventMillis(3)
162+
.setUptimeMillis(1)
163+
.setTransportName(testTransport)
164+
.setPayload("TelemetryData2".getBytes())
165+
.build();
166+
transport.send(stringEvent);
167+
transport.send(stringEvent2);
168+
verify(mockBackend, times(1)).decorate(eq(expectedEvent));
169+
verify(mockBackend, times(1)).decorate(eq(expectedEvent2));
170+
locker.await();
171+
verify(mockUploader, times(1)).upload(eq(mockBackendName), eq(1), any());
172+
}
173+
174+
@Test
175+
public void scheduler_whenEventsScheduledWithDifferentBackends_shouldUploadTwice() {
176+
TransportRuntime runtime = TransportRuntime.getInstance();
177+
String firstBackendName = generateBackendName();
178+
String secondBackendName = generateBackendName();
179+
when(mockRegistry.get(firstBackendName)).thenReturn(mockBackend);
180+
when(mockRegistry.get(secondBackendName)).thenReturn(mockBackend2);
181+
TransportFactory factory = runtime.newFactory(firstBackendName);
182+
Transport<String> transport =
183+
factory.getTransport(testTransport, String.class, String::getBytes);
184+
Event<String> stringEvent = Event.ofTelemetry("TelemetryData");
185+
EventInternal expectedEvent =
186+
EventInternal.builder()
187+
.setEventMillis(3)
188+
.setUptimeMillis(1)
189+
.setTransportName(testTransport)
190+
.setPayload("TelemetryData".getBytes())
191+
.build();
192+
transport.send(stringEvent);
193+
TransportFactory factory2 = runtime.newFactory(secondBackendName);
194+
Transport<String> transport2 =
195+
factory2.getTransport(testTransport, String.class, String::getBytes);
196+
transport2.send(stringEvent);
197+
verify(mockBackend, times(1)).decorate(eq(expectedEvent));
198+
verify(mockBackend2, times(1)).decorate(eq(expectedEvent));
199+
locker.await();
200+
locker.await();
201+
verify(mockUploader, times(1)).upload(eq(firstBackendName), eq(1), any());
202+
verify(mockUploader, times(1)).upload(eq(secondBackendName), eq(1), any());
203+
}
204+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.android.datatransport.runtime;
16+
17+
import dagger.Module;
18+
import dagger.Provides;
19+
import java.util.concurrent.Executor;
20+
import javax.inject.Singleton;
21+
22+
@Module
23+
abstract class TestExecutionModule {
24+
@Singleton
25+
@Provides
26+
static Executor executor() {
27+
return Runnable::run;
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.android.datatransport.runtime;
16+
17+
import android.content.Context;
18+
import com.google.android.datatransport.runtime.backends.BackendRegistry;
19+
import com.google.android.datatransport.runtime.scheduling.SchedulingModule;
20+
import com.google.android.datatransport.runtime.scheduling.jobscheduling.SchedulerConfig;
21+
import com.google.android.datatransport.runtime.scheduling.jobscheduling.Uploader;
22+
import com.google.android.datatransport.runtime.scheduling.persistence.EventStoreModule;
23+
import com.google.android.datatransport.runtime.time.Clock;
24+
import com.google.android.datatransport.runtime.time.Monotonic;
25+
import com.google.android.datatransport.runtime.time.WallTime;
26+
import dagger.BindsInstance;
27+
import dagger.Component;
28+
import javax.inject.Singleton;
29+
30+
@Component(
31+
modules = {
32+
EventStoreModule.class,
33+
TestExecutionModule.class,
34+
SchedulingModule.class,
35+
})
36+
@Singleton
37+
abstract class TestRuntimeComponent extends TransportRuntimeComponent {
38+
39+
abstract TransportRuntime getTransportRuntime();
40+
41+
@Component.Builder
42+
interface Builder {
43+
@BindsInstance
44+
Builder setApplicationContext(Context applicationContext);
45+
46+
@BindsInstance
47+
Builder setBackendRegistry(BackendRegistry registry);
48+
49+
@BindsInstance
50+
Builder setUploader(Uploader uploader);
51+
52+
@BindsInstance
53+
Builder setSchedulerConfig(SchedulerConfig config);
54+
55+
@BindsInstance
56+
Builder setUptimeClock(@Monotonic Clock clock);
57+
58+
@BindsInstance
59+
Builder setEventClock(@WallTime Clock clock);
60+
61+
TestRuntimeComponent build();
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.android.datatransport.runtime;
16+
17+
import org.junit.rules.TestRule;
18+
import org.junit.runner.Description;
19+
import org.junit.runners.model.Statement;
20+
21+
public final class TransportRuntimeRule implements TestRule {
22+
private TransportRuntimeComponent component;
23+
24+
TransportRuntimeRule(TransportRuntimeComponent component) {
25+
this.component = component;
26+
}
27+
28+
@Override
29+
public Statement apply(final Statement base, final Description description) {
30+
return new Statement() {
31+
@Override
32+
public void evaluate() throws Throwable {
33+
TransportRuntime.withInstance(
34+
component,
35+
() -> {
36+
try {
37+
base.evaluate();
38+
} catch (RuntimeException e) {
39+
throw e;
40+
} catch (Exception e) {
41+
throw e;
42+
} catch (Throwable throwable) {
43+
throw new Exception(throwable);
44+
} finally {
45+
component.close();
46+
}
47+
return null;
48+
});
49+
}
50+
};
51+
}
52+
}

transport/transport-runtime/src/androidTest/java/com/google/android/datatransport/runtime/scheduling/RemoteLockRpc.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import android.os.Process;
1818
import com.google.android.datatransport.runtime.ITestRemoteLockRpc;
19+
import com.google.android.datatransport.runtime.scheduling.locking.Locker;
1920
import com.google.android.datatransport.runtime.synchronization.SynchronizationException;
2021
import com.google.android.datatransport.runtime.synchronization.SynchronizationGuard;
2122
import java.util.concurrent.Executor;

0 commit comments

Comments
 (0)