diff --git a/build/sdk.atree b/build/sdk.atree index 0f62ea0bf..438ab77d8 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -24,6 +24,7 @@ bin/aidl platforms/${PLATFORM_NAME}/tools/aidl bin/adb tools/adb bin/sqlite3 tools/sqlite3 bin/dmtracedump tools/dmtracedump +bin/hprof-conv tools/hprof-conv bin/mksdcard tools/mksdcard # other tools diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh index b1e28ff52..77009de8b 100755 --- a/build/tools/make_windows_sdk.sh +++ b/build/tools/make_windows_sdk.sh @@ -74,7 +74,8 @@ function build() { make -j 4 emulator || die "Build failed" # Disable parallel build: it generates "permission denied" issues when # multiple "ar.exe" are running in parallel. - make prebuilt adb fastboot aidl aapt dexdump dmtracedump mksdcard sqlite3 || die "Build failed" + make prebuilt adb fastboot aidl aapt dexdump dmtracedump hprof-conv mksdcard sqlite3 \ + || die "Build failed" } function package() { @@ -114,7 +115,7 @@ function package() { # Remove obsolete stuff from tools & platform TOOLS="$DEST/tools" LIB="$DEST/tools/lib" - rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,mksdcard,sqlite3,android} + rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,hprof-conv,mksdcard,sqlite3,android} rm -v --force "$LIB"/*.so "$LIB"/*.jnilib rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump} diff --git a/docs/howto_build_SDK.txt b/docs/howto_build_SDK.txt new file mode 100644 index 000000000..4b6507d4a --- /dev/null +++ b/docs/howto_build_SDK.txt @@ -0,0 +1,280 @@ +Subject: How to build an Android SDK & ADT Eclipse plugin. +Date: 2009/03/27 + + +Table of content: + 0- License + 1- Foreword + 2- Building an SDK for MacOS and Linux + 3- Building an SDK for Windows + 4- Building an ADT plugin for Eclipse + 5- Conclusion + + + +---------- +0- License +---------- + + Copyright (C) 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. + + + +----------- +1- Foreword +----------- + +This document explains how to build the Android SDK and the ADT Eclipse plugin. + +It is designed for advanced users which are proficient with command-line +operations and know how to setup the pre-required software. + +Basically it's not trivial yet when done right it's not that complicated. + + + +-------------------------------------- +2- Building an SDK for MacOS and Linux +-------------------------------------- + +First, setup your development environment and get the Android source code from +git as explained here: + + http://source.android.com/download + +For example for the cupcake branch: + + $ mkdir ~/my-android-git + $ cd ~/my-android-git + $ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake + $ repo sync + +Then once you have all the source, simply build the SDK using: + + $ cd ~/my-android-git + $ . build/envsetup.sh + $ make sdk + +This will take a while, maybe between 20 minutes and several hours depending on +your machine. After a while you'll see this in the output: + + Package SDK: out/host/darwin-x86/sdk/android-sdk_eng._mac-x86.zip + +Some options: + +- Depending on your machine you can tell 'make' to build more things in + parallel, e.g. if you have a dual core, use "make -j4 sdk" to build faster. + +- You can define "BUILD_NUMBER" to control the build identifier that gets + incorporated in the resulting archive. The default is to use your username. + One suggestion is to include the date, e.g.: + + $ export BUILD_NUMBER=${USER}-`date +%Y%m%d-%H%M%S` + + There are certain characters you should avoid in the build number, typically + everything that might confuse 'make' or your shell. So for example avoid + punctuation and characters like $ & : / \ < > , and . + + + +------------------------------ +3- Building an SDK for Windows +------------------------------ + +A- SDK pre-requisite +-------------------- + +First you need to build an SDK for MacOS and Linux. The Windows build works by +updating an existing MacOS or Linux SDK zip file and replacing the unix +binaries by Windows binaries. + + + +B- Cygwin pre-requisite & code checkout +--------------------------------------- + +Second you need to install Cygwin and configure it: +- Get the installer at http://sources.redhat.com/cygwin/ +- When installing Cygwin, set Default Text File Type to Unix/binary, not DOS/text. + This is really important, otherwise you will get errors when trying to + checkout code using git. +- Packages that you must install or not: + - Required packages: autoconf, bison, curl, flex, gcc, g++, git, gnupg, make, + mingw-zlib, python, zip, unzip. + - Suggested extra packages: diffutils, emacs, openssh, rsync, vim, wget. + - Packages that must not be installed: readline. + +Once you installed Cygwin properly, checkout the code from git as you did +for MacOS or Linux. Make sure to get the same branch, and if possible keep +it as close to the other one as possible: + + $ mkdir ~/my-android-git + $ cd ~/my-android-git + $ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake + $ repo sync + + + +C- Building the Windows SDK +--------------------------- + +Now it's time to build that Windows SDK. You need: +- The path to the MacOS or Linux SDK zip. +- A directory where to place the final SDK. It will also hold some temporary + files. +- The build number will be extracted from the SDK zip filename, but this will + only work if that build number has no underscores in it. It is suggested you + just define SDK_NUMBER (and not BUILD_NUMBER!) on the command line before + invoking the script. + + Note that the "SDK number" is really a free identifier of your choice. It + doesn't need to be strictly a number. As always it is suggested you avoid + too much punctuation and special shell/make characters. Underscores cannot + be used. + + +To summarize, the steps on the command line would be something like this: + + $ mkdir ~/mysdk + $ export SDK_NUMBER=${USER}-`date +%Y%m%d-%H%M%S` + $ cd ~/my-android-git + $ development/build/tools/make_windows_sdk.sh /path/to/macos/or/linux/sdk.zip ~/mysdk + +This will take a while to build some Windows-specific binaries, including the +emulator, unzip the previous zip, rename & replace things and rezip the final +Windows SDK zip file. A typical build time should be around 5-10 minutes. + + + +------------------------------------- +4- Building an ADT plugin for Eclipse +------------------------------------- + +Requirements: +- You can currently only build an ADT plugin for Eclipse under Linux. +- You must have a working version of Eclipse 3.4 "ganymede" RCP installed. +- You need X11 to run Eclipse at least once. +- You need a lot of patience. The trick is to do the initial setup correctly + once, after it's a piece of cake. + + + +A- Pre-requisites +----------------- + +Note for Ubuntu or Debian users: your apt repository probably only has Eclipse +3.2 available and it's probably not suitable to build plugins in the first +place. Forget that and install a working 3.4 manually as described below. + +- Visit http://www.eclipse.org/downloads/ to grab the + "Eclipse for RCP/Plug-in Developers (176 MB)" download for Linux. + 32-bit and 64-bit versions are available, depending on your Linux installation. + + Note: we've always used a 32-bit one, so use the 64-bit one at your own risk. + + Note: Eclipse comes in various editions. Do yourself a favor and just stick + to the RCP for building this plugin. For example the J2EE contains too many + useless features that will get in the way, and the "Java" version lacks some + plugins you need to build other plugins. Please just use the RCP one. + +- Unpack "eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz" in the directory of + your choice, e.g.: + + $ mkdir ~/eclipse-3.4 + $ cd ~/eclipse-3.4 + $ tar xvzf eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz + + This will create an "eclipse" directory in the current directory. + +- Set ECLIPSE_HOME to that "eclipse" directory: + + $ export ECLIPSE_HOME=~/eclipse-3.4/eclipse + + Note: it is important you set ECLIPSE_HOME before starting the build. + Otherwise the build process will try to download and install its own Eclipse + installation in /buildroot, which is probably limited to root. + +- Now, before you can build anything, it is important that you start Eclipse + *manually* once using the same user that you will use to build later. That's + because your Eclipse installation is not finished: Eclipse must be run at + least once to create some files in ~/.eclipse/. So run Eclipse now: + + $ ~/eclipse-3.4/eclipse/eclipse & + + Wait for it load, create a workspace when requested and then simply quit + using the File > Quit menu. That's it. You won't need to run it manually + again. + + + +B- Building ADT +--------------- + +Finally, you have Eclipse, it's installed and it created its own config files, +so now you can build your ADT plugin. To do that you'll change directories to +your git repository and invoke the build script by giving it a destination +directory and an optional build number: + + $ mkdir ~/mysdk + $ cd ~/my-android-git # <-- this is where you did your "repo sync" + $ development/tools/eclipse/scripts/build_server.sh ~/mysdk $USER + +The first argument is the destination directory. It must be absolute. Do not +give a relative destination directory such as "../mysdk". This will make the +Eclipse build fail with a cryptic message: + + BUILD SUCCESSFUL + Total time: 1 minute 5 seconds + **** Package in ../mysdk + Error: Build failed to produce ../mysdk/android-eclipse + Aborting + +The second argument is the build "number". The example used "$USER" but it +really is a free identifier of your choice. It cannot contain spaces nor +periods (dashes are ok.) If the build number is missing, a build timestamp will +be used instead in the filename. + +The build should take something like 5-10 minutes. + + +When the build succeeds, you'll see something like this at the end of the +output: + + ZIP of Update site available at ~/mysdk/android-eclipse-v200903272328.zip +or + ZIP of Update site available at ~/mysdk/android-eclipse-.zip + +When you load the plugin in Eclipse, its feature and plugin name will look like +"com.android.ide.eclipse.adt_0.9.0.v200903272328-.jar". The +internal plugin ID is always composed of the package, the build timestamp and +then your own build identifier (a.k.a. the "build number"), if provided. This +means successive builds with the same build identifier are incremental and +Eclipse will know how to update to more recent ones. + + + +------------- +5- Conclusion +------------- + +This completes the howto guide on building your own SDK and ADT plugin. +Feedback is welcome on the public Android Open Source forums: + http://source.android.com/discuss + +If you are upgrading from a pre-cupcake to a cupcake or later SDK please read +the accompanying document "howto_use_cupcake_sdk.txt". + +-end- + diff --git a/docs/howto_use_cupcake_sdk.txt b/docs/howto_use_cupcake_sdk.txt new file mode 100644 index 000000000..b13123043 --- /dev/null +++ b/docs/howto_use_cupcake_sdk.txt @@ -0,0 +1,371 @@ +Subject: How to build use a Cupcake Android SDK & ADT Eclipse plugin. +Date: 2009/03/27 + + +Table of content: + 0- License + 1- Foreword + 2- Installation steps + 3- For Eclipse users + 4- For Ant users + 5- Targets, AVDs, Emulator changes + 6- Conclusion + + + +---------- +0- License +---------- + + Copyright (C) 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. + + + +----------- +1- Foreword +----------- + +This explains how to use the "new" SDK provided starting with cupcake. +The new SDK has as a different structure than the pre-cupcake ones. + +This means: +- The new SDK does not work with older Eclipse plugins (ADT 0.8) +- The old SDKs (1.0 and 1.1) do NOT work with this Eclipse plugin (ADT 0.9) + + + +---------------------- +2- Installation steps +---------------------- + +First you will need to grab the zip of the SDK for your platform or build it +yourself. Please refer to the accompanying document "howto_build_SDK.txt" if +needed. + +Unzip the SDK somewhere. We'll call that directory "SDK" in command-line +examples. + +Grab the new ADT Eclipse plugin zip file or build it yourself. Keep it +somewhere (no need to unzip). + + + +-------------------- +3- For Eclipse users +-------------------- + + +Below we'll explain how you can upgrade your Eclipse install to the new plugin. +If you already have a working Eclipse installation with a pre-0.9 ADT, +another suggestion is to simply install a new copy of Eclipse and create a +new empty workspace. This is just a precaution. The update process should +be otherwise harmless. + + + +A- Setting up Eclipse +--------------------- + +- You must have Eclipse 3.3 or 3.4. Eclipse 3.2 is not longer supported. + + There are many flavors, or "editions", of Eclipse. To develop, we'd recommend + the "Java" edition. The "RCP" one is totally suitable too. The J2EE one is + probably overkill. + + +- If updating an existing Eclipse, use Help > Software Update and please + uninstall the two features of the previous ADT: the "editors" feature and the + ADT feature itself. + + => If you don't you will get a conflict on editors when installing + the new one. + +- Using Help > Software Update, add a new "archived site", point it to the new + adt.zip (e.g. android-eclipse-.zip), select the "Install" button at + the top right and restart eclipse as needed. + +- After it restarts, please use Window > Preferences > Android and select + the new SDK folder that you unzipped in paragraph 2. + + + +B- Updating older projects +-------------------------- + +If you have pre-0.9 projects in your Eclipse workspace, or if you import them +from your code repository, these projects will fail to build at first. + +First right-click on the project and select "Properties": + +- In the properties, open the Android panel and select the platform to use. + The SDK comes with a 1.5 platform. Select it and close the properties panel. +- Do a clean build. + + +The new plugin creates a "gen" folder in your project where it puts the R.java +and all automatically generated AIDL java files. If you get an error such as: + + "The type R is already defined" + +that means you must check to see if your old R.java or your old auto-generated +AIDL Java files are still present in the "src" folder. If yes, remove them. + +Note: this does not apply to your own hand-crafted parcelable AIDL java files. + +Note: if you want to reuse the project with an older Eclipse ADT install, + simply remove the "gen" folder from the build path of the project. + + +C- New Wizards +-------------- + +The "New Android Project" wizard has been expanded to use the multi-platform +capabilities of the new SDK. + +There is now a "New XML File" wizard that lets you create skeleton XML resource +files for your Android projects. This makes it easier to create a new layout, a +new strings file, etc. + +Both wizard are available via File > New... as well as new icons in the main +icon bar. If you do not see the new icons, you may need to use Window > Reset +Perspective on your Java perspective. + + +Please see step 5 "Emulator changes" below for important details on how to run +the emulator. + + + +---------------- +4- For Ant users +---------------- + + +A- build.xml has changed +------------------------ + +You must re-create your build.xml file. + +First if you had customized your build.xml, make a copy of it: + + $ cd my-project + $ cp build.xml build.xml.old + + +Then use the new "android" tool to create a new build.xml: + + $ SDK/tools/android update project --path /path/to/my-project + +or + + $ cd my-project + $ SDK/tools/android update project --path . + + +A "gen" folder will be created the first time you build and your R.java and +your AIDL Java files will be generated in this "gen" folder. You MUST remove +the old R.java and old auto-generated AIDL java files manually. (Note: this +does not apply to your own hand-crafted parcelabe AIDL java files.) + + +B- Where is activitycreator? +---------------------------- + +Note that the "activitycreator" tool has been replaced by the new "android" +tool too. Example of how to create a new Ant project: + + $ SDK/tools/android create project --path /path/to/my/project --name ProjectName + --package com.mycompany.myapp --activity MyActivityClass + --target 1 --mode activity + + +Please see paragraph 5 below for important details on how to run the emulator +and the meaning of that "--target 1" parameter. + + + +---------------------------------- +5- Targets, AVDs, Emulator changes +---------------------------------- + +This applies to BOTH Eclipse and Ant users. + +One major change with the emulator is that now you must pre-create an "Android +Virtual Device" (a.k.a "AVD") before you run the emulator. + + + +A- What is an AVD and why do I need one? +---------------------------------------- + +What is an "AVD"? If you forget, just run: + + $ SDK/tools/emulator -help-virtual-device + + An Android Virtual Device (AVD) models a single virtual device running the + Android platform that has, at least, its own kernel, system image and data + partition. + +There is a lot more explanation given by the emulator. Please run the help +command given above to read the rest. + +The bottom line is that you can create many emulator configurations, or "AVDs", +each with their own system image and most important each with their own user +data and SD card data. Then you tell Eclipse or the emulator which one to use +to debug or run your applications. + + +Note for Eclipse users: eventually there will be a user interface to do all of +these operations. For right now, please use the command line interface. + + +B- Listing targets and AVDs +--------------------------- + +There is a new tool called "android" in the SDK that lets you know which +"target" and AVDs you can use. + +A target is a specific version of Android that you can use. By default the SDK +comes with an "Android 1.5" target, codenamed "cupcake". In the future there +will be more versions of Android to use, e.g. "Android 2.0" or specific add-ons +provided by hardware manufacturers. When you want to run an emulator, you need +to specify a given flavor of Android: this is the "target". + + +To learn about available targets in your SDK, use this command: + + $ SDK/tools/android list targets + +This will give you an output such as: + + Available Android targets: + [1] Android 1.5 + API level: 3 + Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P + +Note the "[1]". Later you will need to reference this as "--target 1" on the +command line. + + +Similarly you can list the available AVDs: + + $ SDK/tools/android list avds + +Which might output something as: + + Available Android Virtual Devices: + Name: my_avd + Path: C:\Users\\.android\avd\my_avd.avd + Target: Android 1.5 (API level 3) + Skin: 320x480 + Sdcard: 16M + + + +C- Creating an AVD +------------------ + +To create a configuration: + + $ SDK/tools/android create avd --name my_avd_name --target 1 + + +where "target 1" is the index of a target listed by "android list targets". + +The AVD name is purely an identifier used to refer to the AVD later. +Since it is used as directory name, please avoid using shell or path specific +characters. + +To learn the various options available when creating an AVD, simply type: + + $ SDK/tools/android create avd + +The android tool will automatically print an explanation of required arguments. + + + +D- Invoking an AVD from the command-line +---------------------------------------- + +To use this AVD in the emulator from the command-line, type: + + $ SDK/tools/emulator @my_avd_name + + +For more options, please consult the emulator help: + + $ SDK/tools/emulator -help-virtual-device + + + +E- Invoking an AVD from Eclipse +------------------------------- + +By default Android projects in Eclipse have an "automatic target" mode. +In this mode, when a project is deployed in debug or run, it checks: +- If there's one running device or emulator, this is used for deployment. +- If there's more than one running device or emulator, a "device chooser" is + shown to let the user select which one to use. +- If there are no running devices or emulators, ADT looks at available AVDs. + If one matches the project configuration (e.g. same API level), it is + automatically used. + +Alternatively you can edit the "launch configuration" on your Android project +in Eclipse by selecting the menu Run > Run Configurations. In the "target" tab +of the configuration, you can choose: + +- Manual or automatic targetting mode. + + - Manual means to always present the device chooser. + - Automatic is the behavior explained above. + +- In automatic mode, which AVD is preferred. If none is selected, the first + suitable is used. + + +F- AVD concurrency +------------------ + +You can no longer run several emulators at the same time on the same +configuration. + +Before this used to put the second or more emulators in a transient read-only +mode that would not save user data. + +Now you just need to create as many AVDs as you want to run emulators. + +For example if you are working on a client/server application for Android, you +could create a "client" AVD and a "server" AVD then run them both at once. The +emulator window will show you the AVD name so that you know which one is which. + +Example: + + $ SDK/tools/android create avd --name client --target 1 --sdcard 16M --skin HVGA + $ SDK/tools/android create avd --name server --target 1 --sdcard 32M --skin HVGA-P + $ SDK/tools/emulator @server & + $ SDK/tools/emulator @client & + + + +------------- +6- Conclusion +------------- + +This completes the howto guide on how to use the new Cupcake SDK. +Feedback is welcome on the public Android Open Source forums: + http://source.android.com/discuss + +-end- + diff --git a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java index e0a4c76b0..d7ef9d632 100644 --- a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java +++ b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java @@ -89,7 +89,10 @@ public class ExampleAppWidgetConfigure extends Activity { saveTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId, mAppWidgetPrefix.getText().toString()); - setResult(RESULT_OK); + // Make sure we pass back the original appWidgetId + Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); + setResult(RESULT_OK, resultValue); finish(); } }; diff --git a/testrunner/Android.mk b/testrunner/Android.mk index 93c092841..b6bde553b 100644 --- a/testrunner/Android.mk +++ b/testrunner/Android.mk @@ -10,7 +10,7 @@ LOCAL_PATH := $(call my-dir) ######################## include $(CLEAR_VARS) -LOCAL_MODULE := tests.xml +LOCAL_MODULE := test_defs.xml LOCAL_MODULE_TAGS := tests LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(local_target_dir) diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py index fb304df9d..ad1b2c94f 100755 --- a/testrunner/adb_interface.py +++ b/testrunner/adb_interface.py @@ -297,8 +297,7 @@ class AdbInterface: WaitForResponseTimedOutError if wait_time elapses and pm still does not respond. """ - logger.Log("Waiting for device package manager for %s seconds..." - % wait_time) + logger.Log("Waiting for device package manager...") self.SendCommand("wait-for-device") # Now the device is there, but may not be running. # Query the package manager with a basic command @@ -315,7 +314,8 @@ class AdbInterface: time.sleep(wait_period) attempts += 1 if not pm_found: - raise errors.WaitForResponseTimedOutError + raise errors.WaitForResponseTimedOutError( + "Package manager did not respond after %s seconds" % wait_time) def Sync(self, retry_count=3): """Perform a adb sync. @@ -331,13 +331,12 @@ class AdbInterface: output = self.SendCommand("sync", retry_count=retry_count) if "Read-only file system" in output: logger.SilentLog(output) - logger.Log("adb sync failed due to read only fs, retrying") + logger.Log("Remounting read-only filesystem") self.SendCommand("remount") output = self.SendCommand("sync", retry_count=retry_count) if "No space left on device" in output: logger.SilentLog(output) - logger.Log("adb sync failed due to no space on device, trying shell" + - " start/stop") + logger.Log("Restarting device runtime") self.SendShellCommand("stop", retry_count=retry_count) output = self.SendCommand("sync", retry_count=retry_count) self.SendShellCommand("start", retry_count=retry_count) @@ -345,3 +344,15 @@ class AdbInterface: logger.SilentLog(output) self.WaitForDevicePm() return output + + def IsDevicePresent(self): + """Check if targeted device is present. + + Returns: + True if device is present, False otherwise. + """ + output = self.SendShellCommand("ls", retry_count=0) + if output.startswith("error:"): + return False + else: + return True diff --git a/testrunner/coverage.py b/testrunner/coverage.py index 507c5c79d..39a2ceb21 100755 --- a/testrunner/coverage.py +++ b/testrunner/coverage.py @@ -70,6 +70,27 @@ class CoverageGenerator(object): def EnableCoverageBuild(self): """Enable building an Android target with code coverage instrumentation.""" os.environ[self._EMMA_BUILD_FLAG] = "true" + #TODO: can emma.jar automagically be added to bootclasspath here? + + def TestDeviceCoverageSupport(self): + """Check if device has support for generating code coverage metrics. + + Currently this will check if the emma.jar file is on the device's boot + classpath. + + Returns: + True if device can support code coverage. False otherwise. + """ + output = self._adb.SendShellCommand("cat init.rc | grep BOOTCLASSPATH | " + "grep emma.jar") + if len(output) > 0: + return True + else: + logger.Log("Error: Targeted device does not have emma.jar on its " + "BOOTCLASSPATH.") + logger.Log("Modify the BOOTCLASSPATH entry in system/core/rootdir/init.rc" + " to add emma.jar") + return False def ExtractReport(self, test_suite, device_coverage_path=_DEVICE_COVERAGE_PATH, diff --git a/testrunner/logger.py b/testrunner/logger.py index 762c89311..61463a198 100755 --- a/testrunner/logger.py +++ b/testrunner/logger.py @@ -25,6 +25,7 @@ import datetime _LOG_FILE = None _verbose = False +_log_time = True def Init(log_file_path): """Set the path to the log file""" @@ -57,8 +58,13 @@ def _WriteLog(msg): def _PrependTimeStamp(log_string): """Returns the log_string prepended with current timestamp """ - return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"), - log_string) + global _log_time + if _log_time: + return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"), + log_string) + else: + # timestamp logging disabled + return log_string def SilentLog(new_str): """Silently log new_str. Unless verbose mode is enabled, will log new_str @@ -77,7 +83,12 @@ def SetVerbose(new_verbose=True): """ Enable or disable verbose logging""" global _verbose _verbose = new_verbose - + +def SetTimestampLogging(new_timestamp=True): + """ Enable or disable outputting a timestamp with each log entry""" + global _log_time + _log_time = new_timestamp + def main(): pass diff --git a/testrunner/runtest.py b/testrunner/runtest.py index bf5bb2232..8d3659130 100755 --- a/testrunner/runtest.py +++ b/testrunner/runtest.py @@ -41,23 +41,27 @@ class TestRunner(object): # file path to android core platform tests, relative to android build root # TODO move these test data files to another directory - _CORE_TEST_PATH = os.path.join("development", "testrunner", "tests.xml") + _CORE_TEST_PATH = os.path.join("development", "testrunner", "test_defs.xml") # vendor glob file path patterns to tests, relative to android # build root _VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo", - "tests.xml") + "test_defs.xml") _RUNTEST_USAGE = ( "usage: runtest.py [options] short-test-name[s]\n\n" "The runtest script works in two ways. You can query it " "for a list of tests, or you can launch one or more tests.") + def __init__(self): + # disable logging of timestamp + logger.SetTimestampLogging(False) + def _ProcessOptions(self): """Processes command-line options.""" # TODO error messages on once-only or mutually-exclusive options. user_test_default = os.path.join(os.environ.get("HOME"), ".android", - "tests.xml") + "test_defs.xml") parser = optparse.OptionParser(usage=self._RUNTEST_USAGE) @@ -149,7 +153,7 @@ class TestRunner(object): try: known_tests = test_defs.TestDefinitions() known_tests.Parse(core_test_path) - # read all /vendor/*/tests/testinfo/tests.xml paths + # read all /vendor/*/tests/testinfo/test_defs.xml paths vendor_tests_pattern = os.path.join(self._root_path, self._VENDOR_TEST_PATH) test_file_paths = glob.glob(vendor_tests_pattern) @@ -178,10 +182,14 @@ class TestRunner(object): self._coverage_gen.EnableCoverageBuild() self._AddBuildTarget(self._coverage_gen.GetEmmaBuildPath(), target_set) target_build_string = " ".join(list(target_set)) - logger.Log("Building %s" % target_build_string) + logger.Log("mmm %s" % target_build_string) cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files' % (target_build_string, self._root_path) - if not self._options.preview: + if self._options.preview: + # in preview mode, just display to the user what command would have been + # run + logger.Log("adb sync") + else: run_command.RunCommand(cmd, return_output=False) logger.Log("Syncing to device...") self._adb.Sync() @@ -259,6 +267,10 @@ class TestRunner(object): self._DumpTests() return + if not self._adb.IsDevicePresent(): + logger.Log("Error: specified device cannot be found") + return + if not self._options.skip_build: self._DoBuild() diff --git a/testrunner/tests.xml b/testrunner/test_defs.xml similarity index 100% rename from testrunner/tests.xml rename to testrunner/test_defs.xml diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java index 999542634..9dd1d1640 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java @@ -49,6 +49,7 @@ public class RemoteAndroidTestRunner { private static final String LOG_ARG_NAME = "log"; private static final String DEBUG_ARG_NAME = "debug"; private static final String COVERAGE_ARG_NAME = "coverage"; + private static final String PACKAGE_ARG_NAME = "package"; /** * Creates a remote Android test runner. @@ -145,6 +146,16 @@ public class RemoteAndroidTestRunner { setClassName(className + METHOD_SEPARATOR + testName); } + /** + * Sets to run all tests in specified package + * Must be called before 'run'. + * + * @param packageName fully qualified package name (eg x.y.z) + */ + public void setTestPackageName(String packageName) { + addInstrumentationArg(PACKAGE_ARG_NAME, packageName); + } + /** * Adds a argument to include in instrumentation command. *

diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java index 6a653ad05..864e219fc 100644 --- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java +++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java @@ -17,16 +17,17 @@ package com.android.ddmlib.testrunner; import com.android.ddmlib.Client; -import com.android.ddmlib.Device.DeviceState; import com.android.ddmlib.FileListingService; import com.android.ddmlib.IDevice; import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.log.LogReceiver; import com.android.ddmlib.RawImage; import com.android.ddmlib.SyncService; +import com.android.ddmlib.Device.DeviceState; +import com.android.ddmlib.log.LogReceiver; import java.io.IOException; import java.util.Map; + import junit.framework.TestCase; /** @@ -80,6 +81,17 @@ public class RemoteAndroidTestRunnerTest extends TestCase { testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand()); } + /** + * Test the building of the instrumentation runner command with test package set. + */ + public void testRunWithPackage() { + final String packageName = "foo.test"; + mRunner.setTestPackageName(packageName); + mRunner.run(new EmptyListener()); + assertStringsEquals(String.format("am instrument -w -r -e package %s %s/%s", packageName, + TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand()); + } + /** * Test the building of the instrumentation runner command with extra argument added. */ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 0ec97aa38..8092f3a5e 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -43,7 +43,8 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.jdt.junit, org.eclipse.jdt.junit.runtime, org.eclipse.ltk.core.refactoring, - org.eclipse.ltk.ui.refactoring + org.eclipse.ltk.ui.refactoring, + org.eclipse.core.expressions Eclipse-LazyStart: true Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", @@ -52,6 +53,7 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse. com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.adt.ui;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.common;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.common.project;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.common.resources;x-friends:="com.android.ide.eclipse.tests", @@ -77,7 +79,6 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse. com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests" diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 2c1394cd2..35ceba76e 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -551,7 +551,7 @@ point="org.eclipse.debug.ui.launchShortcuts"> @@ -563,7 +563,7 @@ - + @@ -595,4 +595,14 @@ id="com.android.ide.eclipse.adt.refactoring.extract.string"> + + + + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index 42db64a61..4a7a002e2 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -29,8 +29,8 @@ import com.android.ide.eclipse.adt.sdk.AndroidTargetParser; import com.android.ide.eclipse.adt.sdk.LoadStatus; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.ui.EclipseUiHelper; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.EclipseUiHelper; import com.android.ide.eclipse.common.SdkStatsHelper; import com.android.ide.eclipse.common.StreamHelper; import com.android.ide.eclipse.common.project.BaseProjectHelper; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java index 747fcfe5c..9bcc63d06 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java @@ -24,7 +24,6 @@ import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; @@ -39,18 +38,15 @@ import org.eclipse.jdt.launching.VMRunnerConfiguration; */ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { - private String mTestPackage; - private String mRunner; + private final AndroidJUnitLaunchInfo mLaunchInfo; /** * Creates a AndroidJUnitLaunchAction. * - * @param testPackage the Android application package that contains the tests to run - * @param runner the InstrumentationTestRunner that will execute the tests + * @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run */ - public AndroidJUnitLaunchAction(String testPackage, String runner) { - mTestPackage = testPackage; - mRunner = runner; + public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) { + mLaunchInfo = launchInfo; } /** @@ -60,17 +56,21 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice) */ public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) { - String msg = String.format("Launching instrumentation %s on device %s", mRunner, - device.getSerialNumber()); + String msg = String.format("Launching instrumentation %s on device %s", + mLaunchInfo.getRunner(), device.getSerialNumber()); AdtPlugin.printToConsole(info.getProject(), msg); try { - JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(info, device); + mLaunchInfo.setDebugMode(info.isDebugMode()); + mLaunchInfo.setDevice(info.getDevice()); + mLaunchInfo.setLaunch(info.getLaunch()); + JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo); final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE : ILaunchManager.RUN_MODE; + junitDelegate.launch(info.getLaunch().getLaunchConfiguration(), mode, info.getLaunch(), info.getMonitor()); - + // TODO: need to add AMReceiver-type functionality somewhere } catch (CoreException e) { AdtPlugin.printErrorToConsole(info.getProject(), "Failed to launch test"); @@ -82,20 +82,18 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * {@inheritDoc} */ public String getLaunchDescription() { - return String.format("%s JUnit launch", mRunner); + return String.format("%s JUnit launch", mLaunchInfo.getRunner()); } /** * Extends the JDT JUnit launch delegate to allow for JUnit UI reuse. */ - private class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate { + private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate { - private IDevice mDevice; - private DelayedLaunchInfo mLaunchInfo; + private AndroidJUnitLaunchInfo mLaunchInfo; - public JUnitLaunchDelegate(DelayedLaunchInfo info, IDevice device) { - mLaunchInfo = info; - mDevice = device; + public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) { + mLaunchInfo = launchInfo; } /* (non-Javadoc) @@ -110,34 +108,28 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws CoreException * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration) */ @Override - public String verifyMainTypeName(ILaunchConfiguration configuration) throws CoreException { + public String verifyMainTypeName(ILaunchConfiguration configuration) { return "com.android.ide.eclipse.adt.junit.internal.runner.RemoteAndroidTestRunner"; //$NON-NLS-1$ } /** * Overrides parent to return a VM Runner implementation which launches a thread, rather * than a separate VM process - * @throws CoreException */ @Override - public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) - throws CoreException { - return new VMTestRunner(new AndroidJUnitLaunchInfo(mLaunchInfo.getProject(), - mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice)); + public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) { + return new VMTestRunner(mLaunchInfo); } /** * {@inheritDoc} - * @throws CoreException * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String) */ @Override - public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) - throws CoreException { + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) { return mLaunchInfo.getLaunch(); } } @@ -161,7 +153,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { IProgressMonitor monitor) throws CoreException { TestRunnerProcess runnerProcess = - new TestRunnerProcess(config, launch, mJUnitInfo); + new TestRunnerProcess(config, mJUnitInfo); runnerProcess.start(); launch.addProcess(runnerProcess); } @@ -173,15 +165,12 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { private static class TestRunnerProcess extends Thread implements IProcess { private final VMRunnerConfiguration mRunConfig; - private final ILaunch mLaunch; private final AndroidJUnitLaunchInfo mJUnitInfo; private RemoteAdtTestRunner mTestRunner = null; private boolean mIsTerminated = false; - TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch, - AndroidJUnitLaunchInfo info) { + TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) { mRunConfig = runConfig; - mLaunch = launch; mJUnitInfo = info; } @@ -194,10 +183,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws DebugException * @see org.eclipse.debug.core.model.IProcess#getExitValue() */ - public int getExitValue() throws DebugException { + public int getExitValue() { return 0; } @@ -205,14 +193,14 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * @see org.eclipse.debug.core.model.IProcess#getLabel() */ public String getLabel() { - return mLaunch.getLaunchMode(); + return mJUnitInfo.getLaunch().getLaunchMode(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getLaunch() */ public ILaunch getLaunch() { - return mLaunch; + return mJUnitInfo.getLaunch(); } /* (non-Javadoc) @@ -254,10 +242,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws DebugException * @see org.eclipse.debug.core.model.ITerminate#terminate() */ - public void terminate() throws DebugException { + public void terminate() { if (mTestRunner != null) { mTestRunner.terminate(); } @@ -274,3 +261,4 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { } } } + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java index fa8e4b01a..543daf06f 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java @@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration; import com.android.ide.eclipse.adt.launch.AndroidLaunchController; import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction; import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate; +import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; @@ -32,8 +33,11 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants; import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry; +import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; /** * Run configuration that can execute JUnit tests on an Android platform. @@ -47,6 +51,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { /** Launch config attribute that stores instrumentation runner. */ static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$ + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ @Override @@ -55,7 +60,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { AndroidLaunchConfiguration config, AndroidLaunchController controller, IFile applicationPackage, AndroidManifestParser manifestParser) { - String testPackage = manifestParser.getPackage(); + String appPackage = manifestParser.getPackage(); String runner = getRunner(project, configuration, manifestParser); if (runner == null) { AdtPlugin.displayError("Android Launch", @@ -63,14 +68,62 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { androidLaunch.stopLaunch(); return; } + AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project, appPackage, + runner); + junitLaunchInfo.setTestClass(getTestClass(configuration)); + junitLaunchInfo.setTestPackage(getTestPackage(configuration)); + junitLaunchInfo.setTestMethod(getTestMethod(configuration)); - IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(testPackage, runner); + IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo); controller.launch(project, mode, applicationPackage, manifestParser.getPackage(), manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(), junitLaunch, config, androidLaunch, monitor); } + /** + * Returns the test package stored in the launch configuration, or null if not + * specified. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from + * @return the test package or null. + */ + private String getTestPackage(ILaunchConfiguration configuration) { + // try to retrieve a package name from the JUnit container attribute + String containerHandle = getStringLaunchAttribute( + JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration); + if (containerHandle != null && containerHandle.length() > 0) { + IJavaElement element = JavaCore.create(containerHandle); + // containerHandle could be a IProject, check if its a java package + if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { + return element.getElementName(); + } + } + return null; + } + + /** + * Returns the test class stored in the launch configuration. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from + * @return the test class. null if not specified. + */ + private String getTestClass(ILaunchConfiguration configuration) { + return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, + configuration); + } + + /** + * Returns the test method stored in the launch configuration. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from + * @return the test method. null if not specified. + */ + private String getTestMethod(ILaunchConfiguration configuration) { + return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME, + configuration); + } + /** * Gets a instrumentation runner for the launch. *

@@ -114,11 +167,29 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { } private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException { - String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING); - if (runner.length() < 1) { - return null; + return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration); + } + + /** + * Helper method to retrieve a string attribute from the launch configuration + * + * @param attributeName name of the launch attribute + * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from + * @return the attribute's value. null if not found. + */ + private String getStringLaunchAttribute(String attributeName, + ILaunchConfiguration configuration) { + try { + String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING); + if (attrValue.length() < 1) { + return null; + } + return attrValue; + } catch (CoreException e) { + AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s", //$NON-NLS-1$ + attributeName)); } - return runner; + return null; } /** diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java index eb5748269..584d45eb1 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.MainLaunchConfigTab; import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import org.eclipse.core.resources.IProject; @@ -241,7 +242,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat private void createTestContainerSelectionGroup(Composite comp) { mTestContainerRadioButton = new Button(comp, SWT.RADIO); mTestContainerRadioButton.setText( - JUnitMessages.JUnitLaunchConfigurationTab_label_containerTest); + "Run all tests in the selected project, or package"); GridData gd = new GridData(); gd.horizontalSpan = 3; mTestContainerRadioButton.setLayoutData(gd); @@ -249,12 +250,12 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat public void widgetSelected(SelectionEvent e) { if (mTestContainerRadioButton.getSelection()) { testModeChanged(); - } + } } public void widgetDefaultSelected(SelectionEvent e) { } }); - + mContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalIndent = 25; @@ -265,7 +266,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat updateLaunchConfigurationDialog(); } }); - + mContainerSearchButton = new Button(comp, SWT.PUSH); mContainerSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search); mContainerSearchButton.addSelectionListener(new SelectionAdapter() { @@ -821,7 +822,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat @SuppressWarnings("unchecked") private IJavaElement chooseContainer(IJavaElement initElement) { - Class[] acceptedClasses = new Class[] { IPackageFragmentRoot.class, IJavaProject.class, + Class[] acceptedClasses = new Class[] { IJavaProject.class, IPackageFragment.class }; TypedElementSelectionValidator validator = new TypedElementSelectionValidator( acceptedClasses, false) { @@ -839,7 +840,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat if (element instanceof IPackageFragmentRoot && ((IPackageFragmentRoot) element).isArchive()) { return false; - } + } try { if (element instanceof IPackageFragment && !((IPackageFragment) element).hasChildren()) { @@ -852,7 +853,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat } }; - StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider(); + AndroidJavaElementContentProvider provider = new AndroidJavaElementContentProvider(); ILabelProvider labelProvider = new JavaElementLabelProvider( JavaElementLabelProvider.SHOW_DEFAULT); ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), @@ -974,4 +975,23 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat mInstrumentations = null; mInstrumentationCombo.removeAll(); } + + /** + * Overrides the {@link StandardJavaElementContentProvider} to only display Android projects + */ + private static class AndroidJavaElementContentProvider + extends StandardJavaElementContentProvider { + + /** + * Override parent to return only Android projects if at the root. Otherwise, use parent + * functionality. + */ + @Override + public Object[] getChildren(Object element) { + if (element instanceof IJavaModel) { + return BaseProjectHelper.getAndroidProjects((IJavaModel) element); + } + return super.getChildren(element); + } + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java new file mode 100644 index 000000000..eadafee77 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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. + */ +package com.android.ide.eclipse.adt.launch.junit; + +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.junit.util.TestSearchEngine; + +/** + * A {@link PropertyTester} that checks if selected elements can be run as Android + * JUnit tests. + *

+ * Based on org.eclipse.jdt.internal.junit.JUnitPropertyTester. The only substantial difference in + * this implementation is source folders cannot be run as Android JUnit. + */ +@SuppressWarnings("restriction") +public class AndroidJUnitPropertyTester extends PropertyTester { + private static final String PROPERTY_IS_TEST = "isTest"; //$NON-NLS-1$ + + private static final String PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST = "canLaunchAsJUnit"; //$NON-NLS-1$ + + /* (non-Javadoc) + * @see org.eclipse.jdt.internal.corext.refactoring.participants.properties.IPropertyEvaluator#test(java.lang.Object, java.lang.String, java.lang.String) + */ + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (!(receiver instanceof IAdaptable)) { + final String elementName = (receiver == null ? "null" : //$NON-NLS-1$ + receiver.getClass().getName()); + throw new IllegalArgumentException( + String.format("Element must be of type IAdaptable, is %s", //$NON-NLS-1$ + elementName)); + } + + IJavaElement element; + if (receiver instanceof IJavaElement) { + element = (IJavaElement) receiver; + } else if (receiver instanceof IResource) { + element = JavaCore.create((IResource) receiver); + if (element == null) { + return false; + } + } else { // is IAdaptable + element= (IJavaElement) ((IAdaptable) receiver).getAdapter(IJavaElement.class); + if (element == null) { + IResource resource = (IResource) ((IAdaptable) receiver).getAdapter( + IResource.class); + element = JavaCore.create(resource); + if (element == null) { + return false; + } + } + } + if (PROPERTY_IS_TEST.equals(property)) { + return isJUnitTest(element); + } else if (PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST.equals(property)) { + return canLaunchAsJUnitTest(element); + } + throw new IllegalArgumentException( + String.format("Unknown test property '%s'", property)); //$NON-NLS-1$ + } + + private boolean canLaunchAsJUnitTest(IJavaElement element) { + try { + switch (element.getElementType()) { + case IJavaElement.JAVA_PROJECT: + return true; // can run, let JDT detect if there are tests + case IJavaElement.PACKAGE_FRAGMENT_ROOT: + return false; // not supported by Android test runner + case IJavaElement.PACKAGE_FRAGMENT: + return ((IPackageFragment) element).hasChildren(); + case IJavaElement.COMPILATION_UNIT: + case IJavaElement.CLASS_FILE: + case IJavaElement.TYPE: + case IJavaElement.METHOD: + return isJUnitTest(element); + default: + return false; + } + } catch (JavaModelException e) { + return false; + } + } + + /** + * Return whether the target resource is a JUnit test. + */ + private boolean isJUnitTest(IJavaElement element) { + try { + IType testType = null; + if (element instanceof ICompilationUnit) { + testType = (((ICompilationUnit) element)).findPrimaryType(); + } else if (element instanceof IClassFile) { + testType = (((IClassFile) element)).getType(); + } else if (element instanceof IType) { + testType = (IType) element; + } else if (element instanceof IMember) { + testType = ((IMember) element).getDeclaringType(); + } + if (testType != null && testType.exists()) { + return TestSearchEngine.isTestOrTestSuite(testType); + } + } catch (CoreException e) { + // ignore, return false + } + return false; + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java index 89cad97ae..8ac80cab5 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java @@ -15,35 +15,38 @@ */ package com.android.ide.eclipse.adt.launch.junit.runtime; -import org.eclipse.core.resources.IProject; - import com.android.ddmlib.IDevice; +import org.eclipse.core.resources.IProject; +import org.eclipse.debug.core.ILaunch; + /** * Contains info about Android JUnit launch */ public class AndroidJUnitLaunchInfo { private final IProject mProject; - private final String mTestPackage; + private final String mAppPackage; private final String mRunner; - private final boolean mDebugMode; - private final IDevice mDevice; - - public AndroidJUnitLaunchInfo(IProject project, String testPackage, String runner, - boolean debugMode, IDevice device) { + + private boolean mDebugMode = false; + private IDevice mDevice = null; + private String mTestPackage = null; + private String mTestClass = null; + private String mTestMethod = null; + private ILaunch mLaunch = null; + + public AndroidJUnitLaunchInfo(IProject project, String appPackage, String runner) { mProject = project; - mTestPackage = testPackage; + mAppPackage = appPackage; mRunner = runner; - mDebugMode = debugMode; - mDevice = device; } - + public IProject getProject() { return mProject; } - public String getTestPackage() { - return mTestPackage; + public String getAppPackage() { + return mAppPackage; } public String getRunner() { @@ -53,8 +56,80 @@ public class AndroidJUnitLaunchInfo { public boolean isDebugMode() { return mDebugMode; } + + public void setDebugMode(boolean debugMode) { + mDebugMode = debugMode; + } public IDevice getDevice() { return mDevice; } + + public void setDevice(IDevice device) { + mDevice = device; + } + + /** + * Specify to run all tests within given package. + * + * @param testPackage fully qualified java package + */ + public void setTestPackage(String testPackage) { + mTestPackage = testPackage; + } + + /** + * Return the package of tests to run. + * + * @return fully qualified java package. null if not specified. + */ + public String getTestPackage() { + return mTestPackage; + } + + /** + * Sets the test class to run. + * + * @param testClass fully qualfied test class to run + * Expected format: x.y.x.testclass + */ + public void setTestClass(String testClass) { + mTestClass = testClass; + } + + /** + * Returns the test class to run. + * + * @return fully qualfied test class to run. + * null if not specified. + */ + public String getTestClass() { + return mTestClass; + } + + /** + * Sets the test method to run. testClass must also be set. + * + * @param testMethod test method to run + */ + public void setTestMethod(String testMethod) { + mTestMethod = testMethod; + } + + /** + * Returns the test method to run. + * + * @return test method to run. null if not specified. + */ + public String getTestMethod() { + return mTestMethod; + } + + public ILaunch getLaunch() { + return mLaunch; + } + + public void setLaunch(ILaunch launch) { + mLaunch = launch; + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java index 0a6a3daee..962d76133 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java @@ -69,8 +69,9 @@ public class RemoteAdtTestRunner extends RemoteTestRunner { * executing the tests, and send it back to JDT JUnit. The second is the actual test execution, * whose results will be communicated back in real-time to JDT JUnit. * - * @param testClassNames array of fully qualified test class names to execute. Cannot be empty. - * @param testName test to execute. If null, will be ignored. + * @param testClassNames ignored - the AndroidJUnitLaunchInfo will be used to determine which + * tests to run. + * @param testName ignored * @param execution used to report test progress */ @Override @@ -78,16 +79,21 @@ public class RemoteAdtTestRunner extends RemoteTestRunner { // hold onto this execution reference so it can be used to report test progress mExecution = execution; - RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getTestPackage(), + RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getAppPackage(), mLaunchInfo.getRunner(), mLaunchInfo.getDevice()); - if (testClassNames != null && testClassNames.length > 0) { - if (testName != null) { - runner.setMethodName(testClassNames[0], testName); - } else { - runner.setClassNames(testClassNames); - } + if (mLaunchInfo.getTestClass() != null) { + if (mLaunchInfo.getTestMethod() != null) { + runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod()); + } else { + runner.setClassName(mLaunchInfo.getTestClass()); + } } + + if (mLaunchInfo.getTestPackage() != null) { + runner.setTestPackageName(mLaunchInfo.getTestPackage()); + } + // set log only to first collect test case info, so Eclipse has correct test case count/ // tree info runner.setLogOnly(true); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java index 5ffeeb05f..9822b32e7 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java @@ -17,10 +17,10 @@ package com.android.ide.eclipse.adt.refactorings.extractstring; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector; import com.android.sdklib.SdkConstants; import org.eclipse.core.resources.IFolder; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java index ba0b56803..40b3f76ec 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java @@ -100,7 +100,7 @@ public class Sdk implements IProjectListener { ISdkLog log = new ISdkLog() { public void error(Throwable throwable, String errorFormat, Object... arg) { if (errorFormat != null) { - logMessages.add(String.format(errorFormat, arg)); + logMessages.add(String.format("Error: " + errorFormat, arg)); } if (throwable != null) { @@ -109,7 +109,7 @@ public class Sdk implements IProjectListener { } public void warning(String warningFormat, Object... arg) { - logMessages.add(String.format(warningFormat, arg)); + logMessages.add(String.format("Warning: " + warningFormat, arg)); } public void printf(String msgFormat, Object... arg) { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java similarity index 99% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java index 4a05b1ee5..651d1e016 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.ui; import com.android.ide.eclipse.editors.resources.configurations.CountryCodeQualifier; import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java similarity index 98% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java index 6dc856233..55878bf28 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.common; +package com.android.ide.eclipse.adt.ui; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java similarity index 99% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java index 6913ce07d..e141396c5 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.ui; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.resources.IResourceRepository; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java similarity index 99% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java index 60a627b52..4290f6b79 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.ui; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java similarity index 98% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java index 7c6a539ca..3792fe312 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.ui; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java similarity index 99% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java index 024d08433..f7c26346a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.ui; import com.android.ide.eclipse.common.resources.IIdResourceItem; import com.android.ide.eclipse.common.resources.ResourceItem; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java index f3cbfa221..f85050444 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java @@ -21,6 +21,8 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; @@ -31,8 +33,6 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier; import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java index 12d49fe27..e22382034 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java @@ -22,6 +22,10 @@ import com.android.ide.eclipse.adt.sdk.LoadStatus; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DensityVerifier; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DimensionVerifier; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.LanguageRegionVerifier; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.MobileCodeVerifier; import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions; @@ -55,10 +59,6 @@ import com.android.ide.eclipse.editors.ui.tree.CopyCutAction; import com.android.ide.eclipse.editors.ui.tree.PasteAction; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DensityVerifier; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DimensionVerifier; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.LanguageRegionVerifier; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.MobileCodeVerifier; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.ILayoutResult; import com.android.layoutlib.api.IProjectCallback; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java index c4a8f5cc1..69402381a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java @@ -16,12 +16,12 @@ package com.android.ide.eclipse.editors.layout; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector; +import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier; import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector; -import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TrayDialog; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java index f3a5113a9..7bed7ce6a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java @@ -18,8 +18,8 @@ package com.android.ide.eclipse.editors.layout; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.ui.EclipseUiHelper; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.EclipseUiHelper; import com.android.ide.eclipse.editors.AndroidEditor; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java index 536e9020b..4e0e35f06 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java @@ -17,7 +17,7 @@ package com.android.ide.eclipse.editors.layout; -import com.android.ide.eclipse.common.EclipseUiHelper; +import com.android.ide.eclipse.adt.ui.EclipseUiHelper; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.layout.parts.UiDocumentTreeEditPart; import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPart; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java index d1d88917f..b61eddc4d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java @@ -17,14 +17,14 @@ package com.android.ide.eclipse.editors.resources.explorer; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.ui.ResourceContentProvider; +import com.android.ide.eclipse.adt.ui.ResourceLabelProvider; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.resources.manager.ProjectResourceItem; import com.android.ide.eclipse.editors.resources.manager.ProjectResources; import com.android.ide.eclipse.editors.resources.manager.ResourceFile; import com.android.ide.eclipse.editors.resources.manager.ResourceManager; import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener; -import com.android.ide.eclipse.editors.wizards.ResourceContentProvider; -import com.android.ide.eclipse.editors.wizards.ResourceLabelProvider; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java index 32cac9f2b..654e792cc 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java @@ -17,6 +17,8 @@ package com.android.ide.eclipse.editors.uimodel; import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.ui.ReferenceChooserDialog; +import com.android.ide.eclipse.adt.ui.ResourceChooser; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; import com.android.ide.eclipse.common.resources.ResourceType; @@ -26,8 +28,6 @@ import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; import com.android.ide.eclipse.editors.resources.manager.ResourceManager; import com.android.ide.eclipse.editors.ui.SectionHelper; -import com.android.ide.eclipse.editors.wizards.ReferenceChooserDialog; -import com.android.ide.eclipse.editors.wizards.ResourceChooser; import org.eclipse.core.resources.IProject; import org.eclipse.jface.window.Window; diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java index cbe702872..8a8a677da 100755 --- a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java +++ b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java @@ -45,6 +45,10 @@ public class MakeBinaryDictionary { public static final String TAG_WORD = "w"; public static final String ATTR_FREQ = "f"; + private static final int FLAG_ADDRESS_MASK = 0x400000; + private static final int FLAG_TERMINAL_MASK = 0x800000; + private static final int ADDRESS_MASK = 0x3FFFFF; + public static final CharNode EMPTY_NODE = new CharNode(); List roots; @@ -179,7 +183,7 @@ public class MakeBinaryDictionary { parent.children.add(child); } child.data = data; - child.freq += occur; + if (child.freq == 0) child.freq = occur; if (word.length() > charAt + 1) { addWordRec(child, word, charAt + 1, occur); } else { @@ -195,56 +199,76 @@ public class MakeBinaryDictionary { static final int ADDR_WIDTH = 23; // Offset to children static final int FREQ_WIDTH_BYTES = 1; static final int COUNT_WIDTH_BYTES = 1; - static final int NODE_SIZE_BYTES = - (CHAR_WIDTH + FLAGS_WIDTH + ADDR_WIDTH) / 8 + FREQ_WIDTH_BYTES; private void addCount(int count) { dict[dictSize++] = (byte) (0xFF & count); } - /* TODO: Allow characters to be beyond the 0-255 range. This is required for some latin - language not currently supported */ private void addNode(CharNode node) { int charData = 0xFFFF & node.data; if (charData > 254) { - System.out.println("WARNING: Non-ASCII character encountered : " + node.data + - ", value = " + charData); - dict[dictSize++] = '@'; + dict[dictSize++] = (byte) 255; + dict[dictSize++] = (byte) ((node.data >> 8) & 0xFF); + dict[dictSize++] = (byte) (node.data & 0xFF); } else { dict[dictSize++] = (byte) (0xFF & node.data); } - dictSize += 3; // Space for children address - if ((0xFFFFFF & node.freq) > 255) { - node.freq = (byte) 255; + if (node.children != null) { + dictSize += 3; // Space for children address + } else { + dictSize += 1; // Space for just the terminal/address flags + } + if ((0xFFFFFF & node.freq) > 255) { + node.freq = 255; + } + if (node.terminal) { + byte freq = (byte) (0xFF & node.freq); + dict[dictSize++] = freq; } - dict[dictSize++] = (byte) (0xFF & node.freq); } + int nullChildrenCount = 0; + int notTerminalCount = 0; + private void updateNodeAddress(int nodeAddress, CharNode node, int childrenAddress) { - childrenAddress = 0x7FFFFF & childrenAddress; + if ((dict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character + nodeAddress += 2; + } + childrenAddress = ADDRESS_MASK & childrenAddress; + if (childrenAddress == 0) { + nullChildrenCount++; + } else { + childrenAddress |= FLAG_ADDRESS_MASK; + } if (node.terminal) { - childrenAddress |= 0x800000; + childrenAddress |= FLAG_TERMINAL_MASK; + } else { + notTerminalCount++; } dict[nodeAddress + 1] = (byte) (childrenAddress >> 16); - dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8); - dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF)); + if ((childrenAddress & FLAG_ADDRESS_MASK) != 0) { + dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8); + dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF)); + } } void writeWordsRec(List children) { if (children == null || children.size() == 0) { return; } - addCount(children.size()); - int childrenStart = dictSize; - for (int j = 0; j < children.size(); j++) { + final int childCount = children.size(); + addCount(childCount); + //int childrenStart = dictSize; + int[] childrenAddresses = new int[childCount]; + for (int j = 0; j < childCount; j++) { CharNode node = children.get(j); + childrenAddresses[j] = dictSize; addNode(node); } - for (int j = 0; j < children.size(); j++) { + for (int j = 0; j < childCount; j++) { CharNode node = children.get(j); - // TODO: Fix this when child length becomes variable - int nodeAddress = childrenStart + NODE_SIZE_BYTES * j; + int nodeAddress = childrenAddresses[j]; int cacheDictSize = dictSize; writeWordsRec(node.children); updateNodeAddress(nodeAddress, node, node.children != null @@ -253,8 +277,8 @@ public class MakeBinaryDictionary { } void writeToDict(String dictFilename) { - // 2MB max - dict = new byte[2 * 1024 * 1024]; // 2MB upper limit. Actual is probably + // 4MB max, 22-bit offsets + dict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably // < 1MB in most cases, as there is a limit in the // resource size in apks. dictSize = 0; @@ -272,19 +296,29 @@ public class MakeBinaryDictionary { void traverseDict(int pos, char[] word, int depth) { int count = dict[pos++] & 0xFF; for (int i = 0; i < count; i++) { - char c = (char) (dict[pos] & 0xFF); - word[depth] = c; - if ((dict[pos + 1] & 0x80) > 0) { - showWord(word, depth + 1, dict[pos + 4] & 0xFF); + char c = (char) (dict[pos++] & 0xFF); + if (c == 0xFF) { + c = (char) (((dict[pos] & 0xFF) << 8) | (dict[pos+1] & 0xFF)); + pos += 2; + } + word[depth] = c; + boolean terminal = (dict[pos] & 0x80) > 0; + int address = 0; + if ((dict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) { + address = + ((dict[pos + 0] & (FLAG_ADDRESS_MASK >> 16)) << 16) + | ((dict[pos + 1] & 0xFF) << 8) + | ((dict[pos + 2] & 0xFF)); + pos += 2; + } + pos++; + if (terminal) { + showWord(word, depth + 1, dict[pos] & 0xFF); + pos++; } - int address = - ((dict[pos + 1] & 0x7F) << 16) - | ((dict[pos + 2] & 0xFF) << 8) - | ((dict[pos + 3] & 0xFF)); if (address != 0) { traverseDict(address, word, depth + 1); } - pos += NODE_SIZE_BYTES; } } diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java index adf37ed0b..738640214 100644 --- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -459,11 +459,30 @@ class Main { mSdkLog.printf(" Sdcard: %s\n", sdcard); } } + + // Are there some unused AVDs? + List badAvds = avdManager.getUnavailableAvds(); + + if (badAvds == null || badAvds.size() == 0) { + return; + } + + mSdkLog.printf("\nThe following Android Virtual Devices could not be loaded:\n"); + boolean needSeparator = false; + for (AvdInfo info : badAvds) { + if (needSeparator) { + mSdkLog.printf("---------\n"); + } + mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName()); + mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath()); + mSdkLog.printf(" Error: %s\n", info.getError() == null ? "--" : info.getError()); + needSeparator = true; + } } catch (AndroidLocationException e) { errorAndExit(e.getMessage()); } } - + /** * Creates a new AVD. This is a text based creation with command line prompt. */ @@ -573,7 +592,7 @@ class Main { File dir = new File(oldAvdInfo.getPath()); avdManager.recursiveDelete(dir); dir.delete(); - // Remove old avd info from manager + // Remove old AVD info from manager avdManager.removeAvd(oldAvdInfo); } @@ -583,13 +602,27 @@ class Main { } /** - * Delete an AVD. + * Delete an AVD. If the AVD name is not part of the available ones look for an + * invalid AVD (one not loaded due to some error) to remove it too. */ private void deleteAvd() { try { String avdName = mSdkCommandLine.getParamName(); AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); AvdInfo info = avdManager.getAvd(avdName); + + if (info == null) { + // Look in unavailable AVDs + List badAvds = avdManager.getUnavailableAvds(); + if (badAvds != null) { + for (AvdInfo i : badAvds) { + if (i.getName().equals(avdName)) { + info = i; + break; + } + } + } + } if (info == null) { errorAndExit("There is no Android Virtual Device named '%s'.", avdName); diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java index 489451746..163f7a950 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java @@ -26,8 +26,10 @@ public interface ISdkLog { /** * Prints a warning message on stdout. *

+ * The message will be tagged with "Warning" on the output so the caller does not + * need to put such a prefix in the format string. + *

* Implementations should only display warnings in verbose mode. - * The message should be prefixed with "Warning:". * * @param warningFormat is an optional error format. If non-null, it will be printed * using a {@link Formatter} with the provided arguments. @@ -38,8 +40,10 @@ public interface ISdkLog { /** * Prints an error message on stderr. *

+ * The message will be tagged with "Error" on the output so the caller does not + * need to put such a prefix in the format string. + *

* Implementation should always display errors, independent of verbose mode. - * The message should be prefixed with "Error:". * * @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's * message will be printed out. diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java index 93577e42b..a6326630e 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java @@ -32,8 +32,11 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.TreeSet; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -118,15 +121,43 @@ public final class AvdManager { private final String mPath; private final IAndroidTarget mTarget; private final Map mProperties; - - /** Creates a new AVD info. Values are immutable. - * @param properties */ + private final String mError; + + /** + * Creates a new valid AVD info. Values are immutable. + *

+ * Such an AVD is available and can be used. + * The error string is set to null. + * + * @param name The name of the AVD (for display or reference) + * @param path The path to the config.ini file + * @param target The target. Cannot be null. + * @param properties The property map. Cannot be null. + */ public AvdInfo(String name, String path, IAndroidTarget target, Map properties) { + this(name, path, target, properties, null /*error*/); + } + + /** + * Creates a new invalid AVD info. Values are immutable. + *

+ * Such an AVD is not complete and cannot be used. + * The error string must be non-null. + * + * @param name The name of the AVD (for display or reference) + * @param path The path to the config.ini file + * @param target The target. Can be null. + * @param properties The property map. Can be null. + * @param error The error describing why this AVD is invalid. Cannot be null. + */ + public AvdInfo(String name, String path, IAndroidTarget target, + Map properties, String error) { mName = name; mPath = path; mTarget = target; mProperties = properties; + mError = error; } /** Returns the name of the AVD. */ @@ -144,6 +175,11 @@ public final class AvdManager { return mTarget; } + /** Returns the error describing why an AVD failed to load. Always null for valid AVDs. */ + public String getError() { + return mError; + } + /** * Helper method that returns the .ini {@link File} for a given AVD name. * @throws AndroidLocationException if there's a problem getting android root directory. @@ -243,7 +279,7 @@ public final class AvdManager { // AVD shouldn't already exist if removePrevious is false. if (log != null) { log.error(null, - "Folder %s is in the way. Use --force if you want to overwrite.", + "Folder %1$s is in the way. Use --force if you want to overwrite.", avdFolder.getAbsolutePath()); } return null; @@ -393,9 +429,9 @@ public final class AvdManager { if (log != null) { if (target.isPlatform()) { - log.printf("Created AVD '%s' based on %s\n", name, target.getName()); + log.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName()); } else { - log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(), + log.printf("Created AVD '%1$s' based on %2$s (%3$s)\n", name, target.getName(), target.getVendor()); } } @@ -527,29 +563,49 @@ public final class AvdManager { *

* This also remove it from the manager's list, The caller does not need to * call {@link #removeAvd(AvdInfo)} afterwards. + *

+ * This method is designed to somehow work with an unavailable AVD, that is an AVD that + * could not be loaded due to some error. That means this method still tries to remove + * the AVD ini file or its folder if it can be found. An error will be output if any of + * these operations fail. * * @param avdInfo the information on the AVD to delete */ public void deleteAvd(AvdInfo avdInfo, ISdkLog log) { try { + boolean error = false; + File f = avdInfo.getIniFile(); - if (f.exists()) { - log.warning("Deleting file %s", f.getCanonicalPath()); + if (f != null && f.exists()) { + log.warning("Deleting file %1$s", f.getCanonicalPath()); if (!f.delete()) { - log.error(null, "Failed to delete %s", f.getCanonicalPath()); + log.error(null, "Failed to delete %1$s", f.getCanonicalPath()); + error = true; } } - - f = new File(avdInfo.getPath()); - if (f.exists()) { - log.warning("Deleting folder %s", f.getCanonicalPath()); - recursiveDelete(f); - if (!f.delete()) { - log.error(null, "Failed to delete %s", f.getCanonicalPath()); + + String path = avdInfo.getPath(); + if (path != null) { + f = new File(path); + if (f.exists()) { + log.warning("Deleting folder %1$s", f.getCanonicalPath()); + recursiveDelete(f); + if (!f.delete()) { + log.error(null, "Failed to delete %1$s", f.getCanonicalPath()); + error = true; + } } } removeAvd(avdInfo); + + if (error) { + log.printf("AVD '%1$s' deleted with errors. See warnings above.", + avdInfo.getName()); + } else { + log.printf("AVD '%1$s' deleted.", avdInfo.getName()); + } + } catch (AndroidLocationException e) { log.error(e, null); } catch (IOException e) { @@ -575,14 +631,14 @@ public final class AvdManager { try { if (paramFolderPath != null) { File f = new File(avdInfo.getPath()); - log.warning("Moving '%s' to '%s'.", avdInfo.getPath(), paramFolderPath); + log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath); if (!f.renameTo(new File(paramFolderPath))) { - log.error(null, "Failed to move '%s' to '%s'.", + log.error(null, "Failed to move '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath); return false; } - // update avd info + // update AVD info AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget(), avdInfo.getProperties()); mAvdList.remove(avdInfo); @@ -597,19 +653,22 @@ public final class AvdManager { File oldIniFile = avdInfo.getIniFile(); File newIniFile = AvdInfo.getIniFile(newName); - log.warning("Moving '%s' to '%s'.", oldIniFile.getPath(), newIniFile.getPath()); + log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath()); if (!oldIniFile.renameTo(newIniFile)) { - log.error(null, "Failed to move '%s' to '%s'.", + log.error(null, "Failed to move '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath()); return false; } - // update avd info + // update AVD info AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget(), avdInfo.getProperties()); mAvdList.remove(avdInfo); mAvdList.add(info); } + + log.printf("AVD '%1$s' moved.", avdInfo.getName()); + } catch (AndroidLocationException e) { log.error(e, null); } catch (IOException e) { @@ -634,29 +693,28 @@ public final class AvdManager { } } - private void buildAvdList(ArrayList list) throws AndroidLocationException { + /** + * Returns a list of files that are potential AVD ini files. + *

+ * This lists the $HOME/.android/avd/.ini files. + * Such files are properties file than then indicate where the AVD folder is located. + * + * @return A new {@link File} array or null. The array might be empty. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + private File[] buildAvdFilesList() throws AndroidLocationException { // get the Android prefs location. String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - final boolean avdListDebug = System.getenv("AVD_LIST_DEBUG") != null; - if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] AVD root: '%s'\n", avdRoot); - } - // ensure folder validity. File folder = new File(avdRoot); if (folder.isFile()) { - if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] AVD root is a file.\n"); - } - throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot)); + throw new AndroidLocationException( + String.format("%1$s is not a valid folder.", avdRoot)); } else if (folder.exists() == false) { - if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] AVD root folder doesn't exist, creating.\n"); - } // folder is not there, we create it and return folder.mkdirs(); - return; + return null; } File[] avds = folder.listFiles(new FilenameFilter() { @@ -664,10 +722,6 @@ public final class AvdManager { if (INI_NAME_PATTERN.matcher(name).matches()) { // check it's a file and not a folder boolean isFile = new File(parent, name).isFile(); - if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] Item '%s': %s\n", - name, isFile ? "accepted file" : "rejected"); - } return isFile; } @@ -675,63 +729,164 @@ public final class AvdManager { } }); + return avds; + } + + /** + * Computes the internal list of available AVDs. + * This only contains AVDs that reference the target currently available. + * + * @param list An array list that will contain the list of AVDs. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + private void buildAvdList(ArrayList list) throws AndroidLocationException { + + File[] avds = buildAvdFilesList(); + for (File avd : avds) { - AvdInfo info = parseAvdInfo(avd); + AvdInfo info = parseAvdInfo(avd, false /*acceptError*/); if (info != null) { list.add(info); - if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath()); - } - } else if (avdListDebug) { - mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath()); } } } - - private AvdInfo parseAvdInfo(File path) { - Map map = SdkManager.parsePropertyFile(path, mSdkLog); + + /** + * Computes the internal list of not available AVDs. + *

+ * These are the AVDs that failed to load for some reason or another. + * You can retrieve the load error using {@link AvdInfo#getError()}. + *

+ * These {@link AvdInfo} must not be used for usual operations (e.g. instanciating + * an emulator) or trying to use them for anything else but {@link #deleteAvd(AvdInfo, ISdkLog)} + * will have unpredictable results -- that is most likely the operation will fail. + * + * @return A list of unavailable AVDs, all with errors. The list can be null or empty if there + * are no AVDs to return. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public List getUnavailableAvds() throws AndroidLocationException { + AvdInfo[] avds = getAvds(); + File[] allAvds = buildAvdFilesList(); + if (allAvds == null || allAvds.length == 0) { + return null; + } + + TreeSet list = new TreeSet(Arrays.asList(allAvds)); + + for (AvdInfo info : avds) { + if (list.remove(info.getIniFile())) { + if (list.size() == 0) { + return null; + } + } + } + ArrayList errorAvds = new ArrayList(list.size()); + for (File file : list) { + errorAvds.add(parseAvdInfo(file, true /*acceptError*/)); + } + + return errorAvds; + } + + /** + * Parses an AVD config.ini to create an {@link AvdInfo}. + * + * @param path The path to the AVD config.ini + * @param acceptError When false, an AVD that fails to load will be discarded and null will be + * returned. When true, such an AVD will be returned with an error description. + * @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not + * valid and acceptError is false. + */ + private AvdInfo parseAvdInfo(File path, boolean acceptError) { + String error = null; + Map map = SdkManager.parsePropertyFile(path, mSdkLog); + String avdPath = map.get(AVD_INFO_PATH); - if (avdPath == null) { - return null; - } - String targetHash = map.get(AVD_INFO_TARGET); - if (targetHash == null) { - return null; + + IAndroidTarget target = null; + File configIniFile = null; + Map properties = null; + + if (targetHash != null) { + target = mSdk.getTargetFromHashString(targetHash); } - IAndroidTarget target = mSdk.getTargetFromHashString(targetHash); - if (target == null) { - return null; + // load the AVD properties. + if (avdPath != null) { + configIniFile = new File(avdPath, CONFIG_INI); } - // load the avd properties. - File configIniFile = new File(avdPath, CONFIG_INI); - Map properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog); + if (configIniFile != null) { + properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog); + } + // get name + String name = path.getName(); Matcher matcher = INI_NAME_PATTERN.matcher(path.getName()); - + if (matcher.matches()) { + name = matcher.group(1); + } + + if (!acceptError) { + if (avdPath == null || + targetHash == null || + target == null || + configIniFile == null || + properties == null) { + return null; + } + } else { + if (avdPath == null || configIniFile == null) { + error = String.format("Missing AVD 'path' property in %1$s", name); + } else if (targetHash == null) { + error = String.format("Missing 'target' property in %1$s", name); + } else if (target == null) { + error = String.format("Unknown target '%2$s' in %1$s", name, targetHash); + } else if (properties == null) { + error = String.format("Failed to parse properties from %1$s", avdPath); + } + } + AvdInfo info = new AvdInfo( - matcher.matches() ? matcher.group(1) : path.getName(), // should not happen + name, avdPath, target, - properties); + properties, + error); return info; } + /** + * Writes a new AVD config.ini file from a set of properties. + * + * @param iniFile The file to generate. + * @param values THe properties to place in the ini file. + * @throws IOException if {@link FileWriter} fails to open, write or close the file. + */ private static void createConfigIni(File iniFile, Map values) throws IOException { FileWriter writer = new FileWriter(iniFile); for (Entry entry : values.entrySet()) { - writer.write(String.format("%s=%s\n", entry.getKey(), entry.getValue())); + writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue())); } writer.close(); } + /** + * Invokes the tool to create a new SD card image file. + * + * @param toolLocation The path to the mksdcard tool. + * @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}. + * @param location The path of the new sdcard image file to generate. + * @param log The logger object, to report errors. + * @return True if the sdcard could be created. + */ private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) { try { String[] command = new String[3]; @@ -744,26 +899,25 @@ public final class AvdManager { ArrayList stdOutput = new ArrayList(); int status = grabProcessOutput(process, errorOutput, stdOutput, true /* waitForReaders */); - - if (status != 0) { - log.error(null, "Failed to create the SD card."); + + if (status == 0) { + return true; + } else { for (String error : errorOutput) { log.error(null, error); } - - return false; } - return true; } catch (InterruptedException e) { - log.error(null, "Failed to create the SD card."); + // pass, print error below } catch (IOException e) { - log.error(null, "Failed to create the SD card."); + // pass, print error below } + log.error(null, "Failed to create the SD card."); return false; } - + /** * Gets the stderr/stdout outputs of a process and returns when the process is done. * Both must be read or the process will block on windows.