1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.art.cts.host;
18 
19 import static org.junit.Assert.assertNotEquals;
20 import static org.junit.Assert.fail;
21 
22 import com.android.tradefed.device.ITestDevice;
23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
24 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
25 import com.android.tradefed.util.CommandResult;
26 
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.nio.charset.StandardCharsets;
33 import java.time.Duration;
34 import java.util.concurrent.TimeUnit;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 
38 @RunWith(DeviceJUnit4ClassRunner.class)
39 public final class ThreadLocalRandomTest extends BaseHostJUnit4Test {
40 
41     private static final String THREAD_LOCAL_APP_APK = "CtsThreadLocalRandomApp.apk";
42     private static final Duration MAX_TIMEOUT_FOR_COMMAND = Duration.ofMinutes(2);
43 
44     private static final String NEXT_RANDOM_COMMAND =
45             String.format("content call --uri content://%s --method %s", "tlrapp", "next_random");
46 
47     private static final String KILL_APP_COMMAND = "am force-stop com.android.art.cts.tlr_app";
48 
49     private ITestDevice mDevice;
50 
51     @Before
setUp()52     public void setUp() throws Exception {
53         mDevice = getDevice();
54         installPackage(THREAD_LOCAL_APP_APK);
55     }
56 
57     @Test
nextLongAfterRestartShouldReturnDifferentValue()58     public void nextLongAfterRestartShouldReturnDifferentValue() throws Exception {
59         long firstCallResult = getNextLong();
60 
61         killApp();
62 
63         long secondCallResult = getNextLong();
64 
65         assertNotEquals("Both calls returned the same value", firstCallResult, secondCallResult);
66     }
67 
68     private static final Pattern PATTERN =
69             Pattern.compile(".*random=(-?\\d+).*", Pattern.DOTALL);
70 
getNextLong()71     private long getNextLong() throws Exception {
72         return getLongFromResultBundle(execute(NEXT_RANDOM_COMMAND));
73     }
74 
killApp()75     private void killApp() throws Exception {
76         execute(KILL_APP_COMMAND);
77     }
78 
getLongFromResultBundle(byte[] response)79     private long getLongFromResultBundle(byte[] response) {
80         String bundleString = parseBytesAsString(response);
81         Matcher matcher = PATTERN.matcher(bundleString);
82 
83         if (matcher.matches()) {
84             return Long.parseLong(matcher.group(1));
85         } else {
86             throw new IllegalArgumentException("Bundle '" + bundleString
87                     + "' does not have random field");
88         }
89     }
execute(String command)90     private byte[] execute(String command) throws Exception {
91         ByteArrayOutputStream stdOutBytesReceiver = new ByteArrayOutputStream();
92         ByteArrayOutputStream stdErrBytesReceiver = new ByteArrayOutputStream();
93         CommandResult result = mDevice.executeShellV2Command(
94                 command, /*pipeAsInput=*/null, stdOutBytesReceiver, stdErrBytesReceiver,
95                 MAX_TIMEOUT_FOR_COMMAND.toMillis(), TimeUnit.MILLISECONDS, 1 /* retries */);
96         if (result.getExitCode() != 0 || stdErrBytesReceiver.size() > 0) {
97             fail("Command \'" + command + "\' produced exitCode=" + result.getExitCode()
98                     + " and stderr="
99                     + parseBytesAsString(stdErrBytesReceiver.toByteArray()).trim());
100         }
101         return stdOutBytesReceiver.toByteArray();
102     }
103 
parseBytesAsString(byte[] bytes)104     private static String parseBytesAsString(byte[] bytes) {
105         return new String(bytes, StandardCharsets.UTF_8);
106     }
107 }
108