Fix bug 1844502: Add create_test.py to generate Android.mk and AndroidManifest.xml for application tests.
This commit is contained in:
60
testrunner/android_manifest.py
Normal file
60
testrunner/android_manifest.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""In memory representation of AndroidManifest.xml file.
|
||||
|
||||
Specification of AndroidManifest.xml can be found at
|
||||
http://developer.android.com/guide/topics/manifest/manifest-intro.html
|
||||
"""
|
||||
|
||||
# python imports
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
|
||||
class AndroidManifest(object):
|
||||
"""In memory representation of AndroidManifest.xml file."""
|
||||
|
||||
FILENAME = "AndroidManifest.xml"
|
||||
|
||||
def __init__(self, app_path=None):
|
||||
if app_path:
|
||||
self.ParseManifest(app_path)
|
||||
|
||||
def GetPackageName(self):
|
||||
"""Retrieve package name defined at <manifest package="...">.
|
||||
|
||||
Returns:
|
||||
Package name if defined, otherwise None
|
||||
"""
|
||||
manifests = self._dom.getElementsByTagName("manifest")
|
||||
if not manifests or not manifests[0].getAttribute("package"):
|
||||
return None
|
||||
return manifests[0].getAttribute("package")
|
||||
|
||||
def ParseManifest(self, app_path):
|
||||
"""Parse AndroidManifest.xml at the specified path.
|
||||
|
||||
Args:
|
||||
app_path: path to folder containing AndroidManifest.xml
|
||||
Raises:
|
||||
IOError: AndroidManifest.xml cannot be found at given path, or cannot be
|
||||
opened for reading
|
||||
"""
|
||||
self.app_path = app_path.rstrip("/")
|
||||
self.manifest_path = "%s/%s" % (self.app_path, self.FILENAME)
|
||||
self._dom = xml.dom.minidom.parse(self.manifest_path)
|
||||
96
testrunner/android_mk.py
Normal file
96
testrunner/android_mk.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""In memory representation of Android.mk file.
|
||||
|
||||
Specifications for Android.mk can be found at
|
||||
development/ndk/docs/ANDROID-MK.txt
|
||||
"""
|
||||
|
||||
import re
|
||||
from sets import Set
|
||||
|
||||
|
||||
class AndroidMK(object):
|
||||
"""In memory representation of Android.mk file."""
|
||||
|
||||
_RE_INCLUDE = re.compile(r'include\s+\$\((.+)\)')
|
||||
_VAR_DELIMITER = ":="
|
||||
FILENAME = "Android.mk"
|
||||
CERTIFICATE = "LOCAL_CERTIFICATE"
|
||||
PACKAGE_NAME = "LOCAL_PACKAGE_NAME"
|
||||
|
||||
def __init__(self, app_path=None):
|
||||
self._includes = Set() # variables included in makefile
|
||||
self._variables = {} # variables defined in makefile
|
||||
|
||||
if app_path:
|
||||
self.ParseMK(app_path)
|
||||
|
||||
def _ProcessMKLine(self, line):
|
||||
"""Add a variable definition or include.
|
||||
|
||||
Ignores unrecognized lines.
|
||||
|
||||
Args:
|
||||
line: line of text from makefile
|
||||
"""
|
||||
m = self._RE_INCLUDE.match(line)
|
||||
if m:
|
||||
self._includes.add(m.group(1))
|
||||
else:
|
||||
parts = line.split(self._VAR_DELIMITER)
|
||||
if len(parts) > 1:
|
||||
self._variables[parts[0].strip()] = parts[1].strip()
|
||||
|
||||
def GetVariable(self, identifier):
|
||||
"""Retrieve makefile variable.
|
||||
|
||||
Args:
|
||||
identifier: name of variable to retrieve
|
||||
Returns:
|
||||
value of specified identifier, None if identifier not found in makefile
|
||||
"""
|
||||
# use dict.get(x) rather than dict[x] to avoid KeyError exception,
|
||||
# so None is returned if identifier not found
|
||||
return self._variables.get(identifier, None)
|
||||
|
||||
def HasInclude(self, identifier):
|
||||
"""Check variable is included in makefile.
|
||||
|
||||
Args:
|
||||
identifer: name of variable to check
|
||||
Returns:
|
||||
True if identifer is included in makefile, otherwise False
|
||||
"""
|
||||
return identifier in self._includes
|
||||
|
||||
def ParseMK(self, app_path):
|
||||
"""Parse Android.mk at the specified path.
|
||||
|
||||
Args:
|
||||
app_path: path to folder containing Android.mk
|
||||
Raises:
|
||||
IOError: Android.mk cannot be found at given path, or cannot be opened
|
||||
for reading
|
||||
"""
|
||||
self.app_path = app_path.rstrip("/")
|
||||
self.mk_path = "%s/%s" % (self.app_path, self.FILENAME)
|
||||
mk = open(self.mk_path)
|
||||
for line in mk:
|
||||
self._ProcessMKLine(line)
|
||||
mk.close()
|
||||
245
testrunner/create_test.py
Executable file
245
testrunner/create_test.py
Executable file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, 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.
|
||||
|
||||
"""Utility to create Android project files for tests."""
|
||||
|
||||
# python imports
|
||||
import datetime
|
||||
import optparse
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
|
||||
# local imports
|
||||
import android_mk
|
||||
import android_manifest
|
||||
|
||||
|
||||
class TestsConsts(object):
|
||||
"""Constants for test Android.mk and AndroidManifest.xml creation."""
|
||||
|
||||
MK_BUILD_INCLUDE = "call all-makefiles-under,$(LOCAL_PATH)"
|
||||
MK_BUILD_STRING = "\ninclude $(%s)\n" % MK_BUILD_INCLUDE
|
||||
TEST_MANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) $YEAR 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="$PACKAGE_NAME.tests">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="$PACKAGE_NAME"
|
||||
android:label="Tests for $MODULE_NAME">
|
||||
</instrumentation>
|
||||
</manifest>
|
||||
"""
|
||||
TEST_MK_TEMPLATE = """LOCAL_PATH := $$(call my-dir)
|
||||
include $$(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
|
||||
LOCAL_SRC_FILES := $$(call all-java-files-under, src)
|
||||
|
||||
LOCAL_PACKAGE_NAME := ${MODULE_NAME}Tests${CERTIFICATE}
|
||||
|
||||
LOCAL_INSTRUMENTATION_FOR := ${MODULE_NAME}
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $$(BUILD_PACKAGE)
|
||||
"""
|
||||
TESTS_FOLDER = "tests"
|
||||
|
||||
|
||||
def _GenerateTestManifest(manifest, module_name, mapping=None):
|
||||
"""Create and populate tests/AndroidManifest.xml with variable values from
|
||||
Android.mk and AndroidManifest.xml.
|
||||
|
||||
Does nothing if tests/AndroidManifest.xml already exists.
|
||||
|
||||
Args:
|
||||
manifest: AndroidManifest object for application manifest
|
||||
module_name: module name used for labelling
|
||||
mapping: optional user defined mapping of variable values, replaces values
|
||||
extracted from AndroidManifest.xml
|
||||
Raises:
|
||||
IOError: tests/AndroidManifest.xml cannot be opened for writing
|
||||
"""
|
||||
# skip if file already exists
|
||||
tests_path = "%s/%s" % (manifest.app_path, TestsConsts.TESTS_FOLDER)
|
||||
tests_manifest_path = "%s/%s" % (tests_path, manifest.FILENAME)
|
||||
if os.path.exists(tests_manifest_path):
|
||||
_PrintMessage("%s already exists, not overwritten" % tests_manifest_path)
|
||||
return
|
||||
|
||||
if not mapping:
|
||||
package_name = manifest.GetPackageName()
|
||||
mapping = {"PACKAGE_NAME":package_name, "MODULE_NAME":module_name,
|
||||
"YEAR":datetime.date.today().year}
|
||||
output = string.Template(TestsConsts.TEST_MANIFEST_TEMPLATE).substitute(mapping)
|
||||
|
||||
# create tests folder if not existent
|
||||
if not os.path.exists(tests_path):
|
||||
os.mkdir(tests_path)
|
||||
|
||||
# write tests/AndroidManifest.xml
|
||||
tests_manifest = open(tests_manifest_path, mode="w")
|
||||
tests_manifest.write(output)
|
||||
tests_manifest.close()
|
||||
_PrintMessage("Created %s" % tests_manifest_path)
|
||||
|
||||
|
||||
def _GenerateTestMK(mk, mapping=None):
|
||||
"""Create and populate tests/Android.mk with variable values from Android.mk.
|
||||
|
||||
Does nothing if tests/Android.mk already exists.
|
||||
|
||||
Args:
|
||||
mk: AndroidMK object for application makefile
|
||||
mapping: optional user defined mapping of variable values, replaces
|
||||
values stored in mk
|
||||
Raises:
|
||||
IOError: tests/Android.mk cannot be opened for writing
|
||||
"""
|
||||
# skip if file already exists
|
||||
tests_path = "%s/%s" % (mk.app_path, TestsConsts.TESTS_FOLDER)
|
||||
tests_mk_path = "%s/%s" % (tests_path, mk.FILENAME)
|
||||
if os.path.exists(tests_mk_path):
|
||||
_PrintMessage("%s already exists, not overwritten" % tests_mk_path)
|
||||
return
|
||||
|
||||
# append test build if not existent in makefile
|
||||
if not mk.HasInclude(TestsConsts.MK_BUILD_INCLUDE):
|
||||
mk_path = "%s/%s" % (mk.app_path, mk.FILENAME)
|
||||
mk_file = open(mk_path, mode="a")
|
||||
mk_file.write(TestsConsts.MK_BUILD_STRING)
|
||||
mk_file.close()
|
||||
|
||||
# construct tests/Android.mk
|
||||
# include certificate definition if existent in makefile
|
||||
certificate = mk.GetVariable(mk.CERTIFICATE)
|
||||
if certificate:
|
||||
cert_definition = ("\n%s := %s" % (mk.CERTIFICATE, certificate))
|
||||
else:
|
||||
cert_definition = ""
|
||||
if not mapping:
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
mapping = {"MODULE_NAME":module_name, "CERTIFICATE":cert_definition}
|
||||
output = string.Template(TestsConsts.TEST_MK_TEMPLATE).substitute(mapping)
|
||||
|
||||
# create tests folder if not existent
|
||||
if not os.path.exists(tests_path):
|
||||
os.mkdir(tests_path)
|
||||
|
||||
# write tests/Android.mk to disk
|
||||
tests_mk = open(tests_mk_path, mode="w")
|
||||
tests_mk.write(output)
|
||||
tests_mk.close()
|
||||
_PrintMessage("Created %s" % tests_mk_path)
|
||||
|
||||
|
||||
def _ParseArgs(argv):
|
||||
"""Parse the command line arguments.
|
||||
|
||||
Args:
|
||||
argv: the list of command line arguments
|
||||
Returns:
|
||||
a tuple of options and individual command line arguments.
|
||||
"""
|
||||
parser = optparse.OptionParser(usage="%s <app_path>" % sys.argv[0])
|
||||
options, args = parser.parse_args(argv)
|
||||
if len(args) < 1:
|
||||
_PrintError("Error: Incorrect syntax")
|
||||
parser.print_usage()
|
||||
sys.exit()
|
||||
return (options, args)
|
||||
|
||||
|
||||
def _PrintMessage(msg):
|
||||
print >> sys.stdout, msg
|
||||
|
||||
|
||||
def _PrintError(msg):
|
||||
print >> sys.stderr, msg
|
||||
|
||||
|
||||
def _ValidateInputFiles(mk, manifest):
|
||||
"""Verify that required variables are defined in input files.
|
||||
|
||||
Args:
|
||||
mk: AndroidMK object for application makefile
|
||||
manifest: AndroidManifest object for application manifest
|
||||
Raises:
|
||||
RuntimeError: mk does not define LOCAL_PACKAGE_NAME or
|
||||
manifest does not define package variable
|
||||
"""
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
if not module_name:
|
||||
raise RuntimeError("Variable %s missing from %s" %
|
||||
(mk.PACKAGE_NAME, mk.FILENAME))
|
||||
|
||||
package_name = manifest.GetPackageName()
|
||||
if not package_name:
|
||||
raise RuntimeError("Variable package missing from %s" % manifest.FILENAME)
|
||||
|
||||
|
||||
def main(argv):
|
||||
options, args = _ParseArgs(argv)
|
||||
app_path = args[0];
|
||||
|
||||
if not os.path.exists(app_path):
|
||||
_PrintError("Error: Application path %s not found" % app_path)
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
mk = android_mk.AndroidMK(app_path=app_path)
|
||||
manifest = android_manifest.AndroidManifest(app_path=app_path)
|
||||
_ValidateInputFiles(mk, manifest)
|
||||
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
_GenerateTestMK(mk)
|
||||
_GenerateTestManifest(manifest, module_name)
|
||||
except Exception, e:
|
||||
_PrintError("Error: %s" % e)
|
||||
_PrintError("Error encountered, script aborted")
|
||||
sys.exit()
|
||||
|
||||
src_path = app_path + "/tests/src"
|
||||
if not os.path.exists(src_path):
|
||||
os.mkdir(src_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
Reference in New Issue
Block a user