1#!/usr/bin/env python
2#
3# Copyright (C) 2015 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the 'License');
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an 'AS IS' BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import os
19import sys
20import threading
21import time
22import Queue
23sys.path.append(sys.path[0])
24from android_device import *
25
26CTS_THEME_dict = {
27    120 : "ldpi",
28    160 : "mdpi",
29    213 : "tvdpi",
30    240 : "hdpi",
31    320 : "xhdpi",
32    400 : "400dpi",
33    480 : "xxhdpi",
34    560 : "560dpi",
35    640 : "xxxhdpi",
36}
37
38OUT_FILE = "/sdcard/cts-theme-assets.zip"
39
40# pass a function with number of instances to be executed in parallel
41# each thread continues until config q is empty.
42def executeParallel(tasks, setup, q, numberThreads):
43    class ParallelExecutor(threading.Thread):
44        def __init__(self, tasks, q):
45            threading.Thread.__init__(self)
46            self._q = q
47            self._tasks = tasks
48            self._setup = setup
49            self._result = 0
50
51        def run(self):
52            try:
53                while True:
54                    config = q.get(block=True, timeout=2)
55                    for t in self._tasks:
56                        try:
57                            if t(self._setup, config):
58                                self._result += 1
59                        except KeyboardInterrupt:
60                            raise
61                        except:
62                            print "Failed to execute thread:", sys.exc_info()[0]
63                    q.task_done()
64            except KeyboardInterrupt:
65                raise
66            except Queue.Empty:
67                pass
68
69        def getResult(self):
70            return self._result
71
72    result = 0;
73    threads = []
74    for i in range(numberThreads):
75        t = ParallelExecutor(tasks, q)
76        t.start()
77        threads.append(t)
78    for t in threads:
79        t.join()
80        result += t.getResult()
81    return result;
82
83def printAdbResult(device, out, err):
84    print "device: " + device
85    if out is not None:
86        print "out:\n" + out
87    if err is not None:
88        print "err:\n" + err
89
90def getResDir(outPath, resName):
91    resDir = outPath + "/" + resName
92    return resDir
93
94def doCapturing(setup, deviceSerial):
95    (themeApkPath, outPath) = setup
96
97    print "Found device: " + deviceSerial
98    device = androidDevice(deviceSerial)
99
100    # outPath = outPath + "/%d/" % (device.getSdkLevel()) + deviceSerial
101    outPath = outPath + "/%d" % (device.getSdkLevel())
102    density = device.getDensity()
103    resName = CTS_THEME_dict[density]
104
105    device.uninstallApk("android.theme.app")
106
107    (out, err, success) = device.installApk(themeApkPath)
108    if not success:
109        print "Failed to install APK on " + deviceSerial
110        printAdbResult(device, out, err)
111        return False
112
113    print "Generating images on " + deviceSerial + "..."
114    try:
115        (out, err) = device.runInstrumentationTest("android.theme.app/android.support.test.runner.AndroidJUnitRunner")
116    except KeyboardInterrupt:
117        raise
118    except:
119        (out, err) = device.runInstrumentationTest("android.theme.app/android.test.InstrumentationTestRunner")
120
121    # Detect test failure and abort.
122    if "FAILURES!!!" in out.split():
123        printAdbResult(deviceSerial, out, err)
124        return False
125
126    # Make sure that the run is complete by checking the process itself
127    print "Waiting for " + deviceSerial + "..."
128    waitTime = 0
129    while device.isProcessAlive("android.theme.app"):
130        time.sleep(1)
131        waitTime = waitTime + 1
132        if waitTime > 180:
133            print "Timed out"
134            break
135
136    time.sleep(10)
137    resDir = getResDir(outPath, resName)
138
139    print "Pulling images from " + deviceSerial + " to " + resDir + ".zip"
140    device.runAdbCommand("pull " + OUT_FILE + " " + resDir + ".zip")
141    device.runAdbCommand("shell rm -rf " + OUT_FILE)
142    return True
143
144def main(argv):
145    if len(argv) < 3:
146        print "run_theme_capture_device.py themeApkPath outDir"
147        sys.exit(1)
148    themeApkPath = argv[1]
149    outPath = os.path.abspath(argv[2])
150    os.system("mkdir -p " + outPath)
151
152    tasks = []
153    tasks.append(doCapturing)
154
155    devices = runAdbDevices();
156    numberThreads = len(devices)
157
158    configQ = Queue.Queue()
159    for device in devices:
160        configQ.put(device)
161    setup = (themeApkPath, outPath)
162    result = executeParallel(tasks, setup, configQ, numberThreads)
163
164    if result > 0:
165        print 'Generated reference images for %(count)d devices' % {"count": result}
166    else:
167        print 'Failed to generate reference images'
168
169if __name__ == '__main__':
170    main(sys.argv)
171