1 /* 2 * Copyright (C) 2021 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 package android.videoqualityfloor.cts; 17 18 import android.platform.test.annotations.Presubmit; 19 20 import com.android.tradefed.build.IBuildInfo; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.log.LogUtil; 23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 24 import com.android.tradefed.testtype.IAbi; 25 import com.android.tradefed.testtype.IAbiReceiver; 26 import com.android.tradefed.testtype.IBuildReceiver; 27 import com.android.tradefed.testtype.IDeviceTest; 28 29 import java.io.BufferedReader; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.FileNotFoundException; 33 import java.io.FileOutputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.io.OutputStream; 38 import java.lang.Process; 39 import java.lang.Runtime; 40 import java.lang.SecurityException; 41 42 import org.junit.Assert; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 /** 47 * Run the host-side video quality floor tests. 48 * These encode a set of videos at particular resolutions/bitrates and 49 * measure the quality of the output. 50 * Measurement is with the VMAF tool. 51 * 52 * The body of this test is implemented in a test script, not within the java here. 53 * This java code acquires the videos and the test script, unpacks them, executes the 54 * script (which encodes and measures). 55 */ 56 @RunWith(DeviceJUnit4ClassRunner.class) 57 public class CtsVideoQualityFloorHostTest implements IAbiReceiver, IBuildReceiver, IDeviceTest { 58 59 public static final String TAG = CtsVideoQualityFloorHostTest.class.getSimpleName(); 60 String BASE_URL = 61 "https://dl.google.com/android/xts/cts/hostsidetests/videoqualityfloor/"; 62 String[] testClips = { 63 "Fireworks.mp4", 64 "Motorcycle.mp4", 65 "MountainBike.mp4", 66 "TreesAndGrass.mp4" 67 }; 68 // test is not valid before sdk 31, aka Android 12, aka Android S 69 static final int MINIMUM_VALID_SDK = 31; 70 71 /** 72 * A reference to the build info. 73 */ 74 private IBuildInfo mBuildInfo; 75 76 /** 77 * A reference to the device under test. 78 */ 79 private ITestDevice mDevice; 80 81 /** 82 * A reference to the ABI under test. 83 */ 84 private IAbi mAbi; 85 86 @Override setAbi(IAbi abi)87 public void setAbi(IAbi abi) { 88 mAbi = abi; 89 } 90 91 @Override setBuild(IBuildInfo buildInfo)92 public void setBuild(IBuildInfo buildInfo) { 93 mBuildInfo = buildInfo; 94 } 95 96 @Override setDevice(ITestDevice device)97 public void setDevice(ITestDevice device) { 98 mDevice = device; 99 } 100 101 @Override getDevice()102 public ITestDevice getDevice() { 103 return mDevice; 104 } 105 getProperty(String prop)106 private String getProperty(String prop) throws Exception { 107 return mDevice.executeShellCommand("getprop " + prop).replace("\n", ""); 108 } 109 110 /** 111 * TODO: Add JavaDoc 112 */ 113 @Test testEncoding()114 public void testEncoding() throws Exception { 115 116 String sdkAsString = getProperty("ro.build.version.sdk"); 117 int sdk = Integer.parseInt(sdkAsString); 118 if (sdk < MINIMUM_VALID_SDK) { 119 LogUtil.CLog.d("Test requires sdk >= " + MINIMUM_VALID_SDK 120 + " test device has sdk =" + sdk ); 121 return; 122 } 123 124 Runtime runtime = Runtime.getRuntime(); 125 Process subproc; 126 String tmpBase = System.getProperty("java.io.tmpdir"); 127 String dirName = "CtsVideoQualityFloorHostTest"; 128 String tmpDir = tmpBase + "/" + dirName; 129 130 LogUtil.CLog.d("tmpBase= " + tmpBase + " tmpDir =" + tmpDir); 131 132 if (false) { 133 // start with a fresh directory 134 File cwd = new File("."); 135 runCmd("rm -fr " + tmpDir, cwd); 136 } 137 138 // set up test directory, make sure it exists 139 File destination = new File(tmpDir); 140 try { 141 if (!destination.isDirectory()) { 142 destination.mkdirs(); 143 } 144 } catch (SecurityException e) { 145 LogUtil.CLog.d("Unable to establish temp directory " + destination.getPath()); 146 } 147 Assert.assertTrue(destination.isDirectory()); 148 149 /* 150 * 151 * https://dl.google.com/android/xts/cts/hostsidetests/videoqualityfloor/ 152 * + the name of the file 153 * 154 * turning the filename into the appropriate URL is done within downloadFile() 155 */ 156 157 for (String clip : testClips) { 158 LogUtil.CLog.d("downloading " + clip); 159 downloadFile(clip, destination); 160 } 161 162 // get the test script 163 downloadFile("tests.tar.gz", destination); 164 165 // we *always* unpack and setup, even if the downloads were cached. 166 // this avoids any /tmp cleanup problems. 167 // 168 // unpack the test scripts 169 runCmd("tar xzf tests.tar.gz", destination); 170 171 // run the setup scripts 172 runCmd("./set_up.sh", destination); 173 174 // run the test script 175 // return code is # of failures, so 0 == success 176 String clipArgs = ""; 177 for (String clip : testClips) { 178 clipArgs= clipArgs + " " + clip; 179 } 180 String targetSerial = getDevice().getSerialNumber(); 181 String testOutput = runCmd("./testit.sh -serial " + targetSerial + clipArgs, destination); 182 183 LogUtil.CLog.d("(Successful) Output from testit.sh:\n\n" + testOutput); 184 185 } 186 187 // run the indicated command, in the indicated directory 188 // returns the command output 189 // asserts if the command finishes with non-zero exit code. runCmd(String cmd, File cwd)190 private String runCmd(String cmd, File cwd) { 191 Runtime runtime = Runtime.getRuntime(); 192 Process subproc; 193 LogUtil.CLog.d("runCmd() cmd=" + cmd + " cwd=" + cwd.getPath()); 194 int result = 0; 195 String childStdout = ""; 196 try { 197 subproc = runtime.exec(cmd, null, cwd); 198 subproc.waitFor(); // may wait forever; there are versions with timeout 199 BufferedReader kidstream = 200 new BufferedReader(new InputStreamReader(subproc.getInputStream())); 201 String line; 202 StringBuilder kidStdout = new StringBuilder(); 203 while ((line = kidstream.readLine()) != null) { 204 kidStdout.append(line); 205 kidStdout.append("\n"); 206 } 207 childStdout = kidStdout.toString(); 208 result = subproc.exitValue(); 209 } catch (IOException e) { 210 result = -2; 211 } catch (InterruptedException e) { 212 result = -3; 213 } 214 Assert.assertTrue("runCmd() fails with output\n" + childStdout, result == 0); 215 216 return childStdout; 217 } 218 219 // download the indicated file (within the base_url folder) to 220 // our desired destination/fileName. 221 // simple caching -- if file exists, we do not redownload downloadFile(String fileName, File destDir)222 private void downloadFile(String fileName, File destDir) { 223 Runtime runtime = Runtime.getRuntime(); 224 Process subproc; 225 226 File destination = new File(destDir, fileName); 227 228 // save bandwidth, also allows a user to manually preload files 229 LogUtil.CLog.d("Do we already have a copy of file " + destination.getPath()); 230 if (destination.isFile()) { 231 LogUtil.CLog.d("Skipping re-download of file " + destination.getPath()); 232 return; 233 } 234 235 String url = BASE_URL + fileName; 236 String cmd = "wget -O " + destination.getPath() + " " + url; 237 runCmd(cmd, destDir); 238 } 239 240 } 241 242