Merge "Fix CameraManagerTest logic." into marshmallow-cts-dev
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d8a96eb..f4d7825 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -339,8 +339,7 @@
<string name="hifi_ultrasound_test_default_false_string">false</string>
<string name="hifi_ultrasound_test_mic_no_support">
Device does not support near-ultrasound recording.\n
- All new phones and tablets MUST support near-ultrasound recording.\n
- Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+ Please report PASS.\n</string>
<string name="hifi_ultrasound_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
If this is your reference device, please use a different reference device.\n</string>
@@ -360,8 +359,7 @@
If this is your reference device, please use a different reference device.\n</string>
<string name="hifi_ultrasound_speaker_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
- All new phones and tablets MUST support near-ultrasound playback.\n
- Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+ Please report PASS.\n</string>
<string name="hifi_ultrasound_speaker_test_test_side">
Please wait for the result on the reference device then report here.</string>
<string name="hifi_ultrasound_speaker_test_reference_side">
@@ -1732,7 +1730,7 @@
<string name="device_owner_wifi_lockdown_info">
Please enter the SSID and auth method of an available WiFi Access Point and press the button to create a
WiFi configuration. This configuration can be seen on Settings > WiFi. The test cases
- are going use this config. Please go through test cases in order (from top to bottom).
+ are going to use this config. Please go through test cases in order (from top to bottom).
</string>
<string name="switch_wifi_lockdown_off_button">WiFi config lockdown off</string>
<string name="switch_wifi_lockdown_on_button">WiFi config lockdown on</string>
@@ -1854,6 +1852,18 @@
<string name="device_owner_user_restriction_set">Set restriction</string>
<string name="device_owner_settings_go">Go</string>
+ <string name="device_owner_disallow_config_vpn">Disallow configuring VPN</string>
+ <string name="device_owner_disallow_config_vpn_info">
+ Please press the Set VPN restriction button to set the VPN restriction.
+ Then press Go to open the VPN page.\n\n
+ Confirm that:\n
+ - You cannot add a new VPN network.\n
+ - You cannot edit, add or remove any existing VPNs.\n
+ \n
+ Use the Back button to return to this page.
+ </string>
+ <string name="device_owner_user_vpn_restriction_set">Set VPN restriction</string>
+
<!-- Strings for JobScheduler Tests -->
<string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index b129665..a5022a3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -347,6 +347,9 @@
adapter.add(mKeyguardDisabledFeaturesTest);
adapter.add(mAuthenticationBoundKeyTest);
+ /* If there is an application that handles ACTION_IMAGE_CAPTURE, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
// Capture image intent can be resolved in primary profile, so test.
mCrossProfileImageCaptureSupportTest = new DialogTestListItem(this,
@@ -362,6 +365,9 @@
.show();
}
+ /* If there is an application that handles ACTION_VIDEO_CAPTURE, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureVideoIntent())) {
// Capture video intent can be resolved in primary profile, so test.
mCrossProfileVideoCaptureSupportTest = new DialogTestListItem(this,
@@ -409,7 +415,9 @@
adapter.add(mDisableNfcBeamTest);
}
- /* TODO: reinstate when bug b/20131958 is fixed
+ /* If there is an application that handles RECORD_SOUND_ACTION, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureAudioIntent())) {
// Capture audio intent can be resolved in primary profile, so test.
mCrossProfileAudioCaptureSupportTest = new DialogTestListItem(this,
@@ -424,7 +432,6 @@
R.string.provisioning_byod_no_audio_capture_resolver, Toast.LENGTH_SHORT)
.show();
}
- */
}
// Return whether the intent can be resolved in the current profile
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index a6a5e5a..79b9933 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -75,6 +75,9 @@
PermissionLockdownTestActivity.class.getName();
private static final String DISALLOW_CONFIG_BT_ID = "DISALLOW_CONFIG_BT";
private static final String DISALLOW_CONFIG_WIFI_ID = "DISALLOW_CONFIG_WIFI";
+ private static final String DISALLOW_CONFIG_VPN_ID = "DISALLOW_CONFIG_VPN";
+ //TODO(rgl): This symbol should be available in android.provider.settings
+ private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
@Override
@@ -166,6 +169,19 @@
new Intent(Settings.ACTION_WIFI_SETTINGS))}));
}
+ // DISALLOW_CONFIG_VPN
+ adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_VPN_ID,
+ R.string.device_owner_disallow_config_vpn,
+ R.string.device_owner_disallow_config_vpn_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_user_vpn_restriction_set,
+ createSetUserRestrictionIntent(
+ UserManager.DISALLOW_CONFIG_VPN)),
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ new Intent(ACTION_VPN_SETTINGS))}));
+
// DISALLOW_CONFIG_BLUETOOTH
if (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_BT_ID,
@@ -326,6 +342,7 @@
dpm.setKeyguardDisabled(admin, false);
dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_BLUETOOTH);
dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_WIFI);
+ dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_VPN);
dpm.clearDeviceOwnerApp(getPackageName());
}
}
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index b3ebbad..0a454b6 100755
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -160,6 +160,14 @@
return null;
}
+ public static boolean canEncode(MediaFormat format) {
+ if (sMCL.findEncoderForFormat(format) == null) {
+ Log.i(TAG, "no encoder for " + format);
+ return false;
+ }
+ return true;
+ }
+
public static boolean canDecode(MediaFormat format) {
if (sMCL.findDecoderForFormat(format) == null) {
Log.i(TAG, "no decoder for " + format);
@@ -407,6 +415,10 @@
return canDecode(format);
}
+ public static boolean checkEncoderForFormat(MediaFormat format) {
+ return check(canEncode(format), "no encoder for " + format);
+ }
+
public static boolean checkDecoderForFormat(MediaFormat format) {
return check(canDecode(format), "no decoder for " + format);
}
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 672d3ed..9d9e1663 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -82,7 +82,7 @@
public static final String PERMISSION_GRANTED = "android.content.cts.permission.TEST_GRANTED";
public static final String PERMISSION_DENIED = "android.content.cts.permission.TEST_DENIED";
- private static final int BROADCAST_TIMEOUT = 10000;
+ private static final int BROADCAST_TIMEOUT = 15000;
private Context mContext;
@@ -216,13 +216,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertTrue(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
@@ -235,13 +235,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver2.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver2, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
broadcastReceiver2.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver2, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
}
@@ -256,13 +256,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertTrue(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
@@ -792,8 +792,8 @@
waitForCondition(con);
}
- private void waitForFilteredIntent(ContextWrapper contextWrapper,
- final FilteredReceiver receiver, final String action) throws InterruptedException {
+ private void waitForFilteredIntent(ContextWrapper contextWrapper, final String action)
+ throws InterruptedException {
contextWrapper.sendOrderedBroadcast(new Intent(action), null);
synchronized (mLockObj) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 6d62067..28693c6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -178,9 +178,10 @@
if (activeArraySize.getWidth() >= FULLHD.getWidth() &&
activeArraySize.getHeight() >= FULLHD.getHeight()) {
- assertArrayContains(String.format(
+ assertArrayContainsAnyOf(String.format(
"Required FULLHD size not found for format %x for: ID %s",
- ImageFormat.JPEG, mIds[counter]), jpegSizes, FULLHD);
+ ImageFormat.JPEG, mIds[counter]), jpegSizes,
+ new Size[] {FULLHD, FULLHD_ALT});
}
if (activeArraySize.getWidth() >= HD.getWidth() &&
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
index 40934f5..7c4d1fb 100644
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -16,6 +16,7 @@
package android.media.cts;
+import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.MediaCodec;
@@ -284,6 +285,10 @@
@Override
public void run() {
+ if (mTest.shouldSkip()) {
+ return;
+ }
+
InputSurface inputSurface = null;
try {
if (!mUsePersistentInput) {
@@ -339,6 +344,22 @@
mAllowBT709 = allowBT709;
}
+ private boolean shouldSkip() {
+ if (!MediaUtils.hasEncoder(mMimeType)) {
+ return true;
+ }
+
+ MediaFormat format = MediaFormat.createVideoFormat(mMimeType, mWidth, mHeight);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ if (!MediaUtils.checkEncoderForFormat(format)) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Tests encoding and subsequently decoding video from frames generated into a buffer.
* <p>
@@ -348,6 +369,10 @@
* See http://b.android.com/37769 for a discussion of input format pitfalls.
*/
private void encodeDecodeVideoFromBuffer(boolean toSurface) throws Exception {
+ if (shouldSkip()) {
+ return;
+ }
+
MediaCodec encoder = null;
MediaCodec decoder = null;
diff --git a/tools/cts-media/get_achievable_rates.py b/tools/cts-media/get_achievable_rates.py
new file mode 100755
index 0000000..81412da
--- /dev/null
+++ b/tools/cts-media/get_achievable_rates.py
@@ -0,0 +1,396 @@
+#!/usr/bin/python
+# Copyright (C) 2015 The Android Open Source Project
+#
+# 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.
+#
+
+import argparse, math, re, sys
+import xml.etree.ElementTree as ET
+from collections import defaultdict, namedtuple
+import itertools
+
+
+def createLookup(values, key):
+ """Creates a lookup table for a collection of values based on keys.
+
+ Arguments:
+ values: a collection of arbitrary values. Must be iterable.
+ key: a function of one argument that returns the key for a value.
+
+ Returns:
+ A dict mapping keys (as generated by the key argument) to lists of
+ values. All values in the lists have the same key, and are in the order
+ they appeared in the collection.
+ """
+ lookup = defaultdict(list)
+ for v in values:
+ lookup[key(v)].append(v)
+ return lookup
+
+
+def _intify(value):
+ """Returns a value converted to int if possible, else the original value."""
+ try:
+ return int(value)
+ except ValueError:
+ return value
+
+
+class Size(namedtuple('Size', ['width', 'height'])):
+ """A namedtuple with width and height fields."""
+ def __str__(self):
+ return '%dx%d' % (self.width, self.height)
+
+
+class _VideoResultBase(object):
+ """Helper methods for results. Not for use by applications.
+
+ Attributes:
+ codec: The name of the codec (string) or None
+ size: Size representing the video size or None
+ mime: The mime-type of the codec (string) or None
+ rates: The measured achievable frame rates
+ is_decoder: True iff codec is a decoder.
+ """
+
+ def __init__(self, is_decoder):
+ self.codec = None
+ self.mime = None
+ self.size = None
+ self._rates_from_failure = []
+ self._rates_from_message = []
+ self.is_decoder = is_decoder
+
+ def _inited(self):
+ """Returns true iff codec, mime and size was set."""
+ return None not in (self.codec, self.mime, self.size)
+
+ def __len__(self):
+ # don't report any result if codec name, mime type and size is unclear
+ if not self._inited():
+ return 0
+ return len(self.rates)
+
+ @property
+ def rates(self):
+ return self._rates_from_failure or self._rates_from_message
+
+ def _parseDict(self, value):
+ """Parses a MediaFormat from its string representation sans brackets."""
+ return dict((k, _intify(v))
+ for k, v in re.findall(r'([^ =]+)=([^ [=]+(?:|\[[^\]]+\]))(?:, |$)', value))
+
+ def _cleanFormat(self, format):
+ """Removes internal fields from a parsed MediaFormat."""
+ format.pop('what', None)
+ format.pop('image-data', None)
+
+ MESSAGE_PATTERN = r'(?P<key>\w+)=(?P<value>\{[^}]*\}|[^ ,{}]+)'
+
+ def _parsePartialResult(self, message_match):
+ """Parses a partial test result conforming to the message pattern.
+
+ Returns:
+ A tuple of string key and int, string or dict value, where dict has
+ string keys mapping to int or string values.
+ """
+ key, value = message_match.group('key', 'value')
+ if value.startswith('{'):
+ value = self._parseDict(value[1:-1])
+ if key.endswith('Format'):
+ self._cleanFormat(value)
+ else:
+ value = _intify(value)
+ return key, value
+
+ def _parseValuesFromBracket(self, line):
+ """Returns the values enclosed in brackets without the brackets.
+
+ Parses a line matching the pattern "<tag>: [<values>]" and returns <values>.
+
+ Raises:
+ ValueError: if the line does not match the pattern.
+ """
+ try:
+ return re.match(r'^[^:]+: *\[(?P<values>.*)\]\.$', line).group('values')
+ except AttributeError:
+ raise ValueError('line does not match "tag: [value]": %s' % line)
+
+ def _parseRawData(self, line):
+ """Parses the raw data line for video performance tests.
+
+ Yields:
+ Dict objects corresponding to parsed results, mapping string keys to
+ int, string or dict values.
+ """
+ try:
+ values = self._parseValuesFromBracket(line)
+ result = {}
+ for m in re.finditer(self.MESSAGE_PATTERN + r'(?P<sep>,? +|$)', values):
+ key, value = self._parsePartialResult(m)
+ result[key] = value
+ if m.group('sep') != ' ':
+ yield result
+ result = {}
+ except ValueError:
+ print >> sys.stderr, 'could not parse line %s' % repr(line)
+
+ def _tryParseMeasuredFrameRate(self, line):
+ """Parses a line starting with 'Measured frame rate:'."""
+ if line.startswith('Measured frame rate: '):
+ try:
+ values = self._parseValuesFromBracket(line)
+ values = re.split(r' *, *', values)
+ self._rates_from_failure = list(map(float, values))
+ except ValueError:
+ print >> sys.stderr, 'could not parse line %s' % repr(line)
+
+ def parse(self, test):
+ """Parses the ValueArray and FailedScene lines of a test result.
+
+ Arguments:
+ test: An ElementTree <Test> element.
+ """
+ failure = test.find('FailedScene')
+ if failure is not None:
+ trace = failure.find('StackTrace')
+ if trace is not None:
+ for line in re.split(r'[\r\n]+', trace.text):
+ self._parseFailureLine(line)
+ details = test.find('Details')
+ if details is not None:
+ for array in details.iter('ValueArray'):
+ message = array.get('message')
+ self._parseMessage(message, array)
+
+ def _parseFailureLine(self, line):
+ raise NotImplementedError
+
+ def _parseMessage(self, message, array):
+ raise NotImplementedError
+
+ def getData(self):
+ """Gets the parsed test result data.
+
+ Yields:
+ Result objects containing at least codec, size, mime and rates attributes."""
+ yield self
+
+
+class VideoEncoderDecoderTestResult(_VideoResultBase):
+ """Represents a result from a VideoEncoderDecoderTest performance case."""
+
+ def __init__(self, unused_m):
+ super(VideoEncoderDecoderTestResult, self).__init__(is_decoder=False)
+
+ # If a VideoEncoderDecoderTest succeeds, it provides the results in the
+ # message of a ValueArray. If fails, it provides the results in the failure
+ # using raw data. (For now it also includes some data in the ValueArrays even
+ # if it fails, which we ignore.)
+
+ def _parseFailureLine(self, line):
+ """Handles parsing a line from the failure log."""
+ self._tryParseMeasuredFrameRate(line)
+
+ def _parseMessage(self, message, array):
+ """Handles parsing a message from ValueArrays."""
+ if message.startswith('codec='):
+ result = dict(self._parsePartialResult(m)
+ for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
+ if 'EncInputFormat' in result:
+ self.codec = result['codec']
+ fmt = result['EncInputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.mime = result['EncOutputFormat']['mime']
+ self._rates_from_message.append(1000000./result['min'])
+
+
+class VideoDecoderPerfTestResult(_VideoResultBase):
+ """Represents a result from a VideoDecoderPerfTest performance case."""
+
+ # If a VideoDecoderPerfTest succeeds, it provides the results in the message
+ # of a ValueArray. If fails, it provides the results in the failure only
+ # using raw data.
+
+ def __init__(self, unused_m):
+ super(VideoDecoderPerfTestResult, self).__init__(is_decoder=True)
+
+ def _parseFailureLine(self, line):
+ """Handles parsing a line from the failure log."""
+ self._tryParseMeasuredFrameRate(line)
+ # if the test failed, we can only get the codec/size/mime from the raw data.
+ if line.startswith('Raw data: '):
+ for result in self._parseRawData(line):
+ fmt = result['DecOutputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.codec = result['codec']
+ self.mime = result['mime']
+
+ def _parseMessage(self, message, array):
+ """Handles parsing a message from ValueArrays."""
+ if message.startswith('codec='):
+ result = dict(self._parsePartialResult(m)
+ for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
+ if result.get('decodeto') == 'surface':
+ self.codec = result['codec']
+ fmt = result['DecOutputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.mime = result['mime']
+ self._rates_from_message.append(1000000. / result['min'])
+
+
+class Results(object):
+ """Container that keeps all test results."""
+ def __init__(self):
+ self._results = [] # namedtuples
+ self._device = None
+
+ VIDEO_ENCODER_DECODER_TEST_REGEX = re.compile(
+ 'test(.*)(\d{4})x(\d{4})(Goog|Other)$')
+
+ VIDEO_DECODER_PERF_TEST_REGEX = re.compile(
+ 'test(VP[89]|H26[34]|MPEG4|HEVC)(\d+)x(\d+)(.*)$')
+
+ TestCaseSpec = namedtuple('TestCaseSpec', 'package path class_ regex result_class')
+
+ def _getTestCases(self):
+ return [
+ self.TestCaseSpec(package='CtsDeviceVideoPerf',
+ path='TestSuite/TestSuite/TestSuite/TestSuite/TestCase',
+ class_='VideoEncoderDecoderTest',
+ regex=self.VIDEO_ENCODER_DECODER_TEST_REGEX,
+ result_class=VideoEncoderDecoderTestResult),
+ self.TestCaseSpec(package='CtsMediaTestCases',
+ path='TestSuite/TestSuite/TestSuite/TestCase',
+ class_='VideoDecoderPerfTest',
+ regex=self.VIDEO_DECODER_PERF_TEST_REGEX,
+ result_class=VideoDecoderPerfTestResult)
+ ]
+
+ def _verifyDeviceInfo(self, device):
+ assert self._device in (None, device), "expected %s device" % self._device
+ self._device = device
+
+ def importXml(self, xml):
+ self._verifyDeviceInfo(xml.find('DeviceInfo/BuildInfo').get('buildName'))
+
+ packages = createLookup(self._getTestCases(), lambda tc: tc.package)
+
+ for pkg in xml.iter('TestPackage'):
+ tests_in_package = packages.get(pkg.get('name'))
+ if not tests_in_package:
+ continue
+ paths = createLookup(tests_in_package, lambda tc: tc.path)
+ for path, tests_in_path in paths.items():
+ classes = createLookup(tests_in_path, lambda tc: tc.class_)
+ for tc in pkg.iterfind(path):
+ tests_in_class = classes.get(tc.get('name'))
+ if not tests_in_class:
+ continue
+ for test in tc.iter('Test'):
+ for tc in tests_in_class:
+ m = tc.regex.match(test.get('name'))
+ if m:
+ result = tc.result_class(m)
+ result.parse(test)
+ self._results.append(result)
+
+ def importFile(self, path):
+ print >> sys.stderr, 'Importing "%s"...' % path
+ try:
+ return self.importXml(ET.parse(path))
+ except ET.ParseError:
+ raise ValueError('not a valid XML file')
+
+ def getData(self):
+ for result in self._results:
+ for data in result.getData():
+ yield data
+
+ def dumpXml(self, results):
+ yield '<?xml version="1.0" encoding="utf-8" ?>'
+ yield '<!-- Copyright 2015 The Android Open Source Project'
+ yield ''
+ yield ' Licensed under the Apache License, Version 2.0 (the "License");'
+ yield ' you may not use this file except in compliance with the License.'
+ yield ' You may obtain a copy of the License at'
+ yield ''
+ yield ' http://www.apache.org/licenses/LICENSE-2.0'
+ yield ''
+ yield ' Unless required by applicable law or agreed to in writing, software'
+ yield ' distributed under the License is distributed on an "AS IS" BASIS,'
+ yield ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
+ yield ' See the License for the specific language governing permissions and'
+ yield ' limitations under the License.'
+ yield '-->'
+ yield ''
+ yield '<MediaCodecs>'
+ last_section = None
+ Comp = namedtuple('Comp', 'is_decoder google mime name')
+ by_comp = createLookup(results,
+ lambda e: Comp(is_decoder=e.is_decoder, google='.google.' in e.codec, mime=e.mime, name=e.codec))
+ for comp in sorted(by_comp):
+ section = 'Decoders' if comp.is_decoder else 'Encoders'
+ if section != last_section:
+ if last_section:
+ yield ' </%s>' % last_section
+ yield ' <%s>' % section
+ last_section = section
+ yield ' <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
+ by_size = createLookup(by_comp[comp], lambda e: e.size)
+ for size in sorted(by_size):
+ values = list(itertools.chain(*(e.rates for e in by_size[size])))
+ min_, max_ = min(values), max(values)
+ med_ = int(math.sqrt(min_ * max_))
+ yield ' <Limit name="measured-frame-rate-%s" range="%d-%d" />' % (size, med_, med_)
+ yield ' </MediaCodec>'
+ if last_section:
+ yield ' </%s>' % last_section
+ yield '</MediaCodecs>'
+
+
+class Main(object):
+ """Executor of this utility."""
+
+ def __init__(self):
+ self._result = Results()
+
+ self._parser = argparse.ArgumentParser('get_achievable_framerates')
+ self._parser.add_argument('result_xml', nargs='+')
+
+ def _parseArgs(self):
+ self._args = self._parser.parse_args()
+
+ def _importXml(self, xml):
+ self._result.importFile(xml)
+
+ def _report(self):
+ for line in self._result.dumpXml(r for r in self._result.getData() if r):
+ print line
+
+ def run(self):
+ self._parseArgs()
+ try:
+ for xml in self._args.result_xml:
+ try:
+ self._importXml(xml)
+ except (ValueError, IOError, AssertionError) as e:
+ print >> sys.stderr, e
+ raise KeyboardInterrupt
+ self._report()
+ except KeyboardInterrupt:
+ print >> sys.stderr, 'Interrupted.'
+
+if __name__ == '__main__':
+ Main().run()
+