Merge lp:~thomir-deactivatedaccount/ubuntu-test-cases/touch-convert-boottest-to-spaces into lp:ubuntu-test-cases
- touch-convert-boottest-to-spaces
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~thomir-deactivatedaccount/ubuntu-test-cases/touch-convert-boottest-to-spaces |
Merge into: | lp:ubuntu-test-cases |
Diff against target: |
22838 lines (+21174/-0) (has conflicts) 321 files modified
README.rst (+259/-0) config/boottest.rc.example (+10/-0) jenkins/custom-demo.py (+32/-0) jenkins/production-arale.py (+56/-0) jenkins/production-krillin.py (+50/-0) jenkins/production.py (+96/-0) jenkins/setup_jenkins.py (+228/-0) jenkins/staging.py (+42/-0) jenkins/templates/touch-base.xml.jinja2 (+130/-0) jenkins/templates/touch-master.xml.jinja2 (+80/-0) jenkins/templates/touch-smoke.xml.jinja2 (+199/-0) jenkins/testconfig.py (+242/-0) scripts/assert-image (+31/-0) scripts/boottest.sh (+204/-0) scripts/combine_results (+124/-0) scripts/dashboard.py (+305/-0) scripts/device_info.py (+381/-0) scripts/get-adb-id (+14/-0) scripts/get-device-info (+38/-0) scripts/jenkins.sh (+216/-0) scripts/junit2utah.py (+70/-0) scripts/masher_control.py (+64/-0) scripts/ncd_usb.py (+48/-0) scripts/provision.sh (+267/-0) scripts/reboot-and-wait (+54/-0) scripts/recover.py (+128/-0) scripts/run-adt.py (+38/-0) scripts/run-autopilot-tests.sh (+267/-0) scripts/run-emulator (+97/-0) scripts/run-mp.sh (+44/-0) scripts/run-smoke (+408/-0) scripts/run-touch-upgrade.sh (+46/-0) scripts/statsd.py (+35/-0) selftests/test_junit2utah.py (+49/-0) selftests/test_reboot_and_wait.py (+66/-0) selftests/test_run_smoke.py (+148/-0) selftests/test_statsd.py (+64/-0) tests/bootfail/README (+15/-0) tests/bootfail/debian/changelog (+5/-0) tests/bootfail/debian/compat (+1/-0) tests/bootfail/debian/control (+10/-0) tests/bootfail/debian/rules (+4/-0) tests/bootfail/debian/tests/bootfail (+15/-0) tests/bootfail/debian/tests/control (+3/-0) tests/bootspeed/bootchart/run.py (+73/-0) tests/bootspeed/bootchart/setup.sh (+4/-0) tests/bootspeed/bootchart/tc_control (+12/-0) tests/bootspeed/master.run (+5/-0) tests/bootspeed/tslist.run (+1/-0) tests/boottest/README (+10/-0) tests/boottest/debian/changelog (+5/-0) tests/boottest/debian/compat (+1/-0) tests/boottest/debian/control (+10/-0) tests/boottest/debian/rules (+4/-0) tests/boottest/debian/tests/boottest (+13/-0) tests/boottest/debian/tests/control (+6/-0) tests/click_image_tests/check_preinstalled_list/check_preinstalled_list.py (+61/-0) tests/click_image_tests/check_preinstalled_list/tc_control (+10/-0) tests/click_image_tests/master.run (+5/-0) tests/click_image_tests/tslist.run (+1/-0) tests/customizations/master.run (+5/-0) tests/customizations/setup.sh (+10/-0) tests/customizations/ts_control (+1/-0) tests/customizations/tslist.auto (+4/-0) tests/default/apport/tc_control (+9/-0) tests/default/ifconfig/tc_control (+13/-0) tests/default/install/tc_control (+12/-0) tests/default/master.run (+5/-0) tests/default/netstat/tc_control (+12/-0) tests/default/ping/pingtest.sh (+12/-0) tests/default/ping/tc_control (+12/-0) tests/default/pwd/tc_control (+9/-0) tests/default/pwd/test.sh (+18/-0) tests/default/route/tc_control (+12/-0) tests/default/systemsettle/systemsettle.sh (+127/-0) tests/default/systemsettle/tc_control (+9/-0) tests/default/ts_control (+3/-0) tests/default/tslist.run (+12/-0) tests/default/uname/tc_control (+12/-0) tests/default/unity8/tc_control (+9/-0) tests/default/vmstat/tc_control (+12/-0) tests/default/whoopsie/tc_control (+10/-0) tests/default/whoopsie/whoopsie-test.sh (+86/-0) tests/eventstat/eventstat/eventstat.sh (+5/-0) tests/eventstat/eventstat/setup.sh (+7/-0) tests/eventstat/eventstat/tc_control (+12/-0) tests/eventstat/master.run (+5/-0) tests/eventstat/tslist.run (+1/-0) tests/health-check/health-check-test-get-pids.py (+38/-0) tests/health-check/health-check-test-pid.py (+198/-0) tests/health-check/master.run (+5/-0) tests/health-check/setup.sh (+10/-0) tests/health-check/test.sh (+15/-0) tests/health-check/thresholds/armv7l/NetworkManager.threshold (+73/-0) tests/health-check/thresholds/armv7l/accounts-daemon.threshold (+73/-0) tests/health-check/thresholds/armv7l/address-book-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/bluetoothd.threshold (+73/-0) tests/health-check/thresholds/armv7l/bridgemhrd.threshold (+73/-0) tests/health-check/thresholds/armv7l/dconf-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/default.threshold (+73/-0) tests/health-check/thresholds/armv7l/dhclient.threshold (+73/-0) tests/health-check/thresholds/armv7l/dnsmasq.threshold (+73/-0) tests/health-check/thresholds/armv7l/evolution-calendar-factory.threshold (+73/-0) tests/health-check/thresholds/armv7l/evolution-source-registry.threshold (+73/-0) tests/health-check/thresholds/armv7l/hud-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-bluetooth-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-datetime-service.threshold (+74/-0) tests/health-check/thresholds/armv7l/indicator-location-service.threshold (+74/-0) tests/health-check/thresholds/armv7l/indicator-messages-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-network-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-power-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-secret-agent.threshold (+73/-0) tests/health-check/thresholds/armv7l/indicator-sound-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/maliit-server.threshold (+73/-0) tests/health-check/thresholds/armv7l/mediascanner-service.threshold (+73/-0) tests/health-check/thresholds/armv7l/mission-control-5.threshold (+73/-0) tests/health-check/thresholds/armv7l/mpdecision.threshold (+73/-0) tests/health-check/thresholds/armv7l/mtp-server.threshold (+73/-0) tests/health-check/thresholds/armv7l/ofonod.threshold (+73/-0) tests/health-check/thresholds/armv7l/polkitd.threshold (+73/-0) tests/health-check/thresholds/armv7l/powerd.threshold (+73/-0) tests/health-check/thresholds/armv7l/pulseaudio.threshold (+73/-0) tests/health-check/thresholds/armv7l/qmuxd.threshold (+73/-0) tests/health-check/thresholds/armv7l/rsyslogd.threshold (+73/-0) tests/health-check/thresholds/armv7l/rtkit-daemon.threshold (+73/-0) tests/health-check/thresholds/armv7l/systemd-logind.threshold (+73/-0) tests/health-check/thresholds/armv7l/systemd-udevd.threshold (+73/-0) tests/health-check/thresholds/armv7l/telepathy-ofono.threshold (+73/-0) tests/health-check/thresholds/armv7l/thermald.threshold (+73/-0) tests/health-check/thresholds/armv7l/ubuntu-location-serviced.threshold (+74/-0) tests/health-check/thresholds/armv7l/ueventd.theshold (+73/-0) tests/health-check/thresholds/armv7l/unity-scope-home.threshold (+73/-0) tests/health-check/thresholds/armv7l/unity-scope-loader.threshold (+73/-0) tests/health-check/thresholds/armv7l/upowerd.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-dbus-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-event-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-file-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-local-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-property-watcher.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-socket-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/upstart-udev-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/url-dispatcher.threshold (+73/-0) tests/health-check/thresholds/armv7l/whoopsie.threshold (+73/-0) tests/health-check/thresholds/armv7l/window-stack-bridge.threshold (+73/-0) tests/health-check/thresholds/armv7l/wpa_supplicant.threshold (+73/-0) tests/health-check/thresholds/i686/NetworkManager.threshold (+73/-0) tests/health-check/thresholds/i686/accounts-daemon.threshold (+73/-0) tests/health-check/thresholds/i686/acpid.threshold (+73/-0) tests/health-check/thresholds/i686/avahi-daemon:.threshold (+74/-0) tests/health-check/thresholds/i686/colord.threshold (+73/-0) tests/health-check/thresholds/i686/cupsd.threshold (+73/-0) tests/health-check/thresholds/i686/dbus-daemon.threshold.blacklist (+73/-0) tests/health-check/thresholds/i686/dconf-service.threshold (+73/-0) tests/health-check/thresholds/i686/default.threshold (+73/-0) tests/health-check/thresholds/i686/deja-dup-monitor.threshold (+73/-0) tests/health-check/thresholds/i686/dhclient.threshold (+73/-0) tests/health-check/thresholds/i686/dnsmasq.threshold (+73/-0) tests/health-check/thresholds/i686/evolution-calendar-factory.threshold (+73/-0) tests/health-check/thresholds/i686/evolution-source-registry.threshold (+73/-0) tests/health-check/thresholds/i686/gnome-fallback-mount-helper.threshold (+73/-0) tests/health-check/thresholds/i686/gnome-keyring-daemon.threshold.blacklist (+73/-0) tests/health-check/thresholds/i686/gnome-settings-daemon.threshold (+73/-0) tests/health-check/thresholds/i686/gvfs-afc-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/i686/gvfs-gphoto2-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/i686/gvfs-mtp-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/i686/gvfs-udisks2-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/i686/gvfsd-burn.threshold (+73/-0) tests/health-check/thresholds/i686/gvfsd-fuse.threshold (+73/-0) tests/health-check/thresholds/i686/gvfsd.threshold (+73/-0) tests/health-check/thresholds/i686/hud-service.threshold (+73/-0) tests/health-check/thresholds/i686/ibus-dconf.threshold (+73/-0) tests/health-check/thresholds/i686/ibus-engine-simple.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-application-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-bluetooth-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-datetime-service.threshold (+74/-0) tests/health-check/thresholds/i686/indicator-keyboard-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-messages-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-power-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-printers-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-session-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-sound-service.threshold (+73/-0) tests/health-check/thresholds/i686/indicator-sync-service.threshold (+73/-0) tests/health-check/thresholds/i686/kerneloops.threshold (+73/-0) tests/health-check/thresholds/i686/modem-manager.threshold (+73/-0) tests/health-check/thresholds/i686/nm-applet.threshold (+73/-0) tests/health-check/thresholds/i686/polkitd.threshold (+73/-0) tests/health-check/thresholds/i686/rtkit-daemon.threshold (+73/-0) tests/health-check/thresholds/i686/systemd-logind.threshold (+73/-0) tests/health-check/thresholds/i686/systemd-udevd.threshold (+73/-0) tests/health-check/thresholds/i686/telepathy-indicator.threshold (+73/-0) tests/health-check/thresholds/i686/udisksd.threshold (+73/-0) tests/health-check/thresholds/i686/unity-panel-service.threshold (+73/-0) tests/health-check/thresholds/i686/update-notifier.threshold (+73/-0) tests/health-check/thresholds/i686/upowerd.threshold (+73/-0) tests/health-check/thresholds/i686/upstart-dbus-bridge.threshold (+73/-0) tests/health-check/thresholds/i686/upstart-event-bridge.threshold (+73/-0) tests/health-check/thresholds/i686/upstart-file-bridge.threshold (+73/-0) tests/health-check/thresholds/i686/upstart-socket-bridge.threshold (+73/-0) tests/health-check/thresholds/i686/upstart-udev-bridge.threshold (+73/-0) tests/health-check/thresholds/i686/whoopsie.threshold (+73/-0) tests/health-check/thresholds/i686/zeitgeist-daemon.threshold (+73/-0) tests/health-check/thresholds/i686/zeitgeist-datahub.threshold (+73/-0) tests/health-check/thresholds/i686/zeitgeist-fts.threshold (+73/-0) tests/health-check/thresholds/x86_64/NetworkManager.threshold (+73/-0) tests/health-check/thresholds/x86_64/accounts-daemon.threshold (+73/-0) tests/health-check/thresholds/x86_64/acpid.threshold (+73/-0) tests/health-check/thresholds/x86_64/avahi-daemon:.threshold (+74/-0) tests/health-check/thresholds/x86_64/colord.threshold (+73/-0) tests/health-check/thresholds/x86_64/cupsd.threshold (+73/-0) tests/health-check/thresholds/x86_64/dbus-daemon.threshold.blacklist (+73/-0) tests/health-check/thresholds/x86_64/dconf-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/default.threshold (+73/-0) tests/health-check/thresholds/x86_64/deja-dup-monitor.threshold (+73/-0) tests/health-check/thresholds/x86_64/dhclient.threshold (+73/-0) tests/health-check/thresholds/x86_64/dnsmasq.threshold (+73/-0) tests/health-check/thresholds/x86_64/evolution-calendar-factory.threshold (+73/-0) tests/health-check/thresholds/x86_64/evolution-source-registry.threshold (+73/-0) tests/health-check/thresholds/x86_64/gnome-fallback-mount-helper.threshold (+73/-0) tests/health-check/thresholds/x86_64/gnome-keyring-daemon.threshold.blacklist (+73/-0) tests/health-check/thresholds/x86_64/gnome-settings-daemon.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfs-afc-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfs-gphoto2-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfs-mtp-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfs-udisks2-volume-monitor.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfsd-burn.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfsd-fuse.threshold (+73/-0) tests/health-check/thresholds/x86_64/gvfsd.threshold (+73/-0) tests/health-check/thresholds/x86_64/hud-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/ibus-dconf.threshold (+73/-0) tests/health-check/thresholds/x86_64/ibus-engine-simple.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-application-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-bluetooth-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-datetime-service.threshold (+74/-0) tests/health-check/thresholds/x86_64/indicator-keyboard-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-messages-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-power-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-printers-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-session-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-sound-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/indicator-sync-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/kerneloops.threshold (+73/-0) tests/health-check/thresholds/x86_64/modem-manager.threshold (+73/-0) tests/health-check/thresholds/x86_64/nm-applet.threshold (+73/-0) tests/health-check/thresholds/x86_64/polkitd.threshold (+73/-0) tests/health-check/thresholds/x86_64/rtkit-daemon.threshold (+73/-0) tests/health-check/thresholds/x86_64/systemd-logind.threshold (+73/-0) tests/health-check/thresholds/x86_64/systemd-udevd.threshold (+73/-0) tests/health-check/thresholds/x86_64/telepathy-indicator.threshold (+73/-0) tests/health-check/thresholds/x86_64/udisksd.threshold (+73/-0) tests/health-check/thresholds/x86_64/unity-panel-service.threshold (+73/-0) tests/health-check/thresholds/x86_64/update-notifier.threshold (+73/-0) tests/health-check/thresholds/x86_64/upowerd.threshold (+73/-0) tests/health-check/thresholds/x86_64/upstart-dbus-bridge.threshold (+73/-0) tests/health-check/thresholds/x86_64/upstart-event-bridge.threshold (+73/-0) tests/health-check/thresholds/x86_64/upstart-file-bridge.threshold (+73/-0) tests/health-check/thresholds/x86_64/upstart-socket-bridge.threshold (+73/-0) tests/health-check/thresholds/x86_64/upstart-udev-bridge.threshold (+73/-0) tests/health-check/thresholds/x86_64/whoopsie.threshold (+73/-0) tests/health-check/thresholds/x86_64/zeitgeist-daemon.threshold (+73/-0) tests/health-check/thresholds/x86_64/zeitgeist-datahub.threshold (+73/-0) tests/health-check/thresholds/x86_64/zeitgeist-fts.threshold (+73/-0) tests/health-check/ts_control (+2/-0) tests/health-check/tslist.auto (+3/-0) tests/memevent/master.run (+5/-0) tests/memevent/nfss_upload_results.py (+185/-0) tests/memevent/setup.sh (+8/-0) tests/memevent/ts_control (+1/-0) tests/memevent/tslist.auto (+7/-0) tests/memevent/ubuntu_test_cases/__init__.py (+6/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/__init__.py (+1/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/apps/__init__.py (+21/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/apps/gallery.py (+41/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/apps/media_player.py (+53/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/matchers.py (+37/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/probes.py (+183/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/smem-tabs (+687/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/tests/__init__.py (+70/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/tests/test_browser.py (+30/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/tests/test_camera.py (+20/-0) tests/memevent/ubuntu_test_cases/memory_usage_measurement/tests/test_gallery.py (+16/-0) tests/sdk/master.run (+5/-0) tests/sdk/run_test.sh (+20/-0) tests/sdk/setup.sh (+12/-0) tests/sdk/ts_control (+1/-0) tests/sdk/tslist.auto (+3/-0) tests/security/master.run (+5/-0) tests/security/run_test.sh (+26/-0) tests/security/setup.sh (+12/-0) tests/security/ts_control (+1/-0) tests/security/tslist.auto (+6/-0) tests/smem/master.run (+15/-0) tests/smem/smem/smem-tabs (+687/-0) tests/smem/smem/smem.sh (+12/-0) tests/smem/smem/tc_control (+11/-0) tests/smem/tslist.run (+1/-0) tests/suspend-blocker/master.run (+5/-0) tests/suspend-blocker/suspend-blocker/README (+7/-0) tests/suspend-blocker/suspend-blocker/setup.sh (+10/-0) tests/suspend-blocker/suspend-blocker/susblock.conf (+5/-0) tests/suspend-blocker/suspend-blocker/suspend-blocker.sh (+26/-0) tests/suspend-blocker/suspend-blocker/tc_control (+16/-0) tests/suspend-blocker/tslist.run (+1/-0) tests/systemsettle/systemsettle-after/tc_control (+9/-0) tests/systemsettle/systemsettle-before/tc_control (+9/-0) tests/systemsettle/systemsettle.sh (+127/-0) tests/systemsettle/tslist.run (+2/-0) tests/ubuntu-sample-adt-test/COPYING (+674/-0) tests/ubuntu-sample-adt-test/README.md (+41/-0) tests/ubuntu-sample-adt-test/debian/changelog (+5/-0) tests/ubuntu-sample-adt-test/debian/tests/control (+1/-0) tests/ubuntu-sample-adt-test/debian/tests/sample (+19/-0) utils/host/adb-shell (+23/-0) utils/host/autopilot-list (+23/-0) utils/host/autopilot-run (+12/-0) utils/host/get-device-type (+9/-0) utils/host/prepare-autopilot-test.sh (+7/-0) utils/host/reboot-and-unlock.sh (+20/-0) utils/target/autopilot-list (+13/-0) utils/target/autopilot-run (+6/-0) utils/target/check-clickhook-rules (+10/-0) utils/target/prepare-autopilot-test.sh (+16/-0) Conflict adding file scripts. Moved existing file to scripts.moved. |
To merge this branch: | bzr merge lp:~thomir-deactivatedaccount/ubuntu-test-cases/touch-convert-boottest-to-spaces |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Test Case Developers | Pending | ||
Review via email: mp+261030@code.launchpad.net |
This proposal has been superseded by a proposal from 2015-06-03.
Commit message
Convert tabs to spaces in scripts/*
Description of the change
Convert tabs to spaces in scripts/*
- 407. By Thomi Richards
-
realign comments.
Unmerged revisions
- 407. By Thomi Richards
-
realign comments.
- 406. By Thomi Richards
-
Convert tabs to spaces in scripts/*
- 405. By Francis Ginther
-
Extract the package version from the first installed binary package and supply that of the version of the source package under test in the result file sent to britney. [r=Paul Larson]
- 404. By Thomi Richards
-
Add a simple utility to retry adt-run several times in case of failure. [r=Francis Ginther]
- 403. By Paul Larson
-
Eliminate the need for getinstalledpkgs test in boottest [r=Francis Ginther]
- 402. By Francis Ginther
-
Install boottest binary packages during getinstalledpkgs, removing that responsibility from the boottest dep8 test.
- 401. By Francis Ginther
-
Remove boottest dependencies on the actual source package contents for the package under test.
- 400. By Paul Larson
-
Improve recovery image download logic [r=Parameswaran Sivatharman]
- 399. By Paul Larson
-
typo with quotes
- 398. By Paul Larson
-
include offline as a state we want to recover from
Preview Diff
1 | === added file 'README.rst' |
2 | --- README.rst 1970-01-01 00:00:00 +0000 |
3 | +++ README.rst 2015-06-03 20:37:20 +0000 |
4 | @@ -0,0 +1,259 @@ |
5 | +Touch Testing From the CLI |
6 | +========================== |
7 | + |
8 | +The CI touch testing execution framework was written so that it's very easy to |
9 | +run tests from home in the exact same way tests are run for the CI dashboard |
10 | +and CI MP testing. The only things you need are: |
11 | + |
12 | + * This bzr branch: `lp:ubuntu-test-cases/touch <https://code.launchpad.net/~ubuntu-test-case-dev/ubuntu-test-cases/touch>`_ |
13 | + * The phablet-tools_ and ubuntu-device-flash_ packages |
14 | + * An Ubuntu Touch supported_ device |
15 | + |
16 | +.. _phablet-tools: http://launchpad.net/phablet-tools |
17 | +.. _ubuntu-device-flash: http://launchpad.net/goget-ubuntu-touch |
18 | +.. _supported: http://wiki.ubuntu.com/Touch/Devices |
19 | + |
20 | +There are two pieces to touch testing, provisioning and test execution. |
21 | +The provisioning step is required, but once provisioned, you can proceed with |
22 | +any of the test methods described in this document. |
23 | + |
24 | +.. note:: |
25 | + Re-creating test results to match those on http://ci.ubuntu.com/ is not |
26 | + always possible. The Ubuntu touch images are created using the Ubuntu |
27 | + archive which changes over time as packages are constantly being updated. |
28 | + Even when using identical images, test dependencies may change making it |
29 | + impossible to recreate an identical test environment. |
30 | + |
31 | + For best results, it's recommended to always use the most recent image. |
32 | + |
33 | +Prerequisites |
34 | +------------- |
35 | + |
36 | +Before proceeding, a network configuration file is required. This is a one time |
37 | +setup and can be used for all future provisioning and testing as long as your |
38 | +wifi network does not change. This file is the same network configuration file |
39 | +used by *phablet-network* and can be found by running *phablet-network* with no |
40 | +options. For example:: |
41 | + |
42 | + $ phablet-network |
43 | + Network file is /etc/NetworkManager/system-connections/my-network-ssid |
44 | + Provisioning network on device |
45 | + |
46 | + Network setup complete |
47 | + PING launchpad.net (91.189.89.222) 56(84) bytes of data. |
48 | + |
49 | +Once the network configuration file is identified, it can be copied into the |
50 | +default location to be used by the provisioning scripts:: |
51 | + |
52 | + mkdir ~/.ubuntu-ci |
53 | + sudo cp /etc/NetworkManager/system-connections/my-network-ssid ~/.ubuntu-ci/wifi.conf |
54 | + sudo chown $USER:$USER ~/.ubuntu-ci/wifi.conf |
55 | + |
56 | +Provisioning |
57 | +------------ |
58 | + |
59 | +Provisioning is a required step before performing CI dashboard or MP testing. |
60 | +It is necessary to configure the device into a known state so that the tests |
61 | +can run in a predictible manner. |
62 | + |
63 | +.. warning:: |
64 | + This provisioning step will completely erase your device. Be sure to |
65 | + backup any data before proceeding. |
66 | + |
67 | +The provisioning script is a simple wrapper to commands from phablet-tools |
68 | +and some additional device setup to get it ready for testing. Provisioning |
69 | +is performed with the scripts/provision.sh command. Running:: |
70 | + |
71 | + ./scripts/provision.sh -w |
72 | + |
73 | +will install the latest ubuntu-touch/devel-proposed image. If you |
74 | +wish to install the latest ubuntu-rtm image instead, use:: |
75 | + |
76 | + export IMAGE_OPT="--bootstrap --developer-mode --channel=ubuntu-touch/ubuntu-rtm/14.09-proposed" |
77 | + ./scripts/provision.sh -w |
78 | + |
79 | +Provisioning using this script requires that you start off with the |
80 | +device booted and accessible via ADB. The device will be rebooted |
81 | +automatically and completely reinstalled - **ALL DATA WILL BE LOST**. |
82 | + |
83 | +.. note:: |
84 | + provision.sh requires a path to a network-manager wifi configuration file |
85 | + that can be copied to the target device as described above in the |
86 | + Prerequisites section. By default this is set to |
87 | + ${HOME}/.ubuntu-ci/wifi.conf. This can be overridden with the -n parameter. |
88 | + |
89 | +Other customizations can be performed during provisioning, for a full list |
90 | +of options, use:: |
91 | + |
92 | + ./scripts/provision.sh -h |
93 | + |
94 | +Executing Tests |
95 | +--------------- |
96 | + |
97 | +The touch testing tools are intended to provide CI Dashboard and CI MP testing |
98 | +for specific applications. The supported applications and test suites are |
99 | +visible in the `CI Dashboard <http://ci.ubuntu.com/>`_. These tests include |
100 | +both autopilot and UTAH test definitions. The following sections describe how |
101 | +to use these tools for testing on a local device. |
102 | + |
103 | +.. note:: |
104 | + These tools will *only* work on a device that has been provisioned using the |
105 | + methods described above. |
106 | + |
107 | +.. note:: |
108 | + |
109 | + Some of the tests generate subunit result files. These subunit result files |
110 | + provide richer content and potentially more test artifacts over the xml |
111 | + result files. To view the contents of these files, use the trv_ viewer |
112 | + application or your favorite subunit viewer. |
113 | + |
114 | +.. _trv: https://launchpad.net/trv |
115 | + |
116 | +Executing Autopilot Tests |
117 | +~~~~~~~~~~~~~~~~~~~~~~~~~ |
118 | + |
119 | +One or more autopilot tests can be run on the target device using the command:: |
120 | + |
121 | + ./scripts/run-autopilot-tests.sh |
122 | + |
123 | +This is a small wrapper that uses phablet-tools to drive the tests. The |
124 | +script can run one or more autopilot tests. By default it will reboot the |
125 | +device between each test and ensure the device is settled using the |
126 | +*system-settle* script. Both of those options can be disabled via command |
127 | +line options. By default the script will create a directory named |
128 | +*clientlogs* and then a subdirectory for each testsuite with result files. |
129 | +These sub-directories include a xUnit XML formatted file, *test_results.xml*, |
130 | +a subunit result stream, *test_results.subunit*, as well as several log files |
131 | +from the device to help with debugging failures. |
132 | + |
133 | +.. note:: |
134 | + run-autopilot-tests.sh will call a script that installs |
135 | + unity8-autopilot if it is not already installed, to allow the device to |
136 | + be unlocked automatically. |
137 | + |
138 | +Examples |
139 | +^^^^^^^^ |
140 | + |
141 | +An example testing two applications:: |
142 | + |
143 | + ./scripts/provision.sh -w |
144 | + ./scripts/run-autopilot-tests.sh -a dropping_letters_app -a music_app |
145 | + |
146 | +And an example for running all dashboard autopilot tests:: |
147 | + |
148 | + ./scripts/provision.sh -w |
149 | + ./scripts/run-autopilot-tests.sh |
150 | + |
151 | +Executing UTAH Tests |
152 | +~~~~~~~~~~~~~~~~~~~~ |
153 | + |
154 | +Executing UTAH tests locally will require you to install the UTAH client |
155 | +package from a PPA:: |
156 | + |
157 | + sudo add-apt-repository ppa:utah/stable |
158 | + sudo apt-get update |
159 | + sudo apt-get install utah-client |
160 | + |
161 | +With that package installed UTAH tests can be run with:: |
162 | + |
163 | + ./scripts/jenkins.sh |
164 | + |
165 | +This script runs one test at a time and will put its test artifacts under the |
166 | +*clientlogs* directory similar to the autopilot runner. The UTAH result file |
167 | +will be named clientlogs/utah.yaml. |
168 | + |
169 | +An example of running the sdk test suite:: |
170 | + |
171 | + ./scripts/jenkins.sh -a sdk |
172 | + |
173 | +Provisioning and Executing Autopilot Tests for an MP |
174 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
175 | + |
176 | +These scripts are used by jenkins for the testing of MPs that generate Debian |
177 | +packages. The *run-mp.sh* script used below includes provisioning, so for this |
178 | +one case, it is not necessary to call provision.sh separately. |
179 | + |
180 | +To re-create the testing performed by jenkins, set the following |
181 | +environment variables based on the jenkins build parameters:: |
182 | + |
183 | + export package_archive=<from jenkins build parameter> |
184 | + export test_packages=<from jenkins build parameter> |
185 | + export test_suite=<from jenkins build parameter> |
186 | + |
187 | +and set the variable:: |
188 | + |
189 | + export ANDROID_SERIAL=<adb id from your test device> |
190 | + |
191 | +Then execute the following script:: |
192 | + |
193 | + ./scripts/run-mp.sh |
194 | + |
195 | +Running Autopilot Tests for a Modified Click Application |
196 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
197 | + |
198 | +First provision the device with the desired image using the instructions |
199 | +in the "Provisioning" section of this README. |
200 | + |
201 | +Once the image has been provisioned, install the click app to test. |
202 | +The dropping-letters application is used in this example:: |
203 | + |
204 | + adb push com.ubuntu.dropping-letters_0.1.2.2.67_all.click /tmp |
205 | + adb shell pkcon --allow-untrusted install-local \ |
206 | + /tmp/com.ubuntu.dropping-letters_0.1.2.2.67_all.click |
207 | + |
208 | +Now install the test sources ('--wipe' will remove any previously installed |
209 | +test sources):: |
210 | + |
211 | + phablet-click-test-setup --wipe --click com.ubuntu.dropping-letters |
212 | + |
213 | +The above phablet-click-test-setup command will install the standard test |
214 | +dependencies and the click application's test sources as specified in the |
215 | +manifest. This is usually the application's trunk branch. To override the test |
216 | +sources with local changes, replace the test sources that were copied to the |
217 | +device. This example assumes the application code is checked out under the |
218 | +'dropping-letters' directory with the test sources under 'tests/autopilot':: |
219 | + |
220 | + adb shell rm -rf /home/phablet/autopilot/dropping_letters_app |
221 | + adb push dropping-letters/tests/autopilot \ |
222 | + /home/phablet/autopilot |
223 | + |
224 | +Finally, run the application tests:: |
225 | + |
226 | + ./scripts/run-autopilot-tests.sh -a dropping_letters_app |
227 | + |
228 | +The test results are available under:: |
229 | + |
230 | + clientlogs/dropping_letters_app/test_results.subunit |
231 | + clientlogs/dropping_letters_app/test_results.xml |
232 | + |
233 | +Running Autopilot Tests for a Modified Debian Package |
234 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
235 | + |
236 | +First provision the device with the desired image using the instructions |
237 | +in the "Provisioning" section of this README. |
238 | + |
239 | +If the device is provisioned, and you have built the debian package |
240 | +you wish to test with locally, install it on the device. For instance, |
241 | +if you are building and installing dialer-app:: |
242 | + |
243 | + phablet-config writable-image -r 0000 --package-dir /path/to/packages -p dialer-app |
244 | + |
245 | +Alternatively, if you have built the packages in a ppa, you could use:: |
246 | + |
247 | + phablet-config writable-image -r 0000 --ppa ppa:ci-train-ppa-service/landing-004 -p dialer-app |
248 | + |
249 | +.. note:: |
250 | + If you have updates to the dependencies or tests in debian |
251 | + packages, make sure to also install packages for those if required for |
252 | + the change you are making. Some tests need a few extra dependencies |
253 | + installed for the tests to function correctly. To see a list of them, |
254 | + look at jenkins/testconfig.py. |
255 | + |
256 | +Finally, run the application tests:: |
257 | + |
258 | + ./scripts/run-autopilot-tests.sh -a dialer_app |
259 | + |
260 | +The test results are available under:: |
261 | + |
262 | + clientlogs/dialer_app/test_results.subunit |
263 | + clientlogs/dialer_app/test_results.xml |
264 | |
265 | === added directory 'config' |
266 | === added file 'config/boottest.rc.example' |
267 | --- config/boottest.rc.example 1970-01-01 00:00:00 +0000 |
268 | +++ config/boottest.rc.example 2015-06-03 20:37:20 +0000 |
269 | @@ -0,0 +1,10 @@ |
270 | +# This file is sourced by boottest.sh |
271 | + |
272 | +# Example Config Follows - These are the current defaults |
273 | +# |
274 | +# REVISION=81 |
275 | +# ARCH=krillin |
276 | +# PHABLET_PASSWORD=0000 |
277 | +# ADT_TIMEOUT=600 |
278 | +# Disable rsync but echo the commands for feedback |
279 | +# RSYNC="echo rsync" |
280 | |
281 | === added directory 'jenkins' |
282 | === added file 'jenkins/custom-demo.py' |
283 | --- jenkins/custom-demo.py 1970-01-01 00:00:00 +0000 |
284 | +++ jenkins/custom-demo.py 2015-06-03 20:37:20 +0000 |
285 | @@ -0,0 +1,32 @@ |
286 | +# The configuration matrix of our production device testing |
287 | + |
288 | +JENKINS = 'http://q-jenkins.ubuntu-ci:8080' |
289 | + |
290 | + |
291 | +def _url(channel, device): |
292 | + return 'http://system-image.ubuntu.com/ubuntu-touch/%s/%s/index.json' \ |
293 | + % (channel, device) |
294 | + |
295 | + |
296 | +TRUSTY_MATRIX = [ |
297 | + { |
298 | + 'image-type': 'touch_custom_demo', |
299 | + 'include-qa': True, |
300 | + 'dashboard-host': 'ci.ubuntu.com', |
301 | + 'dashboard-port': '80', |
302 | + 'dashboard-user': 'doanac', |
303 | + 'devices': [ |
304 | + { |
305 | + 'name': 'mako', |
306 | + 'slave-label': 'daily-mako', |
307 | + 'trigger_url': _url('trusty-proposed-customized-demo', 'mako'), |
308 | + }, |
309 | + ], |
310 | + 'IMAGE_OPT': 'export IMAGE_OPT="ubuntu-system -b ' |
311 | + '--channel ubuntu-touch/trusty-proposed-customized-demo"' |
312 | + }, |
313 | +] |
314 | + |
315 | +MATRIX = { |
316 | + 'trusty': TRUSTY_MATRIX, |
317 | +} |
318 | |
319 | === added file 'jenkins/production-arale.py' |
320 | --- jenkins/production-arale.py 1970-01-01 00:00:00 +0000 |
321 | +++ jenkins/production-arale.py 2015-06-03 20:37:20 +0000 |
322 | @@ -0,0 +1,56 @@ |
323 | +# The configuration matrix of our staging device testing |
324 | + |
325 | +JENKINS = 'http://dev-jenkins.ubuntu-ci:8080/' |
326 | + |
327 | +VIVID_MATRIX = [ |
328 | + { |
329 | + 'image-type': 'touch_stable', |
330 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
331 | + 'include-qa': True, |
332 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
333 | + 'dashboard-port': '8080', |
334 | + 'dashboard-user': 'ci-bot', |
335 | + 'devices': [ |
336 | + { |
337 | + 'name': 'arale', |
338 | + 'slave-label': 'arale', |
339 | + 'trigger_url': 'http://system-image.ubuntu.com/' |
340 | + 'ubuntu-touch/rc-proposed/meizu.en/' |
341 | + 'arale/index.json', |
342 | + 'num-workers': 4, |
343 | + } |
344 | + ], |
345 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
346 | + '--channel=ubuntu-touch/rc-proposed/meizu.en ' |
347 | + '--device=arale"', |
348 | + }, |
349 | +] |
350 | + |
351 | +WILY_MATRIX = [ |
352 | + { |
353 | + 'image-type': 'touch', |
354 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
355 | + 'include-qa': True, |
356 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
357 | + 'dashboard-port': '8080', |
358 | + 'dashboard-user': 'ci-bot', |
359 | + 'devices': [ |
360 | + { |
361 | + 'name': 'arale', |
362 | + 'slave-label': 'arale', |
363 | + 'trigger_url': 'http://system-image.ubuntu.com/' |
364 | + 'ubuntu-touch/devel-proposed/meizu.en/' |
365 | + 'arale/index.json', |
366 | + 'num-workers': 4, |
367 | + } |
368 | + ], |
369 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
370 | + '--channel=ubuntu-touch/devel-proposed/meizu.en ' |
371 | + '--device=arale"', |
372 | + }, |
373 | +] |
374 | + |
375 | +MATRIX = { |
376 | + 'wily': WILY_MATRIX, |
377 | + 'vivid': VIVID_MATRIX, |
378 | +} |
379 | |
380 | === added file 'jenkins/production-krillin.py' |
381 | --- jenkins/production-krillin.py 1970-01-01 00:00:00 +0000 |
382 | +++ jenkins/production-krillin.py 2015-06-03 20:37:20 +0000 |
383 | @@ -0,0 +1,50 @@ |
384 | +# The configuration matrix of our staging device testing |
385 | + |
386 | +JENKINS = 'http://dev-jenkins.ubuntu-ci:8080/' |
387 | + |
388 | +VIVID_MATRIX = [ |
389 | + { |
390 | + 'image-type': 'touch_stable', |
391 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
392 | + 'include-qa': True, |
393 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
394 | + 'dashboard-port': '8080', |
395 | + 'dashboard-user': 'ci-bot', |
396 | + 'devices': [ |
397 | + { |
398 | + 'name': 'krillin', |
399 | + 'slave-label': 'krillin', |
400 | + 'trigger_url': 'http://system-image.ubuntu.com/ubuntu-touch/rc-proposed/bq-aquaris.en/krillin/index.json', |
401 | + 'num-workers': 4, |
402 | + } |
403 | + ], |
404 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
405 | + '--channel=ubuntu-touch/rc-proposed/bq-aquaris.en"' |
406 | + }, |
407 | +] |
408 | + |
409 | +WILY_MATRIX = [ |
410 | + { |
411 | + 'image-type': 'touch', |
412 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
413 | + 'include-qa': True, |
414 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
415 | + 'dashboard-port': '8080', |
416 | + 'dashboard-user': 'ci-bot', |
417 | + 'devices': [ |
418 | + { |
419 | + 'name': 'krillin', |
420 | + 'slave-label': 'krillin', |
421 | + 'trigger_url': 'http://system-image.ubuntu.com/ubuntu-touch/devel-proposed/krillin.en/index.json', |
422 | + 'num-workers': 4, |
423 | + } |
424 | + ], |
425 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
426 | + '--channel=ubuntu-touch/devel-proposed/krillin.en"' |
427 | + }, |
428 | +] |
429 | + |
430 | +MATRIX = { |
431 | + 'vivid': VIVID_MATRIX, |
432 | + 'wily': WILY_MATRIX, |
433 | +} |
434 | |
435 | === added file 'jenkins/production.py' |
436 | --- jenkins/production.py 1970-01-01 00:00:00 +0000 |
437 | +++ jenkins/production.py 2015-06-03 20:37:20 +0000 |
438 | @@ -0,0 +1,96 @@ |
439 | +# The configuration matrix of our production device testing |
440 | + |
441 | +JENKINS = 'http://q-jenkins.ubuntu-ci:8080' |
442 | + |
443 | + |
444 | +def _url(channel, device): |
445 | + return 'http://system-image.ubuntu.com/ubuntu-touch/%s/%s/index.json' \ |
446 | + % (channel, device) |
447 | + |
448 | + |
449 | +WILY_MATRIX = [ |
450 | + { |
451 | + 'image-type': 'touch', |
452 | + 'statsd-key': 'ubuntu-ci.daily-image.production', |
453 | + 'include-qa': True, |
454 | + 'dashboard-host': 'ci.ubuntu.com', |
455 | + 'dashboard-port': '80', |
456 | + 'dashboard-user': 'uci-bot', |
457 | + 'devices': [ |
458 | + { |
459 | + 'name': 'mako', |
460 | + 'slave-label': 'daily-mako', |
461 | + 'trigger_url': _url('devel-proposed/ubuntu', 'mako'), |
462 | + 'num-workers': 3, |
463 | + }, |
464 | + { |
465 | + 'name': 'flo', |
466 | + 'slave-label': 'daily-flo', |
467 | + 'trigger_url': _url('devel-proposed/ubuntu', 'flo'), |
468 | + 'num-workers': 2, |
469 | + }, |
470 | + { |
471 | + 'name': 'manta', |
472 | + 'slave-label': 'daily-manta', |
473 | + 'trigger_url': _url('devel-proposed/ubuntu', 'manta'), |
474 | + 'num-workers': 2, |
475 | + }, |
476 | + ], |
477 | + }, |
478 | +] |
479 | + |
480 | +VIVID_MATRIX = [ |
481 | + { |
482 | + 'image-type': 'touch_stable', |
483 | + 'statsd-key': 'ubuntu-ci.daily-image.production', |
484 | + 'include-qa': True, |
485 | + 'dashboard-host': 'ci.ubuntu.com', |
486 | + 'dashboard-port': '80', |
487 | + 'dashboard-user': 'uci-bot', |
488 | + 'devices': [ |
489 | + { |
490 | + 'name': 'mako', |
491 | + 'slave-label': 'daily-mako', |
492 | + 'trigger_url': _url('rc-proposed/ubuntu', 'mako'), |
493 | + 'num-workers': 3, |
494 | + }, |
495 | + { |
496 | + 'name': 'flo', |
497 | + 'slave-label': 'daily-flo', |
498 | + 'trigger_url': _url('rc-proposed/ubuntu', 'flo'), |
499 | + 'num-workers': 2, |
500 | + }, |
501 | + { |
502 | + 'name': 'manta', |
503 | + 'slave-label': 'daily-manta', |
504 | + 'trigger_url': _url('rc-proposed/ubuntu', 'manta'), |
505 | + 'num-workers': 2, |
506 | + }, |
507 | + ], |
508 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
509 | + '--channel ubuntu-touch/rc-proposed/ubuntu"' |
510 | + }, |
511 | + { |
512 | + 'image-type': 'touch_custom', |
513 | + 'statsd-key': 'ubuntu-ci.daily-image.production', |
514 | + 'include-qa': False, |
515 | + 'dashboard-host': 'ci.ubuntu.com', |
516 | + 'dashboard-port': '80', |
517 | + 'dashboard-user': 'uci-bot', |
518 | + 'devices': [ |
519 | + { |
520 | + 'name': 'mako', |
521 | + 'slave-label': 'daily-mako', |
522 | + 'trigger_url': _url('utopic-proposed-customized', 'mako'), |
523 | + }, |
524 | + ], |
525 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
526 | + '--channel ubuntu-touch/utopic-proposed-customized"' |
527 | + }, |
528 | +] |
529 | + |
530 | + |
531 | +MATRIX = { |
532 | + 'vivid': VIVID_MATRIX, |
533 | + 'wily': WILY_MATRIX, |
534 | +} |
535 | |
536 | === added file 'jenkins/setup_jenkins.py' |
537 | --- jenkins/setup_jenkins.py 1970-01-01 00:00:00 +0000 |
538 | +++ jenkins/setup_jenkins.py 2015-06-03 20:37:20 +0000 |
539 | @@ -0,0 +1,228 @@ |
540 | +#!/usr/bin/env python |
541 | + |
542 | +# Ubuntu Testing Automation Harness |
543 | +# Copyright 2013 Canonical Ltd. |
544 | + |
545 | +# This program is free software: you can redistribute it and/or modify it |
546 | +# under the terms of the GNU General Public License version 3, as published |
547 | +# by the Free Software Foundation. |
548 | + |
549 | +# This program is distributed in the hope that it will be useful, but |
550 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
551 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
552 | +# PURPOSE. See the GNU General Public License for more details. |
553 | + |
554 | +# You should have received a copy of the GNU General Public License along |
555 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
556 | + |
557 | +import argparse |
558 | +import imp |
559 | +import jenkins |
560 | +import jinja2 |
561 | +import logging |
562 | +import os |
563 | + |
564 | +import testconfig |
565 | + |
566 | +from distro_info import UbuntuDistroInfo |
567 | +DEV_SERIES = UbuntuDistroInfo().devel() |
568 | + |
569 | + |
570 | +def _get_parser(): |
571 | + """Create and return command line parser. |
572 | + |
573 | + :returns: command line parser |
574 | + :rtype: argparse.ArgumentParser |
575 | + |
576 | + """ |
577 | + parser = argparse.ArgumentParser( |
578 | + description='Create/Update upgrade testing jenkins jobs.') |
579 | + parser.add_argument("-d", "--dryrun", action="store_true", |
580 | + help="Dry run mode. Don't execute jenkins commands.") |
581 | + parser.add_argument("-u", "--username", |
582 | + help="username to use when logging into Jenkins.") |
583 | + parser.add_argument("-p", "--password", |
584 | + help="username to use when logging into Jenkins") |
585 | + parser.add_argument("--dashboard-key", default="", |
586 | + help="The api-key for dashboard updates") |
587 | + parser.add_argument("--nfss-config", default="", |
588 | + help="The path to the config file for nfss insertion") |
589 | + parser.add_argument("-b", "--branch", default="lp:ubuntu-test-cases/touch", |
590 | + help="The branch this is located. default=%(default)s") |
591 | + parser.add_argument("-c", "--config", required=True, |
592 | + type=argparse.FileType('r'), |
593 | + help="The job config to use.") |
594 | + parser.add_argument("-P", "--publish", action="store_true", |
595 | + help="Publish at the end of the job") |
596 | + parser.add_argument("--prefix", |
597 | + help="Prefix to add to the beginning of the job name") |
598 | + parser.add_argument("-s", "--series", default=DEV_SERIES, |
599 | + help=("series of Ubuntu to download " |
600 | + "(default=%(default)s)")) |
601 | + parser.add_argument("-w", "--wait", type=int, default=300, |
602 | + help=("How long to wait after jenkins triggers the" |
603 | + "install-and-boot job (default=%(default)d)")) |
604 | + return parser |
605 | + |
606 | + |
607 | +def _get_jenkins(url, username, password): |
608 | + logging.info('Attempting to login to jenkins at %s', url) |
609 | + if username is not None: |
610 | + logging.info('...with authentication as user: %s', username) |
611 | + instance = jenkins.Jenkins(url, username=username, password=password) |
612 | + else: |
613 | + logging.info('...without authentication') |
614 | + instance = jenkins.Jenkins(url) |
615 | + |
616 | + return instance |
617 | + |
618 | + |
619 | +def _get_environment(): |
620 | + base = os.path.join(os.path.dirname(__file__), 'templates') |
621 | + return jinja2.Environment( |
622 | + loader=jinja2.FileSystemLoader(base), |
623 | + undefined=jinja2.StrictUndefined, |
624 | + trim_blocks=True) |
625 | + |
626 | + |
627 | +def _publish(instance, env, args, template, jobname, **params): |
628 | + tmpl = env.get_template(template) |
629 | + cfg = tmpl.render(**params) |
630 | + if args.dryrun: |
631 | + _dryrun_func(jobname, cfg) |
632 | + return |
633 | + if instance.job_exists(jobname): |
634 | + logging.info('reconfiguring job %s', jobname) |
635 | + instance.reconfig_job(jobname, cfg) |
636 | + else: |
637 | + logging.info('creating job %s', jobname) |
638 | + instance.create_job(jobname, cfg) |
639 | + |
640 | + |
641 | +def _configure_smoke(instance, env, args, config_item, device): |
642 | + defserial = '$(${BZRDIR}/scripts/get-adb-id ${NODE_NAME})' |
643 | + |
644 | + params = { |
645 | + 'name': device['slave-label'], |
646 | + 'device_type': device['name'], |
647 | + 'serial': device.get('serial', defserial), |
648 | + 'publish': args.publish, |
649 | + 'branch': args.branch, |
650 | + 'imagetype': config_item['image-type'], |
651 | + 'series': args.series, |
652 | + 'image_opt': config_item.get('IMAGE_OPT', ''), |
653 | + 'image_server': config_item.get('IMAGE_SERVER', ''), |
654 | + 'statsd_key': config_item.get('statsd-key', ''), |
655 | + 'dashboard_host': config_item.get('dashboard-host', ''), |
656 | + 'dashboard_port': config_item.get('dashboard-port', ''), |
657 | + 'dashboard_prefix': config_item.get('dashboard-prefix', ''), |
658 | + 'dashboard_user': config_item.get('dashboard-user', ''), |
659 | + 'dashboard_key': args.dashboard_key, |
660 | + 'nfss_config': args.nfss_config, |
661 | + } |
662 | + |
663 | + prefix = "" |
664 | + if(args.prefix): |
665 | + prefix = args.prefix + "-" |
666 | + |
667 | + job = '{}{}-{}-{}-smoke-daily'.format( |
668 | + prefix, args.series, config_item['image-type'], device['name']) |
669 | + _publish(instance, env, args, 'touch-smoke.xml.jinja2', job, **params) |
670 | + return job |
671 | + |
672 | + |
673 | +def _configure_qa_job(instance, env, args, config_item, device, test): |
674 | + defserial = '$(${BZRDIR}/scripts/get-adb-id ${NODE_NAME})' |
675 | + # If the slave is specified for this test, set it |
676 | + slave = getattr(test, 'device', device['slave-label']) |
677 | + params = { |
678 | + 'name': slave, |
679 | + 'device_type': device['name'], |
680 | + 'serial': device.get('serial', defserial), |
681 | + 'publish': args.publish, |
682 | + 'branch': args.branch, |
683 | + 'imagetype': config_item['image-type'], |
684 | + 'series': args.series, |
685 | + 'image_opt': config_item.get('IMAGE_OPT', ''), |
686 | + 'image_server': config_item.get('IMAGE_SERVER', ''), |
687 | + 'timeout': test.timeout, |
688 | + 'test': test.name, |
689 | + 'nfss_config': args.nfss_config, |
690 | + } |
691 | + prefix = "" |
692 | + if(args.prefix): |
693 | + prefix = args.prefix + "-" |
694 | + job = test.fmt.format(prefix=prefix, |
695 | + series=args.series, |
696 | + testname=test.name, |
697 | + imagetype=config_item['image-type'], |
698 | + type=device['name']) |
699 | + _publish(instance, env, args, 'touch-base.xml.jinja2', job, **params) |
700 | + return job |
701 | + |
702 | + |
703 | +def _configure_qa_jobs(instance, env, args, config_item, device): |
704 | + tests = list(testconfig.TESTSUITES) |
705 | + tests = testconfig.filter_tests(tests, config_item['image-type'], |
706 | + device['name']) |
707 | + tests = [t for t in tests if not t.smoke] |
708 | + jobs = [] |
709 | + for t in tests: |
710 | + j = _configure_qa_job(instance, env, args, config_item, device, t) |
711 | + jobs.append(j) |
712 | + return jobs |
713 | + |
714 | + |
715 | +def _configure_master(instance, env, args, config_item, device, smoke, jobs): |
716 | + params = { |
717 | + 'branch': args.branch, |
718 | + 'trigger_url': device.get('trigger_url', ''), |
719 | + 'wait': args.wait, |
720 | + 'projects': jobs, |
721 | + 'smoke_job': smoke, |
722 | + 'num_workers': device.get('num-workers', 1), |
723 | + } |
724 | + prefix = "" |
725 | + if(args.prefix): |
726 | + prefix = args.prefix + "-" |
727 | + job = testconfig.DEF_FMT.format(prefix=prefix, |
728 | + series=args.series, |
729 | + testname='master', |
730 | + imagetype=config_item['image-type'], |
731 | + type=device['name']) |
732 | + _publish(instance, env, args, 'touch-master.xml.jinja2', job, **params) |
733 | + return job |
734 | + |
735 | + |
736 | +def _dryrun_func(jobname, config): |
737 | + logging.debug(jobname) |
738 | + logging.debug(config) |
739 | + |
740 | + |
741 | +def main(): |
742 | + logging.basicConfig(level=logging.DEBUG) |
743 | + args = _get_parser().parse_args() |
744 | + |
745 | + config = imp.load_source('', 'config.py', args.config) |
746 | + if args.series not in config.MATRIX: |
747 | + print('"%s" series is not supported by this config.' % args.series) |
748 | + exit(1) |
749 | + |
750 | + jenkins_inst = _get_jenkins(config.JENKINS, args.username, args.password) |
751 | + if args.dryrun: |
752 | + jenkins_inst.create_job = _dryrun_func |
753 | + jenkins_inst.reconfig_job = _dryrun_func |
754 | + |
755 | + env = _get_environment() |
756 | + |
757 | + for item in config.MATRIX[args.series]: |
758 | + for device in item['devices']: |
759 | + job = _configure_smoke(jenkins_inst, env, args, item, device) |
760 | + jobs = [] |
761 | + if item.get('include-qa'): |
762 | + jobs = _configure_qa_jobs( |
763 | + jenkins_inst, env, args, item, device) |
764 | + _configure_master(jenkins_inst, env, args, item, device, job, jobs) |
765 | + |
766 | +if __name__ == '__main__': |
767 | + main() |
768 | |
769 | === added file 'jenkins/staging.py' |
770 | --- jenkins/staging.py 1970-01-01 00:00:00 +0000 |
771 | +++ jenkins/staging.py 2015-06-03 20:37:20 +0000 |
772 | @@ -0,0 +1,42 @@ |
773 | +# The configuration matrix of our staging device testing |
774 | + |
775 | +JENKINS = 'http://dev-jenkins.ubuntu-ci:8080/' |
776 | + |
777 | +UTOPIC_MATRIX = [ |
778 | + { |
779 | + 'image-type': 'touch', |
780 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
781 | + 'include-qa': True, |
782 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
783 | + 'dashboard-port': '8080', |
784 | + 'dashboard-user': 'ci-bot', |
785 | + 'devices': [ |
786 | + { |
787 | + 'name': 'mako', |
788 | + 'slave-label': 'mako', |
789 | + #'trigger_url': 'http://system-image.ubuntu.com/utopic-proposed/mako/index.json', |
790 | + } |
791 | + ], |
792 | + }, |
793 | + { |
794 | + 'image-type': 'touch_stable', |
795 | + 'statsd-key': 'ubuntu-ci.daily-image.staging', |
796 | + 'include-qa': True, |
797 | + 'dashboard-host': 'dashboard.ubuntu-ci', |
798 | + 'dashboard-port': '8080', |
799 | + 'dashboard-user': 'ci-bot', |
800 | + 'devices': [ |
801 | + { |
802 | + 'name': 'mako', |
803 | + 'slave-label': 'mako', |
804 | + #'trigger_url': 'http://system-image.ubuntu.com/utopic-proposed/mako/index.json', |
805 | + } |
806 | + ], |
807 | + 'IMAGE_OPT': 'export IMAGE_OPT="--bootstrap --developer-mode ' |
808 | + '--channel ubuntu-touch/staging-stable-proposed"' |
809 | + }, |
810 | +] |
811 | + |
812 | +MATRIX = { |
813 | + 'utopic': UTOPIC_MATRIX, |
814 | +} |
815 | |
816 | === added directory 'jenkins/templates' |
817 | === added file 'jenkins/templates/touch-base.xml.jinja2' |
818 | --- jenkins/templates/touch-base.xml.jinja2 1970-01-01 00:00:00 +0000 |
819 | +++ jenkins/templates/touch-base.xml.jinja2 2015-06-03 20:37:20 +0000 |
820 | @@ -0,0 +1,130 @@ |
821 | +<?xml version='1.0' encoding='UTF-8'?> |
822 | +<project> |
823 | + <actions/> |
824 | + <description> |
825 | +<pre> |
826 | +#NOTE: Automatically created from a script as part of daily smoke testing
 |
827 | + {{branch}}
 |
828 | +</pre> |
829 | + </description> |
830 | + <keepDependencies>false</keepDependencies> |
831 | + <properties> |
832 | + <hudson.model.ParametersDefinitionProperty> |
833 | + <parameterDefinitions> |
834 | + <hudson.model.StringParameterDefinition> |
835 | + <name>INSTALL_URL</name> |
836 | + <description>A URL to the previous job. If provided this job will use the same install options as it used. If the device executing the job happens to have the exact same image, then provisioning can be skipped. |
837 | + </description> |
838 | + <defaultValue></defaultValue> |
839 | + </hudson.model.StringParameterDefinition> |
840 | + </parameterDefinitions> |
841 | + </hudson.model.ParametersDefinitionProperty> |
842 | + <com.sonyericsson.rebuild.RebuildSettings> |
843 | + <autoRebuild>false</autoRebuild> |
844 | + </com.sonyericsson.rebuild.RebuildSettings> |
845 | + </properties> |
846 | + <scm class="hudson.scm.NullSCM"/> |
847 | + <assignedNode>{{ name }}</assignedNode> |
848 | + <canRoam>false</canRoam> |
849 | + <disabled>false</disabled> |
850 | + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding> |
851 | + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding> |
852 | + <concurrentBuild>true</concurrentBuild> |
853 | + <builders> |
854 | + <hudson.tasks.Shell> |
855 | + <command><![CDATA[ |
856 | +set -e |
857 | +sudo rm -rf * |
858 | + |
859 | +BRANCH="{{branch}}" |
860 | +BZRDIR=`echo "$BRANCH" | awk -F/ '{ print $(NF) }'` |
861 | +BZRDIR=$(readlink -f $BZRDIR) |
862 | +bzr branch ${BRANCH} ${BZRDIR} |
863 | + |
864 | +export ANDROID_SERIAL={{serial}} |
865 | +export IMAGE_TYPE={{imagetype}} |
866 | +export IMAGE_SERIES={{series}} |
867 | +export DEVICE_TYPE={{device_type}} |
868 | + |
869 | +${BZRDIR}/scripts/recover.py ${NODE_NAME} |
870 | + |
871 | +{{image_opt}} |
872 | +{{image_server}} |
873 | +${BZRDIR}/scripts/run-smoke -t {{test}} |
874 | + |
875 | +# move results to base directory as expected by dashboard: |
876 | +mv clientlogs/{{test}}/* ./clientlogs/ |
877 | +rm -rf clientlogs/{{test}} |
878 | + |
879 | +{% if test == 'health-check' %} |
880 | +# Post to nfss |
881 | +if [ "$NFSS_CONFIG" ] ; then |
882 | + for FILE in clientlogs/*.json ; do |
883 | + cat "$FILE" | nfss_insert.py "$NFSS_CONFIG" healthcheck $DEVICE_TYPE |
884 | + done |
885 | +fi |
886 | +{% endif %} |
887 | +{% if test == 'memevent' %} |
888 | +adb shell ls /tmp/memory_usage*.json | tr '\r' ' ' | sed 's/ $//' | xargs -n1 -I {} adb pull {} ./clientlogs/ |
889 | +{% if nfss_config %} |
890 | +NFSS_CONFIG_FILE=${WORKSPACE}/nfss_config |
891 | +cat - > ${NFSS_CONFIG_FILE} <<EOD |
892 | +{{nfss_config}} |
893 | +EOD |
894 | +python3 ${BZRDIR}/tests/memevent/nfss_upload_results.py ./clientlogs/ nfss_insert.py ${NFSS_CONFIG_FILE} |
895 | +{% endif %} |
896 | +{% endif %} |
897 | + ]]></command> |
898 | + </hudson.tasks.Shell> |
899 | + </builders> |
900 | + <publishers> |
901 | + <hudson.tasks.ArtifactArchiver> |
902 | + <artifacts>clientlogs/*, clientlogs/**/*</artifacts> |
903 | + <latestOnly>false</latestOnly> |
904 | + </hudson.tasks.ArtifactArchiver> |
905 | + <hudson.plugins.descriptionsetter.DescriptionSetterPublisher> |
906 | + <regexp>^= TOUCH IMAGE VERSION:([0-9]+.*)</regexp> |
907 | + <regexpForFailed>^= TOUCH IMAGE VERSION:([0-9]+.*)</regexpForFailed> |
908 | + <setForMatrix>false</setForMatrix> |
909 | + </hudson.plugins.descriptionsetter.DescriptionSetterPublisher> |
910 | + <hudson.tasks.Mailer> |
911 | + <recipients>paul.larson@canonical.com para.siva@canonical.com</recipients> |
912 | + <dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild> |
913 | + <sendToIndividuals>false</sendToIndividuals> |
914 | + </hudson.tasks.Mailer> |
915 | + <hudson.plugins.postbuildtask.PostbuildTask> |
916 | + <tasks> |
917 | + <hudson.plugins.postbuildtask.TaskProperties> |
918 | + <logTexts> |
919 | + <hudson.plugins.postbuildtask.LogProperties> |
920 | + <logText/> |
921 | + <operator>AND</operator> |
922 | + </hudson.plugins.postbuildtask.LogProperties> |
923 | + </logTexts> |
924 | + <EscalateStatus>false</EscalateStatus> |
925 | + <RunIfJobSuccessful>false</RunIfJobSuccessful> |
926 | + <script> |
927 | +touch/scripts/recover.py ${NODE_NAME} |
928 | + </script> |
929 | + </hudson.plugins.postbuildtask.TaskProperties> |
930 | + </tasks> |
931 | + </hudson.plugins.postbuildtask.PostbuildTask> |
932 | +{% if publish %} |
933 | + <hudson.plugins.build__publisher.BuildPublisher> |
934 | + <publishUnstableBuilds>true</publishUnstableBuilds> |
935 | + <publishFailedBuilds>true</publishFailedBuilds> |
936 | + <postActions class="vector"/> |
937 | + </hudson.plugins.build__publisher.BuildPublisher> |
938 | +{% endif %} |
939 | + </publishers> |
940 | + <buildWrappers> |
941 | + <hudson.plugins.build__timeout.BuildTimeoutWrapper> |
942 | + <timeoutMinutes>{{timeout}}</timeoutMinutes> |
943 | + <failBuild>true</failBuild> |
944 | + <writingDescription>false</writingDescription> |
945 | + <timeoutPercentage>0</timeoutPercentage> |
946 | + <timeoutType>absolute</timeoutType> |
947 | + <timeoutMinutesElasticDefault>3</timeoutMinutesElasticDefault> |
948 | + </hudson.plugins.build__timeout.BuildTimeoutWrapper> |
949 | + </buildWrappers> |
950 | +</project> |
951 | |
952 | === added file 'jenkins/templates/touch-master.xml.jinja2' |
953 | --- jenkins/templates/touch-master.xml.jinja2 1970-01-01 00:00:00 +0000 |
954 | +++ jenkins/templates/touch-master.xml.jinja2 2015-06-03 20:37:20 +0000 |
955 | @@ -0,0 +1,80 @@ |
956 | +<?xml version='1.0' encoding='UTF-8'?> |
957 | +<com.cloudbees.plugins.flow.BuildFlow> |
958 | + <actions/> |
959 | + <description> |
960 | +<pre> |
961 | +#NOTE: Automatically created from a script as part of daily smoke testing
 |
962 | + {{branch}}
 |
963 | +</pre> |
964 | + </description> |
965 | + <keepDependencies>false</keepDependencies> |
966 | + <properties> |
967 | + <hudson.model.ParametersDefinitionProperty> |
968 | + <parameterDefinitions> |
969 | + <hudson.model.StringParameterDefinition> |
970 | + <name>sleep</name> |
971 | + <description>Seconds to sleep before starting the job |
972 | + </description> |
973 | + <defaultValue>{{ wait }}</defaultValue> |
974 | + </hudson.model.StringParameterDefinition> |
975 | + </parameterDefinitions> |
976 | + </hudson.model.ParametersDefinitionProperty> |
977 | + <hudson.plugins.throttleconcurrents.ThrottleJobProperty> |
978 | + <maxConcurrentPerNode>0</maxConcurrentPerNode> |
979 | + <maxConcurrentTotal>0</maxConcurrentTotal> |
980 | + <throttleEnabled>false</throttleEnabled> |
981 | + <throttleOption>project</throttleOption> |
982 | + </hudson.plugins.throttleconcurrents.ThrottleJobProperty> |
983 | + <hudson.plugins.build__publisher.ExternalProjectProperty/> |
984 | + </properties> |
985 | + <scm class="hudson.scm.NullSCM"/> |
986 | + <assignedNode>master</assignedNode> |
987 | + <canRoam>false</canRoam> |
988 | + <disabled>false</disabled> |
989 | + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding> |
990 | + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding> |
991 | +{%if trigger_url %} |
992 | + <triggers class="vector"> |
993 | + <com.redfin.hudson.UrlChangeTrigger> |
994 | + <spec></spec> |
995 | + <url>{{trigger_url}}</url> |
996 | + </com.redfin.hudson.UrlChangeTrigger> |
997 | + </triggers> |
998 | +{% endif %} |
999 | + <concurrentBuild>false</concurrentBuild> |
1000 | + <builders/> |
1001 | + <publishers/> |
1002 | + <buildWrappers> |
1003 | + <hudson.plugins.build__timeout.BuildTimeoutWrapper> |
1004 | + <timeoutMinutes>300</timeoutMinutes> |
1005 | + <failBuild>true</failBuild> |
1006 | + <writingDescription>false</writingDescription> |
1007 | + <timeoutPercentage>0</timeoutPercentage> |
1008 | + <timeoutType>absolute</timeoutType> |
1009 | + <timeoutMinutesElasticDefault>3</timeoutMinutesElasticDefault> |
1010 | + </hudson.plugins.build__timeout.BuildTimeoutWrapper> |
1011 | + </buildWrappers> |
1012 | + <dsl><![CDATA[ |
1013 | +// give the image time to show up before running tests |
1014 | +out.println("sleeping for a bit") |
1015 | +def sleep = build.environment.get("sleep").toLong() |
1016 | +build.sleep(sleep * 1000) |
1017 | + |
1018 | +ignore(UNSTABLE) { |
1019 | + join = parallel ([ |
1020 | +{% for x in range(num_workers) %} |
1021 | + worker_{{x}}: { build("{{smoke_job}}", worker_idx: {{x}}, workers: {{num_workers}}) }, |
1022 | +{% endfor %} |
1023 | + ]) |
1024 | +} |
1025 | +{% if projects %} |
1026 | +out.println("kicking off downstream projects in parellel") |
1027 | +install_url = build.environment.get("HUDSON_URL") + join.worker_0.lastBuild.build.url |
1028 | +parallel ( |
1029 | +{% for project in projects %} |
1030 | + {build("{{project}}", INSTALL_URL: install_url)}, |
1031 | +{% endfor %} |
1032 | +) |
1033 | +{% endif %} |
1034 | + ]]></dsl> |
1035 | +</com.cloudbees.plugins.flow.BuildFlow> |
1036 | |
1037 | === added file 'jenkins/templates/touch-smoke.xml.jinja2' |
1038 | --- jenkins/templates/touch-smoke.xml.jinja2 1970-01-01 00:00:00 +0000 |
1039 | +++ jenkins/templates/touch-smoke.xml.jinja2 2015-06-03 20:37:20 +0000 |
1040 | @@ -0,0 +1,199 @@ |
1041 | +<?xml version='1.0' encoding='UTF-8'?> |
1042 | +<project> |
1043 | + <actions/> |
1044 | + <description> |
1045 | +<![CDATA[ |
1046 | +This job provides a flexible way to execute all the tests in our daily image |
1047 | +testing process that contribute to the |
1048 | +<a href="http://reports.qa.ubuntu.com/smokeng/">QA Dashboard</a>. |
1049 | +The job is parameterized to give flexibility in what gets tested. A couple of |
1050 | +common ways to run this job are: |
1051 | +<dl> |
1052 | + <dt>Full Test Run</dt> |
1053 | + <dd>TESTS=ALL</dd> |
1054 | + <dd>APPS=ALL</dd> |
1055 | + <dt>Re-run a Failed Autopilot Test</dt> |
1056 | + <dd>INSTALL_URL=http://dev-jenkins:8080/job/utopic-touch_ro-mako/9</dd> |
1057 | + <dd>APPS=dropping_letters_app</dd> |
1058 | + <dt>Re-run a Failed UTAH Test</dt> |
1059 | + <dd>INSTALL_URL=http://dev-jenkins:8080/job/utopic-touch_ro-mako/9</dd> |
1060 | + <dd>TESTS=security</dd> |
1061 | +</dl> |
1062 | +<pre> |
1063 | +#NOTE: Automatically created from a script as part of daily smoke testing |
1064 | + {{branch}} |
1065 | +</pre> |
1066 | +{% if statsd_key %} |
1067 | +<h3>Graphite Reports</h3> |
1068 | +<ul> |
1069 | + <li><a href="https://graphite.engineering.canonical.com/graphlot/?width=841&height=770&&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.provision.mean%2C0.000016667%29%2C%22Provisioning%20Time(minutes)%22%29&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.APPS.mean%2C0.000016667%29%2C%22Autopilot%20Tests%20Time%22%29&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.TESTS.mean%2C0.000016667%29%2C%22UTAH%20Tests%20Time%22%29&from=-1weeks">Timings (1 week)</a></li> |
1070 | + <li><a href="https://graphite.engineering.canonical.com/graphlot/?width=841&height=770&&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.provision.mean%2C0.000016667%29%2C%22Provisioning%20Time(minutes)%22%29&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.APPS.mean%2C0.000016667%29%2C%22Autopilot%20Tests%20Time%22%29&target=alias%28scale%28statsd.{{statsd_key}}.{{imagetype}}.TESTS.mean%2C0.000016667%29%2C%22UTAH%20Tests%20Time%22%29&from=-4weeks">Timings (4 weeks)</a></li> |
1071 | +</ul> |
1072 | +{% endif %} |
1073 | +]]> |
1074 | + </description> |
1075 | + <keepDependencies>false</keepDependencies> |
1076 | + <properties> |
1077 | + <hudson.model.ParametersDefinitionProperty> |
1078 | + <parameterDefinitions> |
1079 | + <hudson.model.StringParameterDefinition> |
1080 | + <name>INSTALL_URL</name> |
1081 | + <description>A URL to the previous job. If provided this job will use the same install options as it used. If the device executing the job happens to have the exact same image, then provisioning can be skipped. |
1082 | + </description> |
1083 | + <defaultValue></defaultValue> |
1084 | + </hudson.model.StringParameterDefinition> |
1085 | + <hudson.model.StringParameterDefinition> |
1086 | + <name>TESTS</name> |
1087 | + <description>A space separated list of utah tests to run. "ALL" can be used to run all known utah tests. |
1088 | + </description> |
1089 | + <defaultValue>ALL</defaultValue> |
1090 | + </hudson.model.StringParameterDefinition> |
1091 | + <hudson.model.StringParameterDefinition> |
1092 | + <name>APPS</name> |
1093 | + <description>A space separated list of autopilot tests to run. "ALL" can be used to run all known tests. |
1094 | + </description> |
1095 | + <defaultValue>ALL</defaultValue> |
1096 | + </hudson.model.StringParameterDefinition> |
1097 | + <hudson.model.StringParameterDefinition> |
1098 | + <name>REVISION</name> |
1099 | + <description>The image revision to test with. |
1100 | + </description> |
1101 | + <defaultValue></defaultValue> |
1102 | + </hudson.model.StringParameterDefinition> |
1103 | + <hudson.model.StringParameterDefinition> |
1104 | + <name>workers</name> |
1105 | + <description>The number of workers |
1106 | + </description> |
1107 | + <defaultValue></defaultValue> |
1108 | + </hudson.model.StringParameterDefinition> |
1109 | + <hudson.model.StringParameterDefinition> |
1110 | + <name>worker_idx</name> |
1111 | + <description>The index of this worker |
1112 | + </description> |
1113 | + <defaultValue></defaultValue> |
1114 | + </hudson.model.StringParameterDefinition> |
1115 | + </parameterDefinitions> |
1116 | + </hudson.model.ParametersDefinitionProperty> |
1117 | + <com.sonyericsson.rebuild.RebuildSettings> |
1118 | + <autoRebuild>false</autoRebuild> |
1119 | + </com.sonyericsson.rebuild.RebuildSettings> |
1120 | + </properties> |
1121 | + <scm class="hudson.scm.NullSCM"/> |
1122 | + <assignedNode>{{ name }}</assignedNode> |
1123 | + <canRoam>false</canRoam> |
1124 | + <disabled>false</disabled> |
1125 | + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding> |
1126 | + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding> |
1127 | + <triggers class="vector"/> |
1128 | + <concurrentBuild>true</concurrentBuild> |
1129 | + <builders> |
1130 | + <hudson.tasks.Shell> |
1131 | + <command>set -e |
1132 | +BRANCH="{{branch}}" |
1133 | +BZRDIR=`echo "$BRANCH" | awk -F/ '{ print $(NF) }'` |
1134 | +BZRDIR=$(readlink -f $BZRDIR) |
1135 | +[ -d $BZRDIR ] && rm -rf $BZRDIR |
1136 | +bzr branch ${BRANCH} ${BZRDIR} |
1137 | + |
1138 | +export ANDROID_SERIAL={{serial}} |
1139 | +export IMAGE_TYPE={{imagetype}} |
1140 | +export IMAGE_SERIES={{series}} |
1141 | +export DEVICE_TYPE={{device_type}} |
1142 | + |
1143 | +${BZRDIR}/scripts/recover.py ${NODE_NAME} |
1144 | + |
1145 | +{% if statsd_key %} |
1146 | +# the txstatsd package is too old, use the one from LP: |
1147 | +[ -d txstatsd ] && rm -rf txstatsd |
1148 | +bzr branch -r 109 lp:txstatsd |
1149 | +export PYTHONPATH=`pwd`/txstatsd |
1150 | + |
1151 | +export STATSD_KEY={{statsd_key}}.{{imagetype}} |
1152 | +{% endif %} |
1153 | + |
1154 | +{% if dashboard_host %} |
1155 | +export DASHBOARD_HOST="{{dashboard_host}}" |
1156 | +{% endif %} |
1157 | +{% if dashboard_user %} |
1158 | +export DASHBOARD_USER="{{dashboard_user}}" |
1159 | +{% endif %} |
1160 | +{% if dashboard_key %} |
1161 | +set +x # don't let this leak into the public |
1162 | +export DASHBOARD_KEY="{{dashboard_key}}" |
1163 | +set -x |
1164 | +{% endif %} |
1165 | +{% if dashboard_prefix %} |
1166 | +export DASHBOARD_PREFIX="{{dashboard_prefix}}" |
1167 | +{% endif %} |
1168 | +{% if dashboard_port %} |
1169 | +export DASHBOARD_PORT="{{dashboard_port}}" |
1170 | +{% endif %} |
1171 | +{% if nfss_config %} |
1172 | +export NFSS_CONFIG="{{nfss_config}}" |
1173 | +{% endif %} |
1174 | + |
1175 | +{{image_opt}} |
1176 | +{{image_server}} |
1177 | +${BZRDIR}/scripts/run-smoke |
1178 | + </command> |
1179 | + </hudson.tasks.Shell> |
1180 | + </builders> |
1181 | + <publishers> |
1182 | + <hudson.tasks.ArtifactArchiver> |
1183 | + <artifacts>clientlogs/**</artifacts> |
1184 | + <latestOnly>false</latestOnly> |
1185 | + </hudson.tasks.ArtifactArchiver> |
1186 | + <hudson.tasks.junit.JUnitResultArchiver> |
1187 | + <testResults>clientlogs/**/*.xml</testResults> |
1188 | + <keepLongStdio>true</keepLongStdio> |
1189 | + <testDataPublishers> |
1190 | + <hudson.plugins.junitattachments.AttachmentPublisher/> |
1191 | + </testDataPublishers> |
1192 | + </hudson.tasks.junit.JUnitResultArchiver> |
1193 | + <hudson.plugins.descriptionsetter.DescriptionSetterPublisher> |
1194 | + <regexp>^= TOUCH IMAGE VERSION:([0-9]+.*)</regexp> |
1195 | + <regexpForFailed>^= TOUCH IMAGE VERSION:([0-9]+.*)</regexpForFailed> |
1196 | + <setForMatrix>false</setForMatrix> |
1197 | + </hudson.plugins.descriptionsetter.DescriptionSetterPublisher> |
1198 | + <hudson.plugins.postbuildtask.PostbuildTask> |
1199 | + <tasks> |
1200 | + <hudson.plugins.postbuildtask.TaskProperties> |
1201 | + <logTexts> |
1202 | + <hudson.plugins.postbuildtask.LogProperties> |
1203 | + <logText/> |
1204 | + <operator>AND</operator> |
1205 | + </hudson.plugins.postbuildtask.LogProperties> |
1206 | + </logTexts> |
1207 | + <EscalateStatus>false</EscalateStatus> |
1208 | + <RunIfJobSuccessful>false</RunIfJobSuccessful> |
1209 | + <script> |
1210 | +touch/scripts/recover.py ${NODE_NAME} |
1211 | + </script> |
1212 | + </hudson.plugins.postbuildtask.TaskProperties> |
1213 | + </tasks> |
1214 | + </hudson.plugins.postbuildtask.PostbuildTask> |
1215 | +{% if publish %} |
1216 | + <hudson.tasks.Mailer> |
1217 | + <recipients>paul.larson@canonical.com</recipients> |
1218 | + <dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild> |
1219 | + <sendToIndividuals>false</sendToIndividuals> |
1220 | + </hudson.tasks.Mailer> |
1221 | + <hudson.plugins.build__publisher.BuildPublisher> |
1222 | + <publishUnstableBuilds>true</publishUnstableBuilds> |
1223 | + <publishFailedBuilds>true</publishFailedBuilds> |
1224 | + <postActions class="vector"/> |
1225 | + </hudson.plugins.build__publisher.BuildPublisher> |
1226 | +{% endif %} |
1227 | + </publishers> |
1228 | + <buildWrappers> |
1229 | + <hudson.plugins.build__timeout.BuildTimeoutWrapper> |
1230 | + <timeoutMinutes>300</timeoutMinutes> |
1231 | + <failBuild>true</failBuild> |
1232 | + <writingDescription>false</writingDescription> |
1233 | + <timeoutPercentage>0</timeoutPercentage> |
1234 | + <timeoutType>absolute</timeoutType> |
1235 | + <timeoutMinutesElasticDefault>3</timeoutMinutesElasticDefault> |
1236 | + </hudson.plugins.build__timeout.BuildTimeoutWrapper> |
1237 | + </buildWrappers> |
1238 | +</project> |
1239 | + |
1240 | |
1241 | === added file 'jenkins/testconfig.py' |
1242 | --- jenkins/testconfig.py 1970-01-01 00:00:00 +0000 |
1243 | +++ jenkins/testconfig.py 2015-06-03 20:37:20 +0000 |
1244 | @@ -0,0 +1,242 @@ |
1245 | +#!/usr/bin/env python |
1246 | + |
1247 | +# Ubuntu Testing Automation Harness |
1248 | +# Copyright 2013 Canonical Ltd. |
1249 | + |
1250 | +# This program is free software: you can redistribute it and/or modify it |
1251 | +# under the terms of the GNU General Public License version 3, as published |
1252 | +# by the Free Software Foundation. |
1253 | + |
1254 | +# This program is distributed in the hope that it will be useful, but |
1255 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1256 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1257 | +# PURPOSE. See the GNU General Public License for more details. |
1258 | + |
1259 | +# You should have received a copy of the GNU General Public License along |
1260 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1261 | + |
1262 | +import argparse |
1263 | + |
1264 | +DEF_FMT = '{prefix}{series}-{imagetype}-{type}-smoke-{testname}' |
1265 | +IDLE_FMT = '{prefix}{testname}-{series}-{imagetype}-armhf-install-idle-{type}' |
1266 | + |
1267 | + |
1268 | +class Test(object): |
1269 | + def __init__(self, name, timeout=60, fmt=DEF_FMT, smoke=True): |
1270 | + self.name = name |
1271 | + self.timeout = timeout |
1272 | + self.fmt = fmt |
1273 | + self.smoke = smoke |
1274 | + |
1275 | + def __repr__(self): |
1276 | + return self.name |
1277 | + |
1278 | + |
1279 | +class DevTest(Test): |
1280 | + def __init__(self, name, device): |
1281 | + Test.__init__(self, name, fmt=IDLE_FMT, smoke=False) |
1282 | + self.device = device |
1283 | + |
1284 | + |
1285 | +class APTest(Test): |
1286 | + def __init__(self, name, app=None, pkgs=None): |
1287 | + Test.__init__(self, name) |
1288 | + self.pkgs = pkgs |
1289 | + if not app: |
1290 | + # convert share-app-autopilot to share_app |
1291 | + app = name.replace('-', '_').replace('_autopilot', '') |
1292 | + self.app = app |
1293 | + |
1294 | + |
1295 | +TESTSUITES = [ |
1296 | + Test('default'), |
1297 | +] |
1298 | +TESTSUITES += [ |
1299 | + APTest('mediaplayer-app-autopilot', pkgs=['mediaplayer-app-autopilot']), |
1300 | + APTest('gallery-app-autopilot'), |
1301 | + APTest('webbrowser-app-autopilot', pkgs=['webbrowser-app-autopilot']), |
1302 | + APTest('webapp-container-autopilot', pkgs=['webapp-container-autopilot']), |
1303 | + APTest('unity8-autopilot', 'unity8'), |
1304 | + APTest('camera-app-autopilot', |
1305 | + pkgs=['python3-wand', 'python3-mediainfodll']), |
1306 | + APTest('dialer-app-autopilot', pkgs=['dialer-app-autopilot']), |
1307 | + APTest('ubuntu-keyboard-autopilot', pkgs=['ubuntu-keyboard-autopilot']), |
1308 | + APTest('messaging-app-autopilot', pkgs=['messaging-app-autopilot']), |
1309 | + APTest('address-book-app-autopilot', pkgs=['address-book-app-autopilot']), |
1310 | + APTest('reminders-autopilot', |
1311 | + pkgs=['python3-dbusmock', 'python3-requests-oauthlib', |
1312 | + 'account-plugin-evernote-sandbox']), |
1313 | + APTest('music-app-autopilot'), |
1314 | + APTest('dropping-letters-app-autopilot'), |
1315 | + APTest('sudoku-app-autopilot'), |
1316 | + APTest('ubuntu-calculator-app-autopilot'), |
1317 | + APTest('ubuntu-clock-app-autopilot'), |
1318 | + APTest('filemanager', pkgs=['python3-lxml']), |
1319 | + APTest('shorts-app-autopilot'), |
1320 | + APTest('ubuntu-terminal-app-autopilot'), |
1321 | + APTest('ubuntu-weather-app-autopilot'), |
1322 | + APTest('ubuntu-ui-toolkit-autopilot', 'ubuntuuitoolkit', |
1323 | + ['ubuntu-ui-toolkit-autopilot']), |
1324 | + APTest('ubuntu-system-settings-autopilot', |
1325 | + pkgs=['ubuntu-system-settings-autopilot']), |
1326 | + APTest('ubuntu-system-settings-online-accounts-autopilot', |
1327 | + 'online_accounts_ui', |
1328 | + ['ubuntu-system-settings-online-accounts-autopilot']), |
1329 | +] |
1330 | +TESTSUITES += [ |
1331 | + Test('click_image_tests'), |
1332 | + Test('sdk'), |
1333 | + Test('security'), |
1334 | + Test('eventstat', fmt=IDLE_FMT, smoke=False), |
1335 | + Test('smem', fmt=IDLE_FMT, smoke=False), |
1336 | + Test('health-check', timeout=300, smoke=False), |
1337 | + Test('memevent', |
1338 | + fmt='{prefix}{testname}-{series}-{imagetype}-armhf-default-{type}', |
1339 | + smoke=False), |
1340 | + Test('bootspeed', |
1341 | + fmt='{prefix}{testname}-{series}-{imagetype}-{type}-{type}', |
1342 | + smoke=False), |
1343 | +] |
1344 | + |
1345 | + |
1346 | +def filter_tests(tests, image_type, device_type=None): |
1347 | + if image_type: |
1348 | + func = globals().get('get_tests_%s' % image_type) |
1349 | + if func: |
1350 | + tests = func(tests, device_type) |
1351 | + elif image_type not in ['touch_stable', 'touch', |
1352 | + 'touch_custom_demo']: |
1353 | + print('Unsupported image type: %s' % image_type) |
1354 | + exit(1) |
1355 | + if device_type: |
1356 | + func = globals().get('get_tests_%s' % device_type) |
1357 | + if func: |
1358 | + tests = func(tests) |
1359 | + return tests |
1360 | + |
1361 | + |
1362 | +def _get_tests(test_type, image_type, device_type=None): |
1363 | + # take all our known tests, then call filter tests which should give |
1364 | + # us a list of tests customized for things like touch_custom. |
1365 | + # then eliminate tests that aren't of the proper test_type |
1366 | + tests = [t for t in filter_tests(TESTSUITES, image_type, device_type) |
1367 | + if type(t) == test_type and t.fmt == DEF_FMT] |
1368 | + return tests |
1369 | + |
1370 | + |
1371 | +def _split_work(tests, total_workers, worker_idx): |
1372 | + assigned = [] |
1373 | + for x in range(len(tests)): |
1374 | + if x % total_workers == worker_idx: |
1375 | + assigned.append(tests[x]) |
1376 | + return assigned |
1377 | + |
1378 | + |
1379 | +def _handle_utah(args): |
1380 | + tests = _get_tests(Test, args.image_type, args.device_type) |
1381 | + if args.with_autopilot: |
1382 | + tests = [t for t in TESTSUITES if t.fmt == DEF_FMT] |
1383 | + tests = _split_work(tests, args.total_workers, args.worker) |
1384 | + print(' '.join([t.name for t in tests if t.smoke is True])) |
1385 | + |
1386 | + |
1387 | +def _handle_ap_apps(args): |
1388 | + tests = _get_tests(APTest, args.image_type, args.device_type) |
1389 | + tests = _split_work(tests, args.total_workers, args.worker) |
1390 | + print(' '.join([t.app for t in tests])) |
1391 | + |
1392 | + |
1393 | +def _handle_ap_packages(args): |
1394 | + pkgs = [] |
1395 | + tests = _get_tests(APTest, args.image_type) |
1396 | + for test in tests: |
1397 | + if not args.app or test.app in args.app: |
1398 | + if test.pkgs: |
1399 | + pkgs.extend(test.pkgs) |
1400 | + print(' '.join(pkgs)) |
1401 | + |
1402 | + |
1403 | +def get_tests_mako(common_tests): |
1404 | + tests = common_tests |
1405 | + tests.extend([DevTest('suspend-blocker', 'mako-01')]) |
1406 | + return tests |
1407 | + |
1408 | + |
1409 | +def get_tests_touch_custom(common_tests, device_type): |
1410 | + tests = common_tests |
1411 | + tests.insert(1, Test('customizations')) |
1412 | + return tests |
1413 | + |
1414 | + |
1415 | +def get_tests_touch(common_tests, device_type): |
1416 | + if device_type in ('krillin', 'arale'): |
1417 | + filter_tests = ['filemanager', 'ubuntu-terminal-app-autopilot', |
1418 | + 'dropping-letters-app-autopilot', |
1419 | + 'shorts-app-autopilot', 'sudoku-app-autopilot', |
1420 | + 'click_image_tests'] |
1421 | + tests = [t for t in common_tests if t.name not in filter_tests] |
1422 | + else: |
1423 | + tests = common_tests |
1424 | + return tests |
1425 | + |
1426 | + |
1427 | +def get_tests_touch_stable(common_tests, device_type): |
1428 | + if device_type in ('krillin', 'arale'): |
1429 | + filter_tests = ['filemanager', 'ubuntu-terminal-app-autopilot', |
1430 | + 'dropping-letters-app-autopilot', |
1431 | + 'shorts-app-autopilot', 'sudoku-app-autopilot', |
1432 | + 'click_image_tests'] |
1433 | + tests = [t for t in common_tests if t.name not in filter_tests] |
1434 | + else: |
1435 | + tests = common_tests |
1436 | + return tests |
1437 | + |
1438 | + |
1439 | +def _get_parser(): |
1440 | + parser = argparse.ArgumentParser( |
1441 | + description='List information on configured tests for touch') |
1442 | + sub = parser.add_subparsers(title='Commands', metavar='') |
1443 | + |
1444 | + p = sub.add_parser('utah', help='List UTAH tests') |
1445 | + p.set_defaults(func=_handle_utah) |
1446 | + p.add_argument('-d', '--device-type', |
1447 | + help='Specify the device type where the tests will run') |
1448 | + p.add_argument('-i', '--image-type', |
1449 | + help='Return list of test configured for an image type.') |
1450 | + p.add_argument('-a', '--with-autopilot', action='store_true', |
1451 | + help='Include autopilot tests that can be run under UTAH.') |
1452 | + p.add_argument('-t', '--total-workers', type=int, default=1, |
1453 | + help='''The total number of workers available for running |
1454 | + tests.''') |
1455 | + p.add_argument('-w', '--worker', type=int, default=0, |
1456 | + help='The worker to allocate applications for testing to.') |
1457 | + |
1458 | + p = sub.add_parser('apps', help='List autopilot application names') |
1459 | + p.set_defaults(func=_handle_ap_apps) |
1460 | + p.add_argument('-d', '--device-type', |
1461 | + help='Specify the device type where the tests will run') |
1462 | + p.add_argument('-i', '--image-type', |
1463 | + help='Return list of test configured for an image type.') |
1464 | + p.add_argument('-t', '--total-workers', type=int, default=1, |
1465 | + help='''The total number of workers available for running |
1466 | + tests.''') |
1467 | + p.add_argument('-w', '--worker', type=int, default=0, |
1468 | + help='The worker to allocate applications for testing to.') |
1469 | + |
1470 | + p = sub.add_parser('packages', help='List packages required for autopilot') |
1471 | + p.set_defaults(func=_handle_ap_packages) |
1472 | + g = p.add_mutually_exclusive_group() |
1473 | + g.add_argument('-i', '--image-type', |
1474 | + help='''If no apps are listed, limit to tests for an |
1475 | + image type.''') |
1476 | + g.add_argument('-a', '--app', action='append', |
1477 | + help='Autopilot test application. eg share_app') |
1478 | + return parser |
1479 | + |
1480 | + |
1481 | +def main(): |
1482 | + args = _get_parser().parse_args() |
1483 | + return args.func(args) |
1484 | + |
1485 | +if __name__ == '__main__': |
1486 | + exit(main()) |
1487 | |
1488 | === added directory 'scripts' |
1489 | === renamed directory 'scripts' => 'scripts.moved' |
1490 | === added file 'scripts/assert-image' |
1491 | --- scripts/assert-image 1970-01-01 00:00:00 +0000 |
1492 | +++ scripts/assert-image 2015-06-03 20:37:20 +0000 |
1493 | @@ -0,0 +1,31 @@ |
1494 | +#!/bin/sh |
1495 | + |
1496 | +set -e |
1497 | + |
1498 | +BASEDIR=$(readlink -f $(dirname $0)/..) |
1499 | + |
1500 | +# a simple script to ensure the proper image is installed on the target |
1501 | + |
1502 | +[ -z $INSTALL_URL ] && echo "= INSTALL_URL not set. Using target as-is." && exit 0 |
1503 | + |
1504 | +echo "Ensuring target has proper image..." |
1505 | +REQUIRED_UUID=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-uuid) |
1506 | +ACTUAL_UUID=$(adb shell "cat /home/phablet/.ci-uuid | tr -d '\r\n'") |
1507 | +if [ "$REQUIRED_UUID" != "$ACTUAL_UUID" ] ; then |
1508 | + echo "= UUIDs $REQUIRED_UUID != $ACTUAL_UUID" |
1509 | + SERVER=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-flash-server | tr -d '\r\n') |
1510 | + ARGS=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-flash-args | tr -d '\r\n') |
1511 | + CUST=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-customizations | tr -d '\r\n') |
1512 | + echo "reprovisioning device with: $ARGS" |
1513 | + echo "customizing device with: $CUST" |
1514 | + #Make the image writable if we reprovision by adding a space |
1515 | + #after to CUSTOMIZE so it is never empty |
1516 | + UUID=$REQUIRED_UUID IMAGE_SERVER=$SERVER IMAGE_OPT=$ARGS \ |
1517 | + CUSTOMIZE="$CUST " ${BASEDIR}/scripts/provision.sh |
1518 | +else |
1519 | + echo "= UUIDS match, reusing image on target" |
1520 | + echo $REQUIRED_UUID > clientlogs/.ci-uuid |
1521 | + curl ${INSTALL_URL}/artifact/clientlogs/.ci-flash-args > clientlogs/.ci-flash-args |
1522 | + curl ${INSTALL_URL}/artifact/clientlogs/.ci-customizations > clientlogs/.ci-customizations |
1523 | +fi |
1524 | + |
1525 | |
1526 | === added file 'scripts/boottest.sh' |
1527 | --- scripts/boottest.sh 1970-01-01 00:00:00 +0000 |
1528 | +++ scripts/boottest.sh 2015-06-03 20:37:20 +0000 |
1529 | @@ -0,0 +1,204 @@ |
1530 | +#!/bin/bash |
1531 | +set -ex |
1532 | + |
1533 | +# Where am I ? |
1534 | +BASEDIR=$(dirname $(readlink -f $0))/.. |
1535 | + |
1536 | +# Provide a message to report special information |
1537 | +END_MESSAGE="" |
1538 | + |
1539 | +# The release to test the package on. |
1540 | +export RELEASE=${1:-vivid} |
1541 | +# The source package under test. |
1542 | +export SRC_PKG_NAME=${2:-libpng} |
1543 | +# The phone name. |
1544 | +export NODE_NAME=$3 |
1545 | + |
1546 | +# Default values can be provided via a local rc file (see |
1547 | +# ../config/boottest.rc.example). |
1548 | +BOOTTESTRC=${HOME}/.ubuntu-ci/boottest.rc |
1549 | +if [ -f $BOOTTESTRC ]; then |
1550 | + source $BOOTTESTRC |
1551 | +fi |
1552 | + |
1553 | +# Default adt-run timeout |
1554 | +export ADT_TIMEOUT=${ADT_TIMEOUT:-600} |
1555 | + |
1556 | +# rsync can be disabled for local testing by defining: |
1557 | +# export RSYNC="echo rsync" |
1558 | +export RSYNC=${RSYNC:-rsync} |
1559 | +# XXX psivaa 20150130: This is to use /var/local/boottest |
1560 | +# directory in tachash for rsyncing the results back. |
1561 | +# This should be revisited and fixed when the actual directory |
1562 | +# is decided for final |
1563 | +# May need tweaking/ removing the boottest section of /etc/rsyncd.conf |
1564 | +# in tachash |
1565 | +export RSYNC_DEST=${RSYNC_DEST:-rsync://tachash.ubuntu-ci/boottest/} |
1566 | + |
1567 | +# Look for a known bug, lp1421009, that results in unity8 not starting |
1568 | +# and prevents adt-run from accepting the testbed |
1569 | +check_for_lp1421009() { |
1570 | + SYMPTOM="ERROR: timed out waiting for Unity greeter" |
1571 | + LINK="http://launchpad.net/bugs/1421009" |
1572 | + if [ $1 -eq 16 ] && grep -q "${SYMPTOM}" ${2}/log; then |
1573 | + END_MESSAGE="Test failed due to ${LINK}" |
1574 | + fi |
1575 | +} |
1576 | + |
1577 | +# Create an exit handler so that we are sure to create a error file even |
1578 | +# when the unexpected occurs. |
1579 | +exit_handler() { |
1580 | + # The errfile and resultfile variables can be used to determine if the |
1581 | + # exit was normal. If either exists, it's a normal exit, so don't overwrite |
1582 | + # the original errfile or resultfile. |
1583 | + if [ -z ${errfile} ] && [ -z ${resultfile} ]; then |
1584 | + errfile=${RELEASE}_${ARCH}_${SRC_PKG_NAME}_$(date +%Y%m%d-%H%M%S).error |
1585 | + echo "$RELEASE $ARCH $SRC_PKG_NAME" > $errfile |
1586 | + [ -f "$errfile" ] && ${RSYNC} -a $errfile $RSYNC_DEST/${RELEASE}/tmp/ || true |
1587 | + fi |
1588 | + |
1589 | + # Ensure we leave a usable phone |
1590 | + [ -z ${NODE_NAME} ] || ${BASEDIR}/scripts/recover.py ${NODE_NAME} |
1591 | + |
1592 | + # Leave a parting message |
1593 | + # (disable command tracing as it confuses the output) |
1594 | + set +x |
1595 | + [ -z "${END_MESSAGE}" ] || echo -e "\n\n${END_MESSAGE}\n\n" |
1596 | + set -x |
1597 | +} |
1598 | +trap exit_handler SIGINT SIGTERM EXIT |
1599 | + |
1600 | +# If the NODE_NAME is unset, we're running locally, the commands that |
1601 | +# requires a phone are prefixed with "[ -z ${NODE_NAME} ] ||" |
1602 | +# If you have a phone available locally, set ANDROID_SERIAL and NODE_NAME=yes |
1603 | + |
1604 | +# These can be set by jenkins when running in that context |
1605 | + |
1606 | +# ANDROID_SERIAL: The phone ID. |
1607 | +[ -z ${NODE_NAME} ] || export ANDROID_SERIAL=${ANDROID_SERIAL:-$(${BASEDIR}/scripts/get-adb-id ${NODE_NAME})} |
1608 | + |
1609 | +# FIXME: Should be provided -- vila 2015-01-26 |
1610 | +ARCH="${ARCH:-krillin}" |
1611 | + |
1612 | +PHABLET_PASSWORD="${PHABLET_PASSWORD-0000}" |
1613 | + |
1614 | +# The provision.sh and run-smoke scripts can install extra packages to meet |
1615 | +# the needs of image testing. Since we are installing specific packages from |
1616 | +# the archive, this needs to be disabled. |
1617 | +export SKIP_CLICK=1 |
1618 | +export SKIP_TESTCONFIG=1 |
1619 | + |
1620 | +# Ensures we start with a usable phone |
1621 | +[ -z ${NODE_NAME} ] || ${BASEDIR}/scripts/recover.py ${NODE_NAME} |
1622 | + |
1623 | +TESTS=${BASEDIR}/tests |
1624 | + |
1625 | +# Provision the device |
1626 | +# Allow the image revision to be overridden if the latest is unusable |
1627 | +REVISION="${REVISION:-0}" |
1628 | +PROV_CMD="${BASEDIR}/scripts/provision.sh \ |
1629 | + -r $REVISION \ |
1630 | + -n ${HOME}/.ubuntu-ci/wifi.conf -w" |
1631 | +RETRY=3 |
1632 | +if [ -n "${NODE_NAME}" ]; then |
1633 | + while [ ${RETRY} -gt 0 ]; do |
1634 | + echo "Provisioning device" |
1635 | + ${PROV_CMD} && break |
1636 | + PROV_ERR=$? |
1637 | + # Make sure the device doesn't need to be recovered first |
1638 | + ${BASEDIR}/scripts/recover.py ${NODE_NAME} |
1639 | + if [ ${PROV_ERR} -eq 124 ]; then |
1640 | + # The provisioning fails with a timeout, the image is not |
1641 | + # flashable/bootable |
1642 | + echo ERROR: Device provisioning failed! |
1643 | + END_MESSAGE="Test failed because the image couldn't be flashed/booted" |
1644 | + exit 1 |
1645 | + fi |
1646 | + RETRY=$((${RETRY}-1)) |
1647 | + echo "Provisioning failed, retrying up to ${RETRY} more times..." |
1648 | + done |
1649 | + if [ ${RETRY} -eq 0 ]; then |
1650 | + echo ERROR: Device provisioning failed! |
1651 | + exit 1 |
1652 | + fi |
1653 | +fi |
1654 | + |
1655 | +# Generate the adt-run setup-command |
1656 | +rm -f adt-commands || true |
1657 | +# apt-get update like adt-run does it |
1658 | +echo "(apt-get update || (sleep 15; apt-get update))" >> adt-commands |
1659 | +# We need dctrl-tools installed so we can use grep-aptavail below |
1660 | +echo "apt-get install -f dctrl-tools" >> adt-commands |
1661 | +echo 'dpkg-query -f "\${binary:Package}\n" -W | sed -e "s/:.*$//" > installed.packages' >> adt-commands |
1662 | +echo "grep-aptavail -X -n -S -sPackage ${SRC_PKG_NAME}| sort | uniq > binary.packages" >> adt-commands |
1663 | +echo "comm -1 -2 binary.packages installed.packages > needs_install.packages" >> adt-commands |
1664 | +echo 'release=$(lsb_release -s -c)' >> adt-commands |
1665 | +echo 'cat needs_install.packages | xargs apt-get install -f -t ${release}-proposed 2> apt-get-install.stderr' >> adt-commands |
1666 | +# The sourcepkg-version file contains the version of the first binary in the |
1667 | +# list of binaries to install. This version data is passed back to britney |
1668 | +# via the final .result file. Britney uses this to determine if the package |
1669 | +# that was tested matches the version requested. |
1670 | +# An assumption is made that this binary package version matches the other |
1671 | +# packages installed for this test. This works because the 'apt-get install' |
1672 | +# command is all or nothing. So all packages have either been updated to the |
1673 | +# new version or are all stuck at the original version. |
1674 | +echo 'head -n 1 needs_install.packages | xargs dpkg-query --show --showformat=\${Version} > /home/phablet/sourcepkg-version' >> adt-commands |
1675 | + |
1676 | + |
1677 | +# --no-built-binaries should come first |
1678 | +# --debug helps while debugging, can be removed otherwise |
1679 | +# 'timeout' returns 124 if ${ADT_TIMEOUT} is reached. |
1680 | +ADT_CMD=${ADT_CMD:-timeout ${ADT_TIMEOUT} adt-run --debug --no-built-binaries} |
1681 | +# ADT_VIRT can be overridden for local tests, |
1682 | +# it defaults to ${ANDROID_SERIAL} phone via the adb/ssh combo |
1683 | +ADT_VIRT=${ADT_VIRT:-adt-virt-ssh -s /usr/share/autopkgtest/ssh-setup/adb \ |
1684 | + -- -s "${ANDROID_SERIAL}"} |
1685 | +# - setting up -proposed and doing apt-get update |
1686 | +# - via adt-virt-ssh with a setup from adb |
1687 | +# - using --apt-upgrade to ensure we only deal with packages already on the |
1688 | +# phone (see above) |
1689 | +ADT_OPTS="--apt-pocket=proposed\ |
1690 | + --setup-commands=adt-commands \ |
1691 | + --- ${ADT_VIRT}" |
1692 | + |
1693 | + |
1694 | +if [ -n "${FORCE_FAILURE}" ]; then |
1695 | + # Force a boottest failure by running an alternate DEP8 test |
1696 | + set +e |
1697 | + ${ADT_CMD} --unbuilt-tree ${TESTS}/bootfail -o results ${ADT_OPTS} |
1698 | + RET=$? |
1699 | + set -e |
1700 | +else |
1701 | + # Now execute the boot test |
1702 | + set +e |
1703 | + ${BASEDIR}/scripts/run-adt.py ${ADT_CMD} --unbuilt-tree ${TESTS}/boottest -o results ${ADT_OPTS} |
1704 | + RET=$? |
1705 | + # Fetch the sourcepkg-version file that contains the version data |
1706 | + # for the package under test. We can't use the testpkg-version file |
1707 | + # that adt-run generates because it provides the version of the |
1708 | + # fake boottest package, not the package we're actually testing. |
1709 | + adb pull /home/phablet/sourcepkg-version results/sourcepkg-version |
1710 | + set -e |
1711 | +fi |
1712 | + |
1713 | +check_for_lp1421009 $RET results |
1714 | + |
1715 | +# Return Skipped as Passed |
1716 | +[ $RET -eq 2 ] && RET=0 |
1717 | + |
1718 | +if [ -e "results/sourcepkg-version" -a -e "results/testbed-packages" ]; then |
1719 | + result='PASS' |
1720 | + resultfile=results/${RELEASE}_${ARCH}_${SRC_PKG_NAME}_$(date +%Y%m%d-%H%M%S).result |
1721 | + [ $RET -gt 0 ] && result="FAIL" |
1722 | + set +x # quiet mode as it pollutes output |
1723 | + echo "$RELEASE $ARCH ${SRC_PKG_NAME} $(cat results/sourcepkg-version) $result $(sort -u results/*-packages|tr -s '[\n\t]' ' ')" > $resultfile |
1724 | + set -x |
1725 | + [ -f "$resultfile" ] && ${RSYNC} -a $resultfile $RSYNC_DEST/${RELEASE}/tmp/ || true |
1726 | +else |
1727 | + # Something went wrong with the testbed |
1728 | + errfile=results/${RELEASE}_${ARCH}_${SRC_PKG_NAME}_$(date +%Y%m%d-%H%M%S).error |
1729 | + echo "$RELEASE $ARCH $SRC_PKG_NAME" > $errfile |
1730 | + [ -f "$errfile" ] && ${RSYNC} -a $errfile $RSYNC_DEST/${RELEASE}/tmp/ || true |
1731 | +fi |
1732 | + |
1733 | +exit $RET |
1734 | |
1735 | === added file 'scripts/combine_results' |
1736 | --- scripts/combine_results 1970-01-01 00:00:00 +0000 |
1737 | +++ scripts/combine_results 2015-06-03 20:37:20 +0000 |
1738 | @@ -0,0 +1,124 @@ |
1739 | +#!/usr/bin/env python3 |
1740 | + |
1741 | +""" |
1742 | +We always run the system-settle test before and after an autopilot test. This |
1743 | +script takes the results of the before/after results and combines them in with |
1744 | +the junit xml results from the autopilot test so we have one unified report. |
1745 | +""" |
1746 | + |
1747 | + |
1748 | +import os |
1749 | +import sys |
1750 | + |
1751 | +from xml.etree import ElementTree |
1752 | + |
1753 | + |
1754 | +PRE_COMBINE = [ |
1755 | + ('settle_before', 'settle_before'), |
1756 | + ('setup_setup', 'setup'), |
1757 | +] |
1758 | + |
1759 | +POST_COMBINE = [ |
1760 | + ('settle_after', 'settle_after'), |
1761 | + ('setup_teardown', 'teardown'), |
1762 | +] |
1763 | + |
1764 | + |
1765 | +def _build_node(classname, name, rcfile, stdout): |
1766 | + e = ElementTree.Element('testcase') |
1767 | + e.attrib['classname'] = classname |
1768 | + e.attrib['name'] = name |
1769 | + |
1770 | + if not os.path.exists(rcfile): |
1771 | + return None, False |
1772 | + |
1773 | + rc = int(open(rcfile).read()) |
1774 | + if rc != 0: |
1775 | + f = ElementTree.Element('failure') |
1776 | + e.append(f) |
1777 | + f.attrib['type'] = 'testtools.testresult.real._StringException' |
1778 | + f.text = open(stdout).read() |
1779 | + return e, rc != 0 |
1780 | + |
1781 | + |
1782 | +def _get_results(apfile): |
1783 | + try: |
1784 | + tree = ElementTree.parse(apfile) |
1785 | + except Exception as ex: |
1786 | + e = ElementTree.Element('testsuite') |
1787 | + tree = ElementTree.ElementTree(e) |
1788 | + e.attrib['errors'] = '1' |
1789 | + e.attrib['failures'] = '0' |
1790 | + e.attrib['tests'] = '1' |
1791 | + |
1792 | + # make a guess at the classname: |
1793 | + classname = os.path.basename(os.path.dirname(apfile)) |
1794 | + |
1795 | + t = ElementTree.Element('testcase') |
1796 | + e.append(t) |
1797 | + t.attrib['classname'] = classname |
1798 | + t.attrib['name'] = 'phablet-test-run' |
1799 | + |
1800 | + f = ElementTree.Element('failure') |
1801 | + t.append(f) |
1802 | + f.attrib['type'] = 'testtools.testresult.real._StringException' |
1803 | + f.text = str(ex) |
1804 | + |
1805 | + return tree |
1806 | + |
1807 | + |
1808 | +def _get_classname(results): |
1809 | + if len(results) < 1: |
1810 | + return '???' |
1811 | + |
1812 | + cname = results[0].attrib.get('classname') |
1813 | + if cname: |
1814 | + cname = cname.split('.')[0] |
1815 | + else: |
1816 | + cname = '???' |
1817 | + return cname |
1818 | + |
1819 | + |
1820 | +def combine(resdir): |
1821 | + ap_file = os.path.join(resdir, 'test_results.xml') |
1822 | + tree = _get_results(ap_file) |
1823 | + ap_results = tree.getroot() |
1824 | + added_results = 0 |
1825 | + |
1826 | + errors = int(ap_results.attrib['errors']) |
1827 | + |
1828 | + classname = _get_classname(ap_results) |
1829 | + |
1830 | + for basename, label in PRE_COMBINE: |
1831 | + rc = os.path.join(resdir, basename + '.rc') |
1832 | + log = os.path.join(resdir, basename + '.log') |
1833 | + node, failed = _build_node(classname, label, rc, log) |
1834 | + if node is not None: |
1835 | + ap_results.insert(0, node) |
1836 | + if failed: |
1837 | + errors += 1 |
1838 | + added_results += 1 |
1839 | + |
1840 | + for basename, label in POST_COMBINE: |
1841 | + rc = os.path.join(resdir, basename + '.rc') |
1842 | + log = os.path.join(resdir, basename + '.log') |
1843 | + node, failed = _build_node(classname, label, rc, log) |
1844 | + if node is not None: |
1845 | + ap_results.append(node) |
1846 | + if failed: |
1847 | + errors += 1 |
1848 | + added_results += 1 |
1849 | + |
1850 | + num = int(ap_results.attrib['tests']) + added_results |
1851 | + ap_results.attrib['tests'] = str(num) |
1852 | + ap_results.attrib['errors'] = str(errors) |
1853 | + |
1854 | + tree.write(ap_file) |
1855 | + |
1856 | + |
1857 | +if __name__ == '__main__': |
1858 | + if len(sys.argv) != 2: |
1859 | + print('usage: {} <results directory>'.format(sys.argv[0])) |
1860 | + sys.exit(1) |
1861 | + |
1862 | + combine(sys.argv[1]) |
1863 | |
1864 | === added file 'scripts/dashboard.py' |
1865 | --- scripts/dashboard.py 1970-01-01 00:00:00 +0000 |
1866 | +++ scripts/dashboard.py 2015-06-03 20:37:20 +0000 |
1867 | @@ -0,0 +1,305 @@ |
1868 | +#!/usr/bin/python |
1869 | + |
1870 | +import argparse |
1871 | +import datetime |
1872 | +import json |
1873 | +import logging |
1874 | +import os |
1875 | + |
1876 | +import yaml |
1877 | + |
1878 | +from httplib import ACCEPTED, HTTPConnection, HTTPException, OK, CREATED |
1879 | +from urllib import urlencode |
1880 | +from urlparse import urlparse |
1881 | + |
1882 | +log = logging.getLogger() |
1883 | + |
1884 | + |
1885 | +class API(object): |
1886 | + def __init__(self, host=None, port=None, user=None, key=None, prefix=None): |
1887 | + if not host: |
1888 | + host = os.environ.get('DASHBOARD_HOST', None) |
1889 | + if not port: |
1890 | + port = int(os.environ.get('DASHBOARD_PORT', '80')) |
1891 | + if not user: |
1892 | + user = os.environ.get('DASHBOARD_USER', None) |
1893 | + if not key: |
1894 | + key = os.environ.get('DASHBOARD_KEY', None) |
1895 | + if not prefix: |
1896 | + prefix = os.environ.get('DASHBOARD_PREFIX', None) |
1897 | + |
1898 | + self.host = host |
1899 | + self.port = port |
1900 | + self.resource_base = prefix |
1901 | + |
1902 | + self._headers = None |
1903 | + if user and key: |
1904 | + self._headers = { |
1905 | + 'Content-Type': 'application/json', |
1906 | + 'Authorization': 'ApiKey %s:%s' % (user, key) |
1907 | + } |
1908 | + # mod_wsgi will strip the Authorization header, but tastypie |
1909 | + # allows it as GET param also. More details for fixing apache: |
1910 | + # http://django-tastypie.rtfd.org/en/latest/authentication.html |
1911 | + self._auth_param = '?username=%s&api_key=%s' % (user, key) |
1912 | + |
1913 | + def _connect(self): |
1914 | + if self.host: |
1915 | + return HTTPConnection(self.host, self.port) |
1916 | + return None |
1917 | + |
1918 | + def _http_get(self, resource): |
1919 | + con = self._connect() |
1920 | + if not con: |
1921 | + # we just mock this for the case where the caller wants to |
1922 | + # use our API transparently enabled/disabled |
1923 | + return {} |
1924 | + |
1925 | + if self.resource_base: |
1926 | + resource = self.resource_base + resource |
1927 | + |
1928 | + logging.debug('doing get on: %s', resource) |
1929 | + headers = {'Content-Type': 'application/json'} |
1930 | + con.request('GET', resource, headers=headers) |
1931 | + resp = con.getresponse() |
1932 | + if resp.status != OK: |
1933 | + msg = '' |
1934 | + try: |
1935 | + msg = resp.read().decode() |
1936 | + except: |
1937 | + pass |
1938 | + fmt = '%d error getting resource(%s): %s' |
1939 | + raise HTTPException(fmt % (resp.status, resource, msg)) |
1940 | + data = json.loads(resp.read().decode()) |
1941 | + if len(data['objects']) == 0: |
1942 | + raise HTTPException('resource not found: %s' % resource) |
1943 | + assert len(data['objects']) == 1 |
1944 | + return data['objects'][0]['resource_uri'] |
1945 | + |
1946 | + def _http_post(self, resource, params): |
1947 | + con = self._connect() |
1948 | + if not con or not self._headers: |
1949 | + return None |
1950 | + |
1951 | + if self.resource_base: |
1952 | + resource = self.resource_base + resource |
1953 | + resource += self._auth_param |
1954 | + |
1955 | + params = json.dumps(params) |
1956 | + log.debug('posting (%s): %s', resource, params) |
1957 | + con.request('POST', resource, params, self._headers) |
1958 | + resp = con.getresponse() |
1959 | + if resp.status != CREATED: |
1960 | + msg = '' |
1961 | + try: |
1962 | + msg = str(resp.getheaders()) |
1963 | + msg += resp.read().decode() |
1964 | + except: |
1965 | + pass |
1966 | + raise HTTPException( |
1967 | + '%d creating resource(%s): %s' % (resp.status, resource, msg)) |
1968 | + uri = resp.getheader('Location') |
1969 | + return urlparse(uri).path |
1970 | + |
1971 | + def _http_patch(self, resource, params): |
1972 | + con = self._connect() |
1973 | + if not con or not self._headers: |
1974 | + return None |
1975 | + |
1976 | + resource += self._auth_param |
1977 | + |
1978 | + con.request('PATCH', resource, json.dumps(params), self._headers) |
1979 | + resp = con.getresponse() |
1980 | + if resp.status != ACCEPTED: |
1981 | + msg = '' |
1982 | + try: |
1983 | + msg = resp.getheaders() |
1984 | + except: |
1985 | + pass |
1986 | + raise HTTPException( |
1987 | + '%d patching resource(%s): %s' % (resp.status, resource, msg)) |
1988 | + return resource |
1989 | + |
1990 | + @staticmethod |
1991 | + def _uri_to_pk(resource_uri): |
1992 | + if resource_uri: |
1993 | + return resource_uri.split('/')[-2] |
1994 | + return None # we are mocked |
1995 | + |
1996 | + def job_get(self, name): |
1997 | + resource = '/smokeng/api/v1/job/?' + urlencode({'name': name}) |
1998 | + return self._http_get(resource) |
1999 | + |
2000 | + def job_add(self, name): |
2001 | + resource = '/smokeng/api/v1/job/' |
2002 | + params = { |
2003 | + 'name': name, |
2004 | + 'url': 'http://jenkins.qa.ubuntu.com/job/' + name + '/' |
2005 | + } |
2006 | + return self._http_post(resource, params) |
2007 | + |
2008 | + def build_add(self, job_name, job_number): |
2009 | + try: |
2010 | + logging.debug('trying to find job: %s', job_name) |
2011 | + job = self.job_get(job_name) |
2012 | + except HTTPException: |
2013 | + job = self.job_add(job_name) |
2014 | + logging.info('job is: %s', job) |
2015 | + |
2016 | + resource = '/smokeng/api/v1/build/' |
2017 | + params = { |
2018 | + 'build_number': job_number, |
2019 | + 'job': job, |
2020 | + 'ran_at': datetime.datetime.now().isoformat(), |
2021 | + 'build_description': 'inprogress', |
2022 | + } |
2023 | + return self._http_post(resource, params) |
2024 | + |
2025 | + def _image_get(self, build_number, release, variant, arch, flavor): |
2026 | + resource = '/smokeng/api/v1/image/?' |
2027 | + resource += urlencode({ |
2028 | + 'build_number': build_number, |
2029 | + 'release': release, |
2030 | + 'flavor': flavor, |
2031 | + 'variant': variant, |
2032 | + 'arch': arch, |
2033 | + }) |
2034 | + return self._http_get(resource) |
2035 | + |
2036 | + def image_add(self, build_number, release, variant, arch, flavor): |
2037 | + try: |
2038 | + img = self._image_get(build_number, release, variant, arch, flavor) |
2039 | + return img |
2040 | + except HTTPException: |
2041 | + # image doesn't exist so go continue and create |
2042 | + pass |
2043 | + |
2044 | + resource = '/smokeng/api/v1/image/' |
2045 | + params = { |
2046 | + 'build_number': build_number, |
2047 | + 'release': release, |
2048 | + 'flavor': flavor, |
2049 | + 'variant': variant, |
2050 | + 'arch': arch, |
2051 | + } |
2052 | + try: |
2053 | + return self._http_post(resource, params) |
2054 | + except HTTPException: |
2055 | + # race situation. Both callers saw _image_get fail and tried to |
2056 | + # create. Only one of them can succeed, so the failed call should |
2057 | + # now safely be able to get the image created by the other |
2058 | + img = self._image_get(build_number, release, variant, arch, flavor) |
2059 | + return img |
2060 | + |
2061 | + def result_get(self, image, test): |
2062 | + # deal with getting resource uri's as parameters instead of id's |
2063 | + image = API._uri_to_pk(image) |
2064 | + |
2065 | + resource = '/smokeng/api/v1/result/?' |
2066 | + resource += urlencode({ |
2067 | + 'image': image, |
2068 | + 'name': test, |
2069 | + }) |
2070 | + return self._http_get(resource) |
2071 | + |
2072 | + def _result_status(self, image, build, test, status, results=None): |
2073 | + create = False |
2074 | + params = { |
2075 | + 'ran_at': datetime.datetime.now().isoformat(), |
2076 | + 'status': status, |
2077 | + 'jenkins_build': build, |
2078 | + } |
2079 | + if results: |
2080 | + params['results'] = results |
2081 | + |
2082 | + try: |
2083 | + resource = self.result_get(image, test) |
2084 | + except HTTPException: |
2085 | + create = True |
2086 | + resource = '/smokeng/api/v1/result/' |
2087 | + params['image'] = image |
2088 | + params['name'] = test |
2089 | + |
2090 | + if create: |
2091 | + return self._http_post(resource, params) |
2092 | + else: |
2093 | + return self._http_patch(resource, params) |
2094 | + |
2095 | + def result_queue(self, image, build, test): |
2096 | + return self._result_status(image, build, test, 0) |
2097 | + |
2098 | + def result_running(self, image, build, test): |
2099 | + return self._result_status(image, build, test, 1) |
2100 | + |
2101 | + def result_syncing(self, image, build, test, results): |
2102 | + return self._result_status(image, build, test, 2, results) |
2103 | + |
2104 | + |
2105 | +def _result_running(api, args): |
2106 | + return api.result_running(args.image, args.build, args.test) |
2107 | + |
2108 | + |
2109 | +def _result_syncing(api, args): |
2110 | + results = {} |
2111 | + with open(args.results) as f: |
2112 | + results = yaml.safe_load(f.read()) |
2113 | + return api.result_syncing(args.image, args.build, args.test, results) |
2114 | + |
2115 | + |
2116 | +def _set_args(parser, names, func): |
2117 | + for n in names: |
2118 | + parser.add_argument(n, required=True) |
2119 | + parser.set_defaults(func=func) |
2120 | + |
2121 | + |
2122 | +def _get_parser(): |
2123 | + parser = argparse.ArgumentParser( |
2124 | + description='Interact with the CI dashboard API') |
2125 | + |
2126 | + sub = parser.add_subparsers(title='Commands', metavar='') |
2127 | + |
2128 | + args = ['--image', '--build', '--test'] |
2129 | + p = sub.add_parser('result-running', help='Set a SmokeResult "Running".') |
2130 | + _set_args(p, args, _result_running) |
2131 | + |
2132 | + p = sub.add_parser('result-syncing', help='Set a SmokeResult "Syncing".') |
2133 | + _set_args(p, args, _result_syncing) |
2134 | + p.add_argument('--results', required=True, help='UTAH yaml file') |
2135 | + |
2136 | + return parser |
2137 | + |
2138 | + |
2139 | +def _assert_env(): |
2140 | + required = [ |
2141 | + 'DASHBOARD_HOST', 'DASHBOARD_PORT', 'DASHBOARD_USER', 'DASHBOARD_KEY'] |
2142 | + missing = [] |
2143 | + for r in required: |
2144 | + if r not in os.environ: |
2145 | + missing.append(r) |
2146 | + if len(missing): |
2147 | + print('Missing the following environment variables:') |
2148 | + for x in missing: |
2149 | + print(' %s' % x) |
2150 | + exit(1) |
2151 | + |
2152 | + |
2153 | +def _main(args): |
2154 | + _assert_env() |
2155 | + |
2156 | + api = API() |
2157 | + try: |
2158 | + val = args.func(api, args) |
2159 | + if val: |
2160 | + if '/?' in val: |
2161 | + log.debug('stripping api-key from response') |
2162 | + val = val.split('/?')[0] + '/' |
2163 | + print(val) |
2164 | + except HTTPException as e: |
2165 | + print('ERROR: %s' % e) |
2166 | + exit(1) |
2167 | + |
2168 | + exit(0) |
2169 | + |
2170 | +if __name__ == '__main__': |
2171 | + args = _get_parser().parse_args() |
2172 | + exit(_main(args)) |
2173 | |
2174 | === added file 'scripts/device_info.py' |
2175 | --- scripts/device_info.py 1970-01-01 00:00:00 +0000 |
2176 | +++ scripts/device_info.py 2015-06-03 20:37:20 +0000 |
2177 | @@ -0,0 +1,381 @@ |
2178 | +#!/usr/bin/env python |
2179 | + |
2180 | +import logging |
2181 | +import os |
2182 | +import re |
2183 | +import requests |
2184 | +import subprocess |
2185 | +import sys |
2186 | +import time |
2187 | +import urlparse |
2188 | + |
2189 | +from masher_control import set_button |
2190 | +from ncd_usb import set_relay |
2191 | + |
2192 | +log = logging.getLogger() |
2193 | +logging.basicConfig(level=logging.INFO) |
2194 | + |
2195 | + |
2196 | +class DeviceError(Exception): |
2197 | + pass |
2198 | + |
2199 | + |
2200 | +class TouchDevice(object): |
2201 | + def __init__(self, devtype, serial, relay_url=None, bank=None, |
2202 | + power_pin=None, volume_down_pin=None, volume_up_pin=None, |
2203 | + image_server='https://system-image.ubuntu.com', |
2204 | + image_channel='ubuntu-touch/stable', |
2205 | + extra_args=None): |
2206 | + self.devtype = devtype |
2207 | + self.serial = serial |
2208 | + self.relay_url = relay_url |
2209 | + self.bank = bank |
2210 | + self.power_pin = power_pin |
2211 | + self.volume_down_pin = volume_down_pin |
2212 | + self.volume_up_pin = volume_up_pin |
2213 | + self.image_server = image_server |
2214 | + self.image_channel = image_channel |
2215 | + self.extra_args = extra_args |
2216 | + |
2217 | + def get_serial(self): |
2218 | + return self.serial |
2219 | + |
2220 | + def get_state(self): |
2221 | + """ |
2222 | + Check adb and fastboot to determine the state a device is in. |
2223 | + Possible return values are: |
2224 | + device, recovery, unknown, bootloader, disconnected |
2225 | + """ |
2226 | + pattern = "{}\t(.+)\n".format(self.serial) |
2227 | + adb_devices = subprocess.check_output(['adb', 'devices']) |
2228 | + found = re.search(pattern, adb_devices) |
2229 | + if not found: |
2230 | + #Otherwise, check fastboot |
2231 | + fastboot_devices = subprocess.check_output(['fastboot', 'devices']) |
2232 | + found = re.search(pattern, fastboot_devices) |
2233 | + if found: |
2234 | + state = found.group(1) |
2235 | + return state |
2236 | + else: |
2237 | + return 'disconnected' |
2238 | + |
2239 | + def check_adb_shell(self): |
2240 | + # Run a quick command in adb to see if the device is responding |
2241 | + # subprocess will handle raising an exception if anything |
2242 | + # goes wrong |
2243 | + subprocess.check_call(['timeout', '10', 'adb', '-s', |
2244 | + self.serial, 'shell', 'pwd']) |
2245 | + |
2246 | + def reimage_from_fastboot(self): |
2247 | + #Starting from fastboot mode, put a known-good image on the device |
2248 | + recovery_base = 'http://people.canonical.com/~plars/touch' |
2249 | + recovery_img = 'recovery-{}.img'.format(self.devtype) |
2250 | + recovery_url = os.path.join(recovery_base, recovery_img) |
2251 | + # For certain devices, we need to first pull an alternate |
2252 | + # recovery image that has adb enabled. If it's needed, it will |
2253 | + # be at that url, otherwise we don't even try to use it. |
2254 | + try: |
2255 | + _download(recovery_url, 'recovery') |
2256 | + except: |
2257 | + # Don't fail for any reason, if attempt to flash without |
2258 | + pass |
2259 | + udf_command = ['ubuntu-device-flash', |
2260 | + '--server', self.image_server, |
2261 | + 'touch', '--serial', self.serial, |
2262 | + '--channel', self.image_channel, |
2263 | + '--bootstrap', '--developer-mode', |
2264 | + '--password', '0000'] |
2265 | + if self.extra_args: |
2266 | + udf_command.extend(self.extra_args) |
2267 | + if os.path.exists(os.path.join('recovery', recovery_img)): |
2268 | + udf_command.extend(['--recovery-image', |
2269 | + os.path.join('recovery', recovery_img)]) |
2270 | + log.info("Flashing the last stable image") |
2271 | + subprocess.check_output(udf_command) |
2272 | + return self.wait_for_device(600) |
2273 | + |
2274 | + def wait_for_fastboot(self, timeout=120): |
2275 | + if timeout > 10: |
2276 | + wait = 10 |
2277 | + else: |
2278 | + wait = timeout |
2279 | + waited = 0 |
2280 | + while waited < timeout: |
2281 | + state = self.get_state() |
2282 | + if state == 'fastboot': |
2283 | + return 0 |
2284 | + time.sleep(wait) |
2285 | + waited += wait |
2286 | + else: |
2287 | + state = self.get_state() |
2288 | + if state == 'fastboot': |
2289 | + return 0 |
2290 | + log.error("Timed out waiting for fastboot. Recover device " |
2291 | + "manually") |
2292 | + raise DeviceError("Device in state: {0}, still not available " |
2293 | + "after {1} seconds".format(state, timeout)) |
2294 | + |
2295 | + def reboot(self): |
2296 | + dev_state = self.get_state() |
2297 | + if dev_state not in ('device', 'recovery'): |
2298 | + raise DeviceError('Unable to reboot device from this state') |
2299 | + subprocess.check_call(['adb', '-s', self.serial, 'reboot']) |
2300 | + |
2301 | + def wait_for_device(self, timeout=120): |
2302 | + # Wait for the device to come up to a good/booted state |
2303 | + log.info("Waiting for the device to become available") |
2304 | + try: |
2305 | + subprocess.check_call(['timeout', str(timeout), 'adb', '-s', |
2306 | + self.serial, 'wait-for-device']) |
2307 | + except: |
2308 | + log.error("Timed out waiting for device.") |
2309 | + raise |
2310 | + dev_state = self.get_state() |
2311 | + if dev_state != 'device': |
2312 | + log.error("Device in state: {0}, still not available after " |
2313 | + "{1} seconds".format(dev_state, timeout)) |
2314 | + raise DeviceError("Timed out waiting for device to respond after " |
2315 | + "{1} seconds".format(dev_state, timeout)) |
2316 | + else: |
2317 | + log.info("Device is now available") |
2318 | + return 0 |
2319 | + |
2320 | + def force_bootloader(self): |
2321 | + bootloader_func = getattr( |
2322 | + self, '_{}_to_bootloader'.format(self.devtype)) |
2323 | + if not self.relay_url: |
2324 | + log.error("Device cannot be recovered, no relay data exists!") |
2325 | + sys.exit(-1) |
2326 | + if bootloader_func and callable(bootloader_func): |
2327 | + bootloader_func() |
2328 | + else: |
2329 | + raise DeviceError("Full recovery not possible with this device") |
2330 | + |
2331 | + def _arale_to_bootloader(self): |
2332 | + log.info("Forcing the device to enter the bootloader") |
2333 | + # XXX fginther - 2015-03-29 |
2334 | + # Refine the power off and fastboot sequence |
2335 | + set_button(self.relay_url, self.volume_down_pin, 1) |
2336 | + set_button(self.relay_url, self.power_pin, 1) |
2337 | + time.sleep(10) |
2338 | + set_button(self.relay_url, self.power_pin, 0) |
2339 | + time.sleep(10) |
2340 | + set_button(self.relay_url, self.volume_down_pin, 0) |
2341 | + |
2342 | + def _krillin_to_bootloader(self): |
2343 | + log.info("Forcing the device to enter the bootloader") |
2344 | + #Power off the device from any state |
2345 | + set_relay(self.relay_url, self.bank, self.power_pin, 1) |
2346 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 1) |
2347 | + set_relay(self.relay_url, self.bank, self.volume_up_pin, 1) |
2348 | + time.sleep(16) |
2349 | + set_relay(self.relay_url, self.bank, self.power_pin, 0) |
2350 | + time.sleep(6) |
2351 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 0) |
2352 | + set_relay(self.relay_url, self.bank, self.volume_up_pin, 0) |
2353 | + |
2354 | + def _flo_to_bootloader(self): |
2355 | + log.info("Forcing the device to enter the bootloader") |
2356 | + #Power off the device from any state |
2357 | + set_relay(self.relay_url, self.bank, self.power_pin, 1) |
2358 | + time.sleep(12) |
2359 | + set_relay(self.relay_url, self.bank, self.power_pin, 0) |
2360 | + time.sleep(10) |
2361 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 1) |
2362 | + set_relay(self.relay_url, self.bank, self.power_pin, 1) |
2363 | + time.sleep(5) |
2364 | + set_relay(self.relay_url, self.bank, self.power_pin, 0) |
2365 | + time.sleep(1) |
2366 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 0) |
2367 | + |
2368 | + def _mako_to_bootloader(self): |
2369 | + log.info("Forcing the device to enter the bootloader") |
2370 | + #Power off the device from any state |
2371 | + set_relay(self.relay_url, self.bank, self.power_pin, 1) |
2372 | + time.sleep(12) |
2373 | + set_relay(self.relay_url, self.bank, self.power_pin, 0) |
2374 | + time.sleep(10) |
2375 | + #Enter the bootloader |
2376 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 1) |
2377 | + set_relay(self.relay_url, self.bank, self.power_pin, 1) |
2378 | + time.sleep(5) |
2379 | + set_relay(self.relay_url, self.bank, self.volume_down_pin, 0) |
2380 | + set_relay(self.relay_url, self.bank, self.power_pin, 0) |
2381 | + |
2382 | + |
2383 | +# When looking at the relay webUI for the mapping, we consider all |
2384 | +# ports and banks to start numbering from 0 |
2385 | +DEVICES = { |
2386 | + "arale-01": TouchDevice("arale", "75UABKPUK9EW", |
2387 | + relay_url="http://10.74.120.150:8000", |
2388 | + power_pin=0, volume_down_pin=1, |
2389 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2390 | + extra_args=['--device', 'arale']), |
2391 | + "arale-02": TouchDevice("arale", "75UABKPN2CND", |
2392 | + relay_url="http://10.74.120.150:8000", |
2393 | + power_pin=2, volume_down_pin=3, |
2394 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2395 | + extra_args=['--device', 'arale']), |
2396 | + "arale-03": TouchDevice("arale", "75UABKP44J83", |
2397 | + relay_url="http://10.74.120.150:8000", |
2398 | + power_pin=4, volume_down_pin=5, |
2399 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2400 | + extra_args=['--device', 'arale']), |
2401 | + "arale-04": TouchDevice("arale", "75UABKPUFHL9", |
2402 | + relay_url="http://10.74.120.150:8000", |
2403 | + power_pin=6, volume_down_pin=7, |
2404 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2405 | + extra_args=['--device', 'arale']), |
2406 | + "arale-05": TouchDevice("arale", "75UABL662MQ3", |
2407 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2408 | + extra_args=['--device', 'arale']), |
2409 | + "arale-06": TouchDevice("arale", "75UABL6NEYKR", |
2410 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2411 | + extra_args=['--device', 'arale']), |
2412 | + "arale-07": TouchDevice("arale", "75UABL6K4KPD", |
2413 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2414 | + extra_args=['--device', 'arale']), |
2415 | + "arale-08": TouchDevice("arale", "75UABL65U7BB", |
2416 | + image_channel="ubuntu-touch/rc-proposed/meizu.en", |
2417 | + extra_args=['--device', 'arale']), |
2418 | + "krillin-01": TouchDevice("krillin", "JB011018"), |
2419 | + "krillin-02": TouchDevice("krillin", "JB010894"), |
2420 | + "krillin-03": TouchDevice("krillin", "JB015156", |
2421 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2422 | + bank=2, power_pin=0, volume_up_pin=1, |
2423 | + volume_down_pin=2), |
2424 | + "krillin-04": TouchDevice("krillin", "JB006885", |
2425 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2426 | + bank=1, power_pin=4, volume_up_pin=5, |
2427 | + volume_down_pin=6), |
2428 | + "krillin-05": TouchDevice("krillin", "JB015256"), |
2429 | + "krillin-06": TouchDevice("krillin", "JW010687"), |
2430 | + "krillin-07": TouchDevice("krillin", "JW011999", |
2431 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2432 | + bank=2, power_pin=4, volume_up_pin=5, |
2433 | + volume_down_pin=6), |
2434 | + "krillin-08": TouchDevice("krillin", "JW013513", |
2435 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2436 | + bank=1, power_pin=0, volume_up_pin=1, |
2437 | + volume_down_pin=2), |
2438 | + "krillin-09": TouchDevice("krillin", "JW010053", |
2439 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2440 | + bank=0, power_pin=4, volume_up_pin=5, |
2441 | + volume_down_pin=6), |
2442 | + "krillin-10": TouchDevice("krillin", "JB012976", |
2443 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2444 | + bank=2, power_pin=0, volume_up_pin=1, |
2445 | + volume_down_pin=2), |
2446 | + "krillin-11": TouchDevice("krillin", "JB072312"), |
2447 | + "krillin-12": TouchDevice("krillin", "JB071487"), |
2448 | + "krillin-13": TouchDevice("krillin", "JB044235"), |
2449 | + "krillin-14": TouchDevice("krillin", "JB072205"), |
2450 | + "krillin-15": TouchDevice("krillin", "JB071743"), |
2451 | + "mako-21": TouchDevice("mako", "0090f741e3d141bc", |
2452 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2453 | + bank=0, power_pin=0, volume_down_pin=1), |
2454 | + "mako-22": TouchDevice("mako", "04ccca120acd4dea", |
2455 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2456 | + bank=0, power_pin=2, volume_down_pin=3), |
2457 | + "mako-23": TouchDevice("mako", "04cb53b598546534", |
2458 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2459 | + bank=0, power_pin=4, volume_down_pin=5), |
2460 | + "mako-24": TouchDevice("mako", "04cbcc545f5328a5", |
2461 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2462 | + bank=0, power_pin=6, volume_down_pin=7), |
2463 | + "mako-01": TouchDevice("mako", "01aa3d7a5dcba4a2"), |
2464 | + "mako-02": TouchDevice("mako", "01ade38b552014d4"), |
2465 | + "mako-03": TouchDevice("mako", "04c6714ed7c863f2"), |
2466 | + "mako-04": TouchDevice("mako", "04df89cf0f9d0933", |
2467 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2468 | + bank=2, power_pin=2, volume_down_pin=3), |
2469 | + "mako-05": TouchDevice("mako", "01b22f82dc5cec63", |
2470 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2471 | + bank=0, power_pin=0, volume_down_pin=1), |
2472 | + "mako-06": TouchDevice("mako", "04ed70928fdc13ba", |
2473 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2474 | + bank=0, power_pin=2, volume_down_pin=3), |
2475 | + "mako-07": TouchDevice("mako", "01e2f64788556934", |
2476 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2477 | + bank=0, power_pin=4, volume_down_pin=5), |
2478 | + "mako-08": TouchDevice("mako", "04ea16a163930769", |
2479 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2480 | + bank=0, power_pin=6, volume_down_pin=7), |
2481 | + "mako-09": TouchDevice("mako", "04fda12ea08fe3c7", |
2482 | + relay_url="http://bos01-a-04-shelf01-relay.power", |
2483 | + bank=2, power_pin=0, volume_down_pin=1), |
2484 | + "mako-10": TouchDevice("mako", "01ce848e48dfa6a2"), |
2485 | + "mako-11": TouchDevice("mako", "04ed727c929709ba"), |
2486 | + #If looking at the LAB wiki page, subtract 1 from the bank and pin numbers |
2487 | + #from what it says on the wiki (our numbers start at 0) |
2488 | + "mako-12": TouchDevice("mako", "00693fd555c9186a", |
2489 | + relay_url="http://bos01-a-04-shelf04-relay.power", |
2490 | + bank=2, power_pin=0, volume_down_pin=1), |
2491 | + "mako-13": TouchDevice("mako", "0084e99c5315731b", |
2492 | + relay_url="http://bos01-a-04-shelf04-relay.power", |
2493 | + bank=0, power_pin=3, volume_down_pin=4), |
2494 | + "mako-14": TouchDevice("mako", "007c6d84d348838e", |
2495 | + relay_url="http://bos01-a-04-shelf04-relay.power", |
2496 | + bank=0, power_pin=1, volume_down_pin=2), |
2497 | + "mako-15": TouchDevice("mako", "00763b4a61ce0f87", |
2498 | + relay_url="http://bos01-a-04-shelf04-relay.power", |
2499 | + bank=0, power_pin=6, volume_down_pin=7), |
2500 | + "mako-16": TouchDevice("mako", "017121eacf5282c4", |
2501 | + relay_url="http://bos01-a-04-shelf04-relay.power", |
2502 | + bank=0, power_pin=4, volume_down_pin=5), |
2503 | + #mako-17 has a broken screen but should work, on ashes |
2504 | + "mako-17": TouchDevice("mako", "04e0d2f6d3cab77d"), |
2505 | + "mako-18": TouchDevice("mako", "027b981a4c1110dd", |
2506 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2507 | + bank=1, power_pin=0, volume_down_pin=1), |
2508 | + "mako-19": TouchDevice("mako", "021c8cdfd5d38602", |
2509 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2510 | + bank=1, power_pin=4, volume_down_pin=5), |
2511 | + "mako-20": TouchDevice("mako", "05083705e0d29402", |
2512 | + relay_url="http://bos01-a-04-shelf02-relay.power", |
2513 | + bank=1, power_pin=2, volume_down_pin=3), |
2514 | + "mako-fginther": TouchDevice("mako", "04c3c2be1d5248b3"), |
2515 | + "ps-manta-01": TouchDevice("manta", "R32D203DDZR"), |
2516 | + "manta-01": TouchDevice("manta", "R32D102RPZL"), |
2517 | + "manta-02": TouchDevice("manta", "R32D102RPPK"), |
2518 | + "manta-03": TouchDevice("manta", "R32D200N4YH"), |
2519 | + "manta-05": TouchDevice("manta", "R32D203DMBY"), # Out of lab for now |
2520 | + "flo-01": TouchDevice("flo", "09f306dc"), |
2521 | + "flo-02": TouchDevice("flo", "08dbee36"), |
2522 | + "flo-03": TouchDevice("flo", "09d55fa8"), |
2523 | + "flo-04": TouchDevice("flo", "09e68682"), |
2524 | + "flo-05": TouchDevice("flo", "0a22f7cf", |
2525 | + relay_url="http://bos01-a-04-shelf03-relay.power", |
2526 | + bank=0, power_pin=0, volume_down_pin=2), |
2527 | + "flo-06": TouchDevice("flo", "08f09bb0"), |
2528 | +} |
2529 | + |
2530 | + |
2531 | +def get_device(name): |
2532 | + # This raises KeyError if we don't have any record of that device |
2533 | + return DEVICES[name] |
2534 | + |
2535 | + |
2536 | +def _download(url, path): |
2537 | + try: |
2538 | + os.makedirs(path) |
2539 | + except OSError: |
2540 | + # Don't fail if the directory already exists |
2541 | + pass |
2542 | + urlpath = urlparse.urlsplit(url).path |
2543 | + filename = os.path.basename(urlpath) |
2544 | + filename = os.path.join(path, filename) |
2545 | + if os.path.exists(filename): |
2546 | + # Don't re-download if it's already there |
2547 | + return filename |
2548 | + |
2549 | + response = requests.get(url, stream=True) |
2550 | + response.raise_for_status() |
2551 | + with open(filename, 'wb') as f: |
2552 | + for chunk in response.iter_content(chunk_size=64 * 1024): |
2553 | + # Ignore keep-alives |
2554 | + if not chunk: |
2555 | + continue |
2556 | + f.write(chunk) |
2557 | + f.flush() |
2558 | + return filename |
2559 | |
2560 | === added file 'scripts/get-adb-id' |
2561 | --- scripts/get-adb-id 1970-01-01 00:00:00 +0000 |
2562 | +++ scripts/get-adb-id 2015-06-03 20:37:20 +0000 |
2563 | @@ -0,0 +1,14 @@ |
2564 | +#!/usr/bin/python |
2565 | + |
2566 | +import sys |
2567 | +from device_info import get_device |
2568 | + |
2569 | +if __name__ == '__main__': |
2570 | + name = sys.argv[1] |
2571 | + |
2572 | + try: |
2573 | + device = get_device(name) |
2574 | + print(device.get_serial()) |
2575 | + except: |
2576 | + print("Unknown device name: '%s'" % name) |
2577 | + sys.exit(-1) |
2578 | |
2579 | === added file 'scripts/get-device-info' |
2580 | --- scripts/get-device-info 1970-01-01 00:00:00 +0000 |
2581 | +++ scripts/get-device-info 2015-06-03 20:37:20 +0000 |
2582 | @@ -0,0 +1,38 @@ |
2583 | +#!/usr/bin/env python |
2584 | + |
2585 | +import argparse |
2586 | +import device_info |
2587 | +import sys |
2588 | + |
2589 | + |
2590 | +def _get_state(args): |
2591 | + if args.name == 'all': |
2592 | + for device in device_info.DEVICES: |
2593 | + sys.stdout.write("{}: ".format(device)) |
2594 | + print(device_info.get_device(device).get_state()) |
2595 | + else: |
2596 | + device = device_info.get_device(args.name) |
2597 | + print(device.get_state()) |
2598 | + |
2599 | + |
2600 | +def _get_serial(args): |
2601 | + device = device_info.get_device(args.name) |
2602 | + print(device.get_serial()) |
2603 | + |
2604 | + |
2605 | +def _get_parser(): |
2606 | + parser = argparse.ArgumentParser( |
2607 | + description='Get information about a device') |
2608 | + sub = parser.add_subparsers(title='Commands', metavar='') |
2609 | + serial = sub.add_parser('serial', help='Get serial for a device name') |
2610 | + serial.set_defaults(func=_get_serial) |
2611 | + serial.add_argument('name', help='Device name') |
2612 | + state = sub.add_parser('state', help='Get device state for a device') |
2613 | + state.set_defaults(func=_get_state) |
2614 | + state.add_argument('name', help='Device name') |
2615 | + return parser |
2616 | + |
2617 | + |
2618 | +if __name__ == '__main__': |
2619 | + args = _get_parser().parse_args() |
2620 | + exit(args.func(args)) |
2621 | |
2622 | === added file 'scripts/jenkins.sh' |
2623 | --- scripts/jenkins.sh 1970-01-01 00:00:00 +0000 |
2624 | +++ scripts/jenkins.sh 2015-06-03 20:37:20 +0000 |
2625 | @@ -0,0 +1,216 @@ |
2626 | +#!/bin/bash |
2627 | + |
2628 | +## This is the script jenkins should run to execute various touch applications |
2629 | + |
2630 | +set -e |
2631 | + |
2632 | +BASEDIR=$(dirname $(readlink -f $0))/.. |
2633 | + |
2634 | +RESDIR="${RESDIR-`pwd`/clientlogs}" |
2635 | +UTAHFILE=${RESDIR}/utah.yaml |
2636 | +UTAH_PHABLET_CMD="${UTAH_PHABLET_CMD-/usr/share/utah/examples/run_utah_phablet.py}" |
2637 | + |
2638 | + |
2639 | +usage() { |
2640 | + cat <<EOF |
2641 | +usage: $0 -a APP [-s ANDROID_SERIAL] [-p FILE -p FILE ...] [-Q] |
2642 | + |
2643 | +Provisions the given device with the latest build |
2644 | + |
2645 | +OPTIONS: |
2646 | + -h Show this message |
2647 | + -s Specify the serial of the device to install |
2648 | + -a The application under the "tests" directory to test |
2649 | + -p Extra file to pull from target (absolute path or relative to /home/phablet) |
2650 | + -Q "Quick" don't do a reboot of the device before running the test |
2651 | + |
2652 | +EOF |
2653 | +} |
2654 | + |
2655 | +PIDS="" |
2656 | + |
2657 | +cleanup() { |
2658 | + set +e |
2659 | + echo "killing child pids: $PIDS" |
2660 | + for p in $PIDS ; do |
2661 | + kill $p |
2662 | + done |
2663 | +} |
2664 | + |
2665 | +test_from_host() { |
2666 | + export PATH=${BASEDIR}/utils/host:${PATH} |
2667 | + |
2668 | + # allow for certain commands to run from host/target |
2669 | + # see unity8-autopilot/ts_control for example |
2670 | + export TARGET_PREFIX=adb-shell |
2671 | + |
2672 | + [ -z $ANDROID_SERIAL ] || ADBOPTS="-s $ANDROID_SERIAL" |
2673 | + |
2674 | + # If we are not in the utah group, then we don't have permissions |
2675 | + # for /var/lib/utah, so run under sudo |
2676 | + if ! groups |grep -q utah ; then |
2677 | + SUDO="sudo" |
2678 | + sudo TARGET_PREFIX="${TARGET_PREFIX}" PATH="${PATH}" \ |
2679 | + ${UTAH_PHABLET_CMD} \ |
2680 | + ${ADBOPTS} \ |
2681 | + --from-host \ |
2682 | + --whoopsie \ |
2683 | + --results-dir "${RESDIR}" \ |
2684 | + --skip-install --skip-network --skip-utah \ |
2685 | + --pull /var/crash \ |
2686 | + --pull /home/phablet/.cache/upstart \ |
2687 | + --pull /tmp/xmlresults \ |
2688 | + --pull /var/log/syslog \ |
2689 | + --pull /var/log/kern.log \ |
2690 | + --pull /var/log/upstart/whoopsie.log \ |
2691 | + $EXTRA_PULL \ |
2692 | + -l "${TESTSUITE_HOST}/master.run" |
2693 | + else |
2694 | + TARGET_PREFIX="${TARGET_PREFIX}" PATH="${PATH}" \ |
2695 | + ${UTAH_PHABLET_CMD} \ |
2696 | + ${ADBOPTS} \ |
2697 | + --from-host \ |
2698 | + --whoopsie \ |
2699 | + --results-dir "${RESDIR}" \ |
2700 | + --skip-install --skip-network --skip-utah \ |
2701 | + --pull /var/crash \ |
2702 | + --pull /home/phablet/.cache/upstart \ |
2703 | + --pull /tmp/xmlresults \ |
2704 | + --pull /var/log/syslog \ |
2705 | + --pull /var/log/kern.log \ |
2706 | + --pull /var/log/upstart/whoopsie.log \ |
2707 | + $EXTRA_PULL \ |
2708 | + -l "${TESTSUITE_HOST}/master.run" |
2709 | + fi |
2710 | + |
2711 | + # make sure the user running this script can remove its artifacts. |
2712 | + # only run this if we had to run under sudo |
2713 | + if [ "${SUDO}" = "sudo" ] ; then |
2714 | + sudo chown -R "${USER}" ${RESDIR} |
2715 | + fi |
2716 | +} |
2717 | + |
2718 | +assert_image() { |
2719 | + [ -z $INSTALL_URL ] && return |
2720 | + echo "Ensuring target has proper image..." |
2721 | + REQUIRED_UUID=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-uuid) |
2722 | + ACTUAL_UUID=$(adb shell "cat /home/phablet/.ci-uuid | tr -d '\r\n'") |
2723 | + if [ "$REQUIRED_UUID" != "$ACTUAL_UUID" ] ; then |
2724 | + echo "UUIDs $REQUIRED_UUID != $ACTUAL_UUID, reprovisioning device..." |
2725 | + ARGS=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-utah-args | tr -d '\r\n') |
2726 | + SERVER=$(curl ${INSTALL_URL}/artifact/clientlogs/.ci-flash-server | tr -d '\r\n') |
2727 | + UUID=$REQUIRED_UUID IMAGE_SERVER=$SERVER IMAGE_OPT=$ARGS \ |
2728 | + ${BASEDIR}/scripts/provision.sh |
2729 | + else |
2730 | + echo "UUIDS match" |
2731 | + fi |
2732 | +} |
2733 | + |
2734 | +main() { |
2735 | + rm -rf $RESDIR |
2736 | + mkdir $RESDIR |
2737 | + |
2738 | + assert_image |
2739 | + |
2740 | + # print the build date so the jenkins job can use it as the |
2741 | + # build description |
2742 | + adb pull /var/log/installer/media-info ${RESDIR} |
2743 | + BUILDID=$(adb shell cat /home/phablet/.ci-version) |
2744 | + echo "= TOUCH IMAGE VERSION:$BUILDID" |
2745 | + |
2746 | + adb shell "top -n1 -b" > ${RESDIR}/top.log |
2747 | + |
2748 | + set -x |
2749 | + adb shell 'sudo rm -f /var/crash/*' |
2750 | + if [ -z $QUICK ] ; then |
2751 | + # get the phone in sane place |
2752 | + adb reboot |
2753 | + # sometimes reboot doesn't happen fast enough, so add a little |
2754 | + # delay to help ensure its actually rebooted and we didn't just |
2755 | + # connect back to the device before it rebooted |
2756 | + adb wait-for-device |
2757 | + sleep 5 |
2758 | + adb wait-for-device |
2759 | + phablet-network --skip-setup -t 90s |
2760 | + adb shell sudo powerd-cli active & |
2761 | + PIDS="$PIDS $!" |
2762 | + adb shell sudo powerd-cli display on & |
2763 | + PIDS="$PIDS $!" |
2764 | + else |
2765 | + echo "SKIPPING phone reboot..." |
2766 | + fi |
2767 | + |
2768 | + ${BASEDIR}/utils/host/adb-shell "sudo aa-clickhook -f --include=/usr/share/autopilot-touch/apparmor/click.rules" |
2769 | + |
2770 | + echo "launching test from the host...." |
2771 | + test_from_host |
2772 | + adb shell 'sudo rm -f /var/crash/*' |
2773 | + |
2774 | + if ! `grep "^errors: [!0]" < $UTAHFILE >/dev/null` ; then |
2775 | + echo "errors found" |
2776 | + EXITCODE=1 |
2777 | + fi |
2778 | + if ! `grep "^failures: [!0]" < $UTAHFILE >/dev/null` ; then |
2779 | + echo "failures found" |
2780 | + EXITCODE=2 |
2781 | + fi |
2782 | + echo "Results Summary" |
2783 | + echo "---------------" |
2784 | + egrep '^(errors|failures|passes|fetch_errors):' $UTAHFILE |
2785 | + exit $EXITCODE |
2786 | +} |
2787 | + |
2788 | +while getopts p:s:a:Qh opt; do |
2789 | + case $opt in |
2790 | + h) |
2791 | + usage |
2792 | + exit 0 |
2793 | + ;; |
2794 | + s) |
2795 | + export ANDROID_SERIAL=$OPTARG |
2796 | + ;; |
2797 | + a) |
2798 | + APP=$OPTARG |
2799 | + ;; |
2800 | + p) |
2801 | + EXTRA_PULL_FILE=$OPTARG |
2802 | + |
2803 | + if [ ! -z $EXTRA_PULL_FILE ]; then |
2804 | + # relative paths are assumed to be relative to /home/phablet |
2805 | + E_P_START=`echo $EXTRA_PULL_FILE | cut -c1` |
2806 | + |
2807 | + if [ $E_P_START = '/' ]; then |
2808 | + EXTRA_PULL="$EXTRA_PULL --pull $EXTRA_PULL_FILE" |
2809 | + else |
2810 | + EXTRA_PULL="$EXTRA_PULL --pull /home/phablet/$EXTRA_PULL_FILE" |
2811 | + fi |
2812 | + fi |
2813 | + ;; |
2814 | + Q) |
2815 | + QUICK=1 |
2816 | + ;; |
2817 | + esac |
2818 | +done |
2819 | + |
2820 | +if [ -z $ANDROID_SERIAL ] ; then |
2821 | + # ensure we only have one device attached |
2822 | + lines=$(adb devices | wc -l) |
2823 | + if [ $lines -gt 3 ] ; then |
2824 | + echo "ERROR: More than one device attached, please use -s option" |
2825 | + echo |
2826 | + usage |
2827 | + exit 1 |
2828 | + fi |
2829 | +fi |
2830 | +if [ -z $APP ] ; then |
2831 | + echo "ERROR: No app specified" |
2832 | + usage |
2833 | + exit 1 |
2834 | +fi |
2835 | + |
2836 | +TESTSUITE_HOST=$(readlink -f ${BASEDIR}/tests/${APP}) |
2837 | +TESTSUITE_TARGET_BASE=/tmp/tests |
2838 | +TESTSUITE_TARGET=${TESTSUITE_TARGET_BASE}/$(basename ${TESTSUITE_HOST}) |
2839 | + |
2840 | +trap cleanup TERM INT EXIT |
2841 | +main |
2842 | |
2843 | === added file 'scripts/junit2utah.py' |
2844 | --- scripts/junit2utah.py 1970-01-01 00:00:00 +0000 |
2845 | +++ scripts/junit2utah.py 2015-06-03 20:37:20 +0000 |
2846 | @@ -0,0 +1,70 @@ |
2847 | +#!/usr/bin/python |
2848 | + |
2849 | +import datetime |
2850 | +import sys |
2851 | + |
2852 | +from xml.etree import ElementTree |
2853 | + |
2854 | +import yaml |
2855 | + |
2856 | + |
2857 | +def _convert_testcase(tc): |
2858 | + x = { |
2859 | + 'testcase': tc.attrib['name'], |
2860 | + 'testsuite': tc.attrib['classname'], |
2861 | + 'command': 'autopilot', |
2862 | + 'cmd_type': 'testcase_test', |
2863 | + 'stdout': '', |
2864 | + 'stderr': '', |
2865 | + 'returncode': 0 |
2866 | + } |
2867 | + t = tc.attrib.get('time', False) |
2868 | + if t: |
2869 | + x['time_delta'] = t |
2870 | + |
2871 | + for e in tc.getchildren(): |
2872 | + if e.tag in ('failure', 'error'): |
2873 | + x['stderr'] = e.text |
2874 | + x['returncode'] = 1 |
2875 | + elif e.tag == 'skip': |
2876 | + # NOTE: this isn't a real thing in UTAH. However, the |
2877 | + # qa-dashboard code doesn't care and will display it as skipped |
2878 | + x['cmd_type'] = 'testcase_skipped' |
2879 | + x['stdout'] = e.text |
2880 | + else: |
2881 | + raise RuntimeError('Unknown element type: %s' % e.tag) |
2882 | + return x |
2883 | + |
2884 | + |
2885 | +def _get_results(stream): |
2886 | + tree = ElementTree.fromstring(stream.read()) |
2887 | + results = { |
2888 | + 'errors': int(tree.attrib.get('errors', '0')), |
2889 | + 'failures': int(tree.attrib.get('failures', '0')), |
2890 | + 'commands': [], |
2891 | + 'fetch_errors': 0, |
2892 | + 'uname': 'n/a', |
2893 | + 'media-info': 'n/a', |
2894 | + 'install_type': 'n/a', |
2895 | + 'arch': 'n/a', |
2896 | + 'release': 'n/a', |
2897 | + 'build_number': 'n/a', |
2898 | + 'name': 'unamed', |
2899 | + 'runlist': 'n/a', |
2900 | + 'ran_at': datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'), |
2901 | + } |
2902 | + results['passes'] = \ |
2903 | + int(tree.attrib['tests']) - results['errors'] - results['failures'] |
2904 | + |
2905 | + for tc in tree.getchildren(): |
2906 | + results['commands'].append(_convert_testcase(tc)) |
2907 | + return results |
2908 | + |
2909 | + |
2910 | +def _main(stream): |
2911 | + results = _get_results(stream) |
2912 | + print(yaml.safe_dump(results, default_flow_style=False)) |
2913 | + |
2914 | + |
2915 | +if __name__ == '__main__': |
2916 | + exit(_main(sys.stdin)) |
2917 | |
2918 | === added file 'scripts/masher_control.py' |
2919 | --- scripts/masher_control.py 1970-01-01 00:00:00 +0000 |
2920 | +++ scripts/masher_control.py 2015-06-03 20:37:20 +0000 |
2921 | @@ -0,0 +1,64 @@ |
2922 | +#!/usr/bin/env python |
2923 | + |
2924 | +"""A utility to control the uci-phone-masher controllers in the CI lab.""" |
2925 | + |
2926 | +import argparse |
2927 | +import json |
2928 | +import sys |
2929 | +import urllib |
2930 | +import urllib2 |
2931 | + |
2932 | + |
2933 | +def set_button(urlbase, button, on): |
2934 | + '''Set the desired button to on for any non-false value of "on"''' |
2935 | + state = 'on' if on else 'off' |
2936 | + data = { |
2937 | + 'state': state, |
2938 | + } |
2939 | + url = '{}/button/{}'.format(urlbase, button) |
2940 | + try: |
2941 | + url_resp = urllib2.urlopen(url, data=urllib.urlencode(data)) |
2942 | + except urllib2.URLError as e: |
2943 | + print('ERROR: URL connection or read error on {}: {}'.format(url, e)) |
2944 | + return -1 |
2945 | + |
2946 | + try: |
2947 | + content = url_resp.read() |
2948 | + except urllib2.URLError as e: |
2949 | + print('ERROR: URL read error on {}: {}'.format(url_resp, e)) |
2950 | + return -1 |
2951 | + |
2952 | + try: |
2953 | + json_resp = json.loads(content) |
2954 | + except ValueError as e: |
2955 | + print('ERROR: json conversion failed on {}: {}'.format(content, e)) |
2956 | + return -1 |
2957 | + |
2958 | + if not json_resp.get('button_states', None) == state: |
2959 | + # This is a fail safe to make sure the controller attempted to set |
2960 | + # the button to what was requested. If it fails, dump the original |
2961 | + # content. |
2962 | + print('ERROR: unexpected response: {}'.format(content)) |
2963 | + return -1 |
2964 | + |
2965 | + return 0 |
2966 | + |
2967 | + |
2968 | +def _get_parser(): |
2969 | + parser = argparse.ArgumentParser( |
2970 | + description='Toggles a button connected to a uci-phone-masher on/off') |
2971 | + parser.add_argument('-u', '--url', |
2972 | + default='http://ci-phone-masher.ubuntu-ci', |
2973 | + help='Phone Masher controller. default=%(default)s') |
2974 | + parser.add_argument('-b', '--button', type=int, required=True, |
2975 | + help='The uci-phone-master button ID.') |
2976 | + parser.add_argument('action', metavar='action', |
2977 | + choices=('on', 'off'), |
2978 | + help='Action to perform on|off') |
2979 | + return parser |
2980 | + |
2981 | + |
2982 | +if __name__ == '__main__': |
2983 | + args = _get_parser().parse_args() |
2984 | + |
2985 | + sys.exit(set_button(args.url, args.button, args.action == 'on')) |
2986 | |
2987 | === added file 'scripts/ncd_usb.py' |
2988 | --- scripts/ncd_usb.py 1970-01-01 00:00:00 +0000 |
2989 | +++ scripts/ncd_usb.py 2015-06-03 20:37:20 +0000 |
2990 | @@ -0,0 +1,48 @@ |
2991 | +#! /usr/bin/python |
2992 | + |
2993 | +"""A utility to control the USB relay's in the QA lab.""" |
2994 | + |
2995 | +import argparse |
2996 | +import sys |
2997 | +import urllib2 |
2998 | + |
2999 | + |
3000 | +def set_relay(urlbase, bank, relay, on): |
3001 | + # the values 100/108 came from the JavaScript of our web-based management |
3002 | + # system for the relays. The meaning of the values isn't documented. |
3003 | + if on: |
3004 | + relay += 108 |
3005 | + else: |
3006 | + relay += 100 |
3007 | + cmd = '254,{},{}'.format(relay, bank + 1) |
3008 | + url = '{}/cgi-bin/runcommand.sh?1:cmd={}'.format(urlbase, cmd) |
3009 | + resp = urllib2.urlopen(url) |
3010 | + resp = resp.read() |
3011 | + if 'OK' not in resp: |
3012 | + print('ERROR: bad response: {}'.format(resp)) |
3013 | + sys.exit(1) |
3014 | + |
3015 | + |
3016 | +def _get_parser(): |
3017 | + parser = argparse.ArgumentParser( |
3018 | + description='Toggles an NCD relay connected to a USB cable on/off') |
3019 | + parser.add_argument('-u', '--url', |
3020 | + default='http://qa-relay-control.ubuntu-ci', |
3021 | + help='NCD relay URL. default=%(default)s') |
3022 | + parser.add_argument('-b', '--bank', type=int, required=True, |
3023 | + help='NCD relay 0-based bank ID.') |
3024 | + parser.add_argument('-r', '--relay', type=int, required=True, |
3025 | + help='NCD relay 0-based relay ID.') |
3026 | + parser.add_argument('action', metavar='action', |
3027 | + choices=('on', 'off'), |
3028 | + help='action to perform on|off') |
3029 | + return parser |
3030 | + |
3031 | + |
3032 | +if __name__ == '__main__': |
3033 | + args = _get_parser().parse_args() |
3034 | + |
3035 | + # NOTE: when the relay is ON usb is actually OFF. ie the logic |
3036 | + # is backwards between them, thus action=off actually turns |
3037 | + # the relay on |
3038 | + set_relay(args.url, args.bank, args.relay, args.action == 'off') |
3039 | |
3040 | === added file 'scripts/provision.sh' |
3041 | --- scripts/provision.sh 1970-01-01 00:00:00 +0000 |
3042 | +++ scripts/provision.sh 2015-06-03 20:37:20 +0000 |
3043 | @@ -0,0 +1,267 @@ |
3044 | +#!/bin/bash |
3045 | + |
3046 | +## This is the script jenkins should run to provision a device in the lab |
3047 | + |
3048 | +set -e |
3049 | + |
3050 | +BASEDIR=$(dirname $(readlink -f $0)) |
3051 | +export PATH=${BASEDIR}/../utils/host:${PATH} |
3052 | + |
3053 | +RESDIR=`pwd`/clientlogs |
3054 | +RECOVERY_URL="http://people.canonical.com/~plars/touch" |
3055 | + |
3056 | +NETWORK_FILE="${NETWORK_FILE-${HOME}/.ubuntu-ci/wifi.conf}" |
3057 | + |
3058 | +IMAGE_OPT="${IMAGE_OPT---bootstrap --developer-mode --channel ubuntu-touch/devel-proposed/ubuntu}" |
3059 | +UUID="${UUID-$(uuidgen -r)}" |
3060 | +PHABLET_PASSWORD="${PHABLET_PASSWORD-0000}" |
3061 | + |
3062 | +usage() { |
3063 | +cat <<EOF |
3064 | +usage: $0 [-s ANDROID_SERIAL] [-n NETWORK_FILE] [-P ppa] [-p package] [-r revision] [-w] |
3065 | + |
3066 | +Provisions the given device with the latest build |
3067 | + |
3068 | +OPTIONS: |
3069 | + -h Show this message |
3070 | + -s Specify the serial of the device to install |
3071 | + -n Select network file |
3072 | + -P add the ppa to the target (can be repeated) |
3073 | + -D add a debian package dir to the target (can be repeated) |
3074 | + -p add the package to the target (can be repeated) |
3075 | + -r Specify the image revision to flash |
3076 | + -w make the system writeable (implied with -p and -P arguments) |
3077 | + |
3078 | +EOF |
3079 | +} |
3080 | + |
3081 | +image_info() { |
3082 | + # mark the version we installed in /home/phablet/.ci-[uuid,flash-args] |
3083 | + # adb shell messes up \n's with \r\n's so do the whole of the regex on the target |
3084 | + IMAGEVER=$(adb shell "sudo system-image-cli -i | sed -n -e 's/version version: \([0-9]*\)/\1/p' -e 's/version ubuntu: \([0-9]*\)/\1/p' -e 's/version device: \([0-9]*\)/\1/p' | paste -s -d:") |
3085 | + CHAN=$(adb shell "sudo system-image-cli -i | sed -n -e 's/channel: \(.*\)/\1/p' | paste -s -d:") |
3086 | + REV=$(echo $IMAGEVER | cut -d: -f1) |
3087 | + echo "$IMAGE_OPT" | grep -q "\-\-revision" || REVISION="--revision=${REV}" |
3088 | + echo "$IMAGE_OPT" | grep -q "\-\-channel" || IMAGE_OPT="${IMAGE_OPT} --channel $CHAN" |
3089 | + adb shell "echo '${IMAGEVER}' > /home/phablet/.ci-version" |
3090 | + echo $UUID > $RESDIR/.ci-uuid |
3091 | + adb push $RESDIR/.ci-uuid /home/phablet/ |
3092 | + cat > $RESDIR/.ci-flash-args <<EOF |
3093 | +$IMAGE_OPT |
3094 | +EOF |
3095 | + adb push $RESDIR/.ci-flash-args /home/phablet/.ci-flash-args |
3096 | + cat > $RESDIR/.ci-flash-server <<EOF |
3097 | +$IMAGE_SERVER |
3098 | +EOF |
3099 | + adb push $RESDIR/.ci-flash-args /home/phablet/.ci-flash-server |
3100 | + echo $CUSTOMIZE > $RESDIR/.ci-customizations |
3101 | + adb push $RESDIR/.ci-customizations /home/phablet/.ci-customizations |
3102 | +} |
3103 | + |
3104 | +log() { |
3105 | + echo = $(date): $* |
3106 | +} |
3107 | + |
3108 | +set_hwclock() { |
3109 | + log "SETTING HWCLOCK TO CURRENT TIME" |
3110 | + # Use ip for ntp.ubuntu.com in case resolving doesn't work yet |
3111 | + adb-shell sudo ntpdate 91.189.94.4 || log "WARNING: could not set ntpdate" |
3112 | + # hwclock sync has to happen after we set writable image |
3113 | + adb-shell sudo hwclock -w || log "WARNING: could not sync hwclock" |
3114 | + log "Current date on device is:" |
3115 | + adb shell date |
3116 | + log "Current hwclock on device is:" |
3117 | + adb shell sudo hwclock |
3118 | +} |
3119 | + |
3120 | +retry() { |
3121 | + timeout=$1 |
3122 | + shift |
3123 | + loops=$1 |
3124 | + shift |
3125 | + cmd=$* |
3126 | + loopcnt=0 |
3127 | + while true; do |
3128 | + $cmd && break || { |
3129 | + if [ $loopcnt -lt $loops ] ; then |
3130 | + loopcnt=$[$loopcnt+1] |
3131 | + echo "Retry [$loopcnt/$loops] after $timeout seconds..." |
3132 | + sleep $timeout |
3133 | + else |
3134 | + echo Failed on \'$cmd\' after $loops retries |
3135 | + exit 1 |
3136 | + fi |
3137 | + } |
3138 | + done |
3139 | +} |
3140 | + |
3141 | +reboot_bootloader() { |
3142 | + # In CI, we've seen cases where 'adb reboot bootloader' will just |
3143 | + # reboot the device and not enter the bootloader. Adding another |
3144 | + # reboot and retrying was found to be a successful workaround: |
3145 | + # https://bugs.launchpad.net/ubuntu/+source/android-tools/+bug/1359488 |
3146 | + # |
3147 | + # We only want to do this if we know ANDROID_SERIAL. Attempting |
3148 | + # to guess might end up flashing the wrong device. |
3149 | + |
3150 | + log "Attempting adb reboot bootloader" |
3151 | + adb reboot bootloader |
3152 | + if [ -n "${ANDROID_SERIAL}" ] ; then |
3153 | + # Entering the bootloader should take < 10 seconds, add some |
3154 | + # padding for device variance. |
3155 | + sleep 30 |
3156 | + if ! fastboot devices | grep -q "${ANDROID_SERIAL}"; then |
3157 | + log "Device not in fastboot after adb reboot bootloader" |
3158 | + # After a failed 'reboot bootloader' attempt, a reboot |
3159 | + # is used to get the device back to a saner state. |
3160 | + adb reboot |
3161 | + return 1 |
3162 | + fi |
3163 | + fi |
3164 | + return 0 |
3165 | +} |
3166 | + |
3167 | +download_recovery () { |
3168 | + # FIXME: ev mentioned on irc that we should add some cheksum for |
3169 | + # those images -- vila 2015-02-20 |
3170 | + wget -P recovery ${RECOVERY_URL}/recovery-${DEVICE_TYPE}.img |
3171 | + if [ -f recovery/recovery-${DEVICE_TYPE}.img ]; then |
3172 | + RECOVERY="--recovery-image=recovery/recovery-${DEVICE_TYPE}.img" |
3173 | + return 0 |
3174 | + fi |
3175 | + return 1 |
3176 | +} |
3177 | + |
3178 | +full_flash() { |
3179 | + log "FLASHING DEVICE" |
3180 | + DEVICE_TYPE=$(get-device-type) |
3181 | + # Use a 60 second retry loop for reboot_bootloader. |
3182 | + # If the attempt failed, it may take nearly 60 seconds to complete |
3183 | + # the reboot cycle to get the device back to a sane state. |
3184 | + retry 60 3 reboot_bootloader |
3185 | + RECOVERY="" |
3186 | + # We need to distinguish between devices with no recovery images and |
3187 | + # failures to download existing recovery images. Only krillin |
3188 | + # and arale have a recovery image for now. |
3189 | + if [ "${DEVICE_TYPE}" == 'krillin' ] || |
3190 | + [ "${DEVICE_TYPE}" == 'arale' ]; then |
3191 | + mkdir -p recovery |
3192 | + retry 10 3 download_recovery |
3193 | + fi |
3194 | + # Use a 10 second retry loop for ubuntu-device-flash. |
3195 | + # Most failures appear to be transient and work with an immediate |
3196 | + # retry. |
3197 | + retry 10 3 timeout 1800 ubuntu-device-flash ${IMAGE_SERVER} ${REVISION} touch ${RECOVERY} --password $PHABLET_PASSWORD $IMAGE_OPT |
3198 | + # If the flashed image fails to install and reboots, wait-for-device |
3199 | + # will timeout |
3200 | + timeout 600 adb wait-for-device |
3201 | + sleep 60 #give the system a little time |
3202 | +} |
3203 | + |
3204 | +while getopts i:s:n:P:D:p:r:wh opt; do |
3205 | + case $opt in |
3206 | + h) |
3207 | + usage |
3208 | + exit 0 |
3209 | + ;; |
3210 | + n) |
3211 | + NETWORK_FILE=$OPTARG |
3212 | + ;; |
3213 | + s) |
3214 | + export ANDROID_SERIAL=$OPTARG |
3215 | + ;; |
3216 | + i) |
3217 | + IMAGE_TYPE=$OPTARG |
3218 | + ;; |
3219 | + w) |
3220 | + # making this a non-zero length string enables the logic |
3221 | + CUSTOMIZE=" " |
3222 | + ;; |
3223 | + P) |
3224 | + CUSTOMIZE="$CUSTOMIZE --ppa $OPTARG" |
3225 | + ;; |
3226 | + D) |
3227 | + CUSTOMIZE="$CUSTOMIZE --package-dir $OPTARG" |
3228 | + ;; |
3229 | + p) |
3230 | + CUSTOMIZE="$CUSTOMIZE -p $OPTARG" |
3231 | + ;; |
3232 | + r) |
3233 | + REVISION="--revision=$OPTARG" |
3234 | + ;; |
3235 | + |
3236 | + esac |
3237 | +done |
3238 | + |
3239 | +if [ -z $ANDROID_SERIAL ] ; then |
3240 | + # ensure we only have one device attached |
3241 | + lines=$(adb devices | wc -l) |
3242 | + if [ $lines -gt 3 ] ; then |
3243 | + echo "ERROR: More than one device attached, please use -s option" |
3244 | + echo |
3245 | + usage |
3246 | + exit 1 |
3247 | + fi |
3248 | +fi |
3249 | + |
3250 | +if [ ! -f $NETWORK_FILE ] && [ -z $USE_EMULATOR ] ; then |
3251 | + echo "ERROR: NETWORK_FILE, $NETWORK_FILE, not found" |
3252 | + exit 1 |
3253 | +fi |
3254 | + |
3255 | +set -x |
3256 | +[ -d $RESDIR ] && rm -rf $RESDIR |
3257 | +mkdir -p $RESDIR |
3258 | + |
3259 | +if [ -z $USE_EMULATOR ] ; then |
3260 | + full_flash |
3261 | +else |
3262 | + log "CREATING EMULATOR" |
3263 | + ubuntu-emulator destroy --yes $ANDROID_SERIAL || true |
3264 | + sudo ubuntu-emulator create $ANDROID_SERIAL $IMAGE_OPT |
3265 | + ${BASEDIR}/reboot-and-wait |
3266 | +fi |
3267 | + |
3268 | +if [ -z $USE_EMULATOR ] ; then |
3269 | + log "SETTING UP WIFI" |
3270 | + retry 60 5 adb-shell 'sudo -iu phablet env |grep UPSTART_SESSION=unix' |
3271 | + retry 60 5 phablet-network -n $NETWORK_FILE |
3272 | +fi |
3273 | + |
3274 | +phablet-config welcome-wizard --disable |
3275 | + |
3276 | +if [ -n "$CUSTOMIZE" ] ; then |
3277 | + log "CUSTOMIZING IMAGE" |
3278 | + phablet-config writable-image -r ${PHABLET_PASSWORD} $CUSTOMIZE |
3279 | +fi |
3280 | + |
3281 | +log "SETTING UP SUDO" |
3282 | +adb shell "echo ${PHABLET_PASSWORD} |sudo -S bash -c 'echo phablet ALL=\(ALL\) NOPASSWD: ALL > /etc/sudoers.d/phablet && chmod 600 /etc/sudoers.d/phablet'" |
3283 | + |
3284 | +# FIXME: Can't do this through phablet-config for now because it needs auth |
3285 | +# phablet-config edges-intro --disable |
3286 | +adb shell "sudo dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User32011 org.freedesktop.DBus.Properties.Set string:com.canonical.unity.AccountsService string:demo-edges variant:boolean:false" |
3287 | + |
3288 | +if [ -n "${SKIP_CLICK}" ]; then |
3289 | + log "SKIPPING CLICK PACKAGE SETUP AS REQUESTED" |
3290 | +else |
3291 | + log "SETTING UP CLICK PACKAGES" |
3292 | + CLICK_TEST_OPTS="" |
3293 | + channel_name=$(adb shell "sudo system-image-cli -i | sed -n -e 's/channel: \(.*\)/\1/p' | paste -s -d:") |
3294 | + # Before running phablet-click-test setup, we need to make sure the |
3295 | + # session is available |
3296 | + retry 60 5 adb-shell 'sudo -iu phablet env |grep UPSTART_SESSION=unix' |
3297 | + |
3298 | + # FIXME: workaround for phablet-click-test-setup to pull the right sources |
3299 | + if [[ $channel_name == *rtm* ]] ; then |
3300 | + CLICK_TEST_OPTS="--distribution ubuntu-rtm --series 14.09" |
3301 | + fi |
3302 | + phablet-click-test-setup $CLICK_TEST_OPTS |
3303 | +fi |
3304 | + |
3305 | +# get our target-based utilities into our PATH |
3306 | +adb push ${BASEDIR}/../utils/target /home/phablet/bin |
3307 | + |
3308 | +image_info |
3309 | + |
3310 | +set_hwclock |
3311 | |
3312 | === added file 'scripts/reboot-and-wait' |
3313 | --- scripts/reboot-and-wait 1970-01-01 00:00:00 +0000 |
3314 | +++ scripts/reboot-and-wait 2015-06-03 20:37:20 +0000 |
3315 | @@ -0,0 +1,54 @@ |
3316 | +#!/usr/bin/python |
3317 | + |
3318 | +import argparse |
3319 | +import logging |
3320 | +import os |
3321 | +import subprocess |
3322 | +import time |
3323 | + |
3324 | +from phabletutils.device import AndroidBridge |
3325 | + |
3326 | +EMULATOR = os.environ.get('USE_EMULATOR', '') |
3327 | + |
3328 | + |
3329 | +def _get_arg_parser(): |
3330 | + parser = argparse.ArgumentParser( |
3331 | + description='Reboot device and waits for networking to become active.') |
3332 | + parser.add_argument('-s', '--serial', help='Device serial') |
3333 | + parser.add_argument('-n', '--num-tries', type=int, default=3, |
3334 | + help='''How many times to retry on failure. |
3335 | + default=%(default)d''') |
3336 | + return parser |
3337 | + |
3338 | + |
3339 | +def main(args): |
3340 | + device = AndroidBridge(args.serial) |
3341 | + device.wait_for_device() |
3342 | + for i in range(args.num_tries): |
3343 | + device.reboot() |
3344 | + device.wait_for_device() |
3345 | + time.sleep(5) |
3346 | + device.wait_for_device() |
3347 | + try: |
3348 | + device.wait_for_network() |
3349 | + return 0 |
3350 | + except: |
3351 | + pass # try the loop again |
3352 | + logging.error('device failed to start and activate networking') |
3353 | + return 1 |
3354 | + |
3355 | + |
3356 | +def emulator_main(args): |
3357 | + emulator = os.path.join(os.path.dirname(__file__), 'run-emulator') |
3358 | + subprocess.check_call([emulator]) |
3359 | + |
3360 | + |
3361 | +if __name__ == '__main__': |
3362 | + logging.basicConfig(level=logging.INFO) |
3363 | + logging.getLogger().name = 'reboot-and-wait' |
3364 | + args = _get_arg_parser().parse_args() |
3365 | + if EMULATOR: |
3366 | + logging.info('using emulator logic for reboot') |
3367 | + exit(emulator_main(args)) |
3368 | + else: |
3369 | + exit(main(args)) |
3370 | |
3371 | === added file 'scripts/recover.py' |
3372 | --- scripts/recover.py 1970-01-01 00:00:00 +0000 |
3373 | +++ scripts/recover.py 2015-06-03 20:37:20 +0000 |
3374 | @@ -0,0 +1,128 @@ |
3375 | +#!/usr/bin/env python |
3376 | + |
3377 | +import device_info |
3378 | +import logging |
3379 | +import os |
3380 | +import requests |
3381 | +import sys |
3382 | +import time |
3383 | +import yaml |
3384 | + |
3385 | +log = logging.getLogger() |
3386 | +logging.basicConfig(level=logging.INFO) |
3387 | + |
3388 | + |
3389 | +def _full_recovery(device): |
3390 | + try: |
3391 | + device.force_bootloader() |
3392 | + except: |
3393 | + #This device does not have information about relays |
3394 | + _offline_device() |
3395 | + raise |
3396 | + try: |
3397 | + device.wait_for_fastboot() |
3398 | + device.reimage_from_fastboot() |
3399 | + except: |
3400 | + _offline_device() |
3401 | + raise |
3402 | + try: |
3403 | + device.check_adb_shell() |
3404 | + except: |
3405 | + # The device looks like it's available, but not responding |
3406 | + _offline_device() |
3407 | + raise device_info.DeviceError("Could not fully recover device") |
3408 | + return 0 |
3409 | + |
3410 | + |
3411 | +def _get_jenkins_creds(url): |
3412 | + try: |
3413 | + home = os.environ.get('HOME') |
3414 | + credpath = os.path.join(home, '.ubuntu-ci/jenkins-keys.yaml') |
3415 | + with open(credpath) as credfile: |
3416 | + creds = yaml.load(credfile.read()) |
3417 | + jenkins = creds.get(url) |
3418 | + user = jenkins.get('user') |
3419 | + key = jenkins.get('key') |
3420 | + except (AttributeError, IOError): |
3421 | + user = None |
3422 | + key = None |
3423 | + return (user, key) |
3424 | + |
3425 | + |
3426 | +def _offline_device(): |
3427 | + # It's unlikely the node name will be different, but just in case |
3428 | + node = os.environ.get('NODE_NAME', None) |
3429 | + host = os.environ.get('JENKINS_URL', None) |
3430 | + (user, key) = _get_jenkins_creds(host) |
3431 | + if not (user and key and host and node): |
3432 | + log.warn("Unable to mark device offline automatically") |
3433 | + return |
3434 | + url = "{}/computer/{}/toggleOffline".format(host, node) |
3435 | + param_data = {'offlineMessage': 'unrecoverable'} |
3436 | + delay = 2 |
3437 | + # Retry with exponential delay from 1 to 128 seconds |
3438 | + # This will retry for no longer than 4 min 15 sec before failing |
3439 | + for attempt in range(8): |
3440 | + time.sleep(delay ** attempt) |
3441 | + try: |
3442 | + response = requests.post(url, params=param_data, auth=(user, key)) |
3443 | + except Exception as exc: |
3444 | + log.exception('Error contacting jenkins: {}'.format(exc.message)) |
3445 | + continue |
3446 | + if response.status_code != 200: |
3447 | + log.warn("Error marking {} offline, retrying".format(node)) |
3448 | + else: |
3449 | + log.info("{} has been marked offline".format(node)) |
3450 | + return |
3451 | + log.error("Fatal error marking {} offline".format(node)) |
3452 | + |
3453 | + |
3454 | +def recover(device_name): |
3455 | + try: |
3456 | + device = device_info.get_device(device_name) |
3457 | + except KeyError: |
3458 | + log.error("No device found for '{}'".format(device_name)) |
3459 | + raise |
3460 | + state = device.get_state() |
3461 | + if state in ('device'): |
3462 | + try: |
3463 | + device.check_adb_shell() |
3464 | + except: |
3465 | + # The device looks like it's available, but not responding |
3466 | + return _full_recovery(device) |
3467 | + #The device can proceed with testing |
3468 | + return 0 |
3469 | + if state == 'recovery': |
3470 | + try: |
3471 | + device.reboot() |
3472 | + device.wait_for_device() |
3473 | + device.check_adb_shell() |
3474 | + return 0 |
3475 | + except: |
3476 | + return _full_recovery(device) |
3477 | + if state == 'fastboot': |
3478 | + #The device is in fastboot right now, we need it booted first |
3479 | + try: |
3480 | + device.reimage_from_fastboot() |
3481 | + device.check_adb_shell() |
3482 | + return 0 |
3483 | + except: |
3484 | + # The device looks like it's available, but not responding |
3485 | + return _full_recovery(device) |
3486 | + if state in ('offline', 'unknown', 'disconnected'): |
3487 | + #The device is in an unknown state, we need full recovery |
3488 | + return _full_recovery(device) |
3489 | + #In theory, we should never get here, but.... |
3490 | + _offline_device() |
3491 | + raise device_info.DeviceError( |
3492 | + "Device '{}' is in an unknown state!".format(device_name)) |
3493 | + |
3494 | + |
3495 | +if __name__ == '__main__': |
3496 | + name = sys.argv[1] |
3497 | + try: |
3498 | + print(recover(name)) |
3499 | + except AttributeError: |
3500 | + #This is what we'll get if it's an unknown device, raise for |
3501 | + #everything else so we get better debugging information |
3502 | + sys.exit(-1) |
3503 | |
3504 | === added file 'scripts/run-adt.py' |
3505 | --- scripts/run-adt.py 1970-01-01 00:00:00 +0000 |
3506 | +++ scripts/run-adt.py 2015-06-03 20:37:20 +0000 |
3507 | @@ -0,0 +1,38 @@ |
3508 | +#!/usr/bin/python3 |
3509 | + |
3510 | +# A simple script that runs adt-run, and retries a certain number of times |
3511 | +# if it didn't succeed. |
3512 | +# |
3513 | +# To use: simply call this with all the adt-run arguments afterwards, including |
3514 | +# the adt-run call as well: |
3515 | +# |
3516 | +# ./scripts/run-adt.py adt-run --unbuilt-tree foo -o results ..... |
3517 | + |
3518 | +import subprocess |
3519 | +import sys |
3520 | + |
3521 | +# Number of times to run adt-run before giving up. |
3522 | +MAX_RUN_COUNT = 3 |
3523 | + |
3524 | +# adt-run exit codes we should re-run the adt-run call on: |
3525 | +RETRY_CODES = (16, 20) |
3526 | + |
3527 | + |
3528 | +def main(): |
3529 | + arguments = sys.argv[1:] |
3530 | + for _ in range(MAX_RUN_COUNT): |
3531 | + with open('adt-run-stdout', 'wb') as stdout_file, \ |
3532 | + open('adt-run-stderr', 'wb') as stderr_file: |
3533 | + process = subprocess.Popen( |
3534 | + arguments, |
3535 | + stdout=stdout_file, |
3536 | + stderr=stderr_file, |
3537 | + ) |
3538 | + returncode = process.wait() |
3539 | + if returncode not in RETRY_CODES: |
3540 | + break |
3541 | + sys.exit(returncode) |
3542 | + |
3543 | + |
3544 | +if __name__ == '__main__': |
3545 | + main() |
3546 | |
3547 | === added file 'scripts/run-autopilot-tests.sh' |
3548 | --- scripts/run-autopilot-tests.sh 1970-01-01 00:00:00 +0000 |
3549 | +++ scripts/run-autopilot-tests.sh 2015-06-03 20:37:20 +0000 |
3550 | @@ -0,0 +1,267 @@ |
3551 | +#!/bin/sh |
3552 | + |
3553 | +set -e |
3554 | + |
3555 | +BASEDIR=$(dirname $(readlink -f $0))/.. |
3556 | +RESDIR=`pwd`/clientlogs |
3557 | + |
3558 | +export PATH=${BASEDIR}/utils/host:${PATH} |
3559 | +export TARGET_PREFIX=adb-shell |
3560 | + |
3561 | + |
3562 | +usage() { |
3563 | + cat <<EOF |
3564 | +usage: $0 -a APP [-s ANDROID_SERIAL] [-Q] [-o results_dir] [-S] |
3565 | + |
3566 | +Runs a set of autopilot tests on the target |
3567 | + |
3568 | +OPTIONS: |
3569 | + -h Show this message |
3570 | + -s Specify the serial of the device to test |
3571 | + -a The application to test (can be repeated) |
3572 | + -o Specify the directory to place results in. |
3573 | + Default: $RESDIR |
3574 | + -Q "Quick" don't do a reboot of the device before/between testsuites. |
3575 | + -S Skip the system-settle tests that run before/after each testsuite. |
3576 | + |
3577 | +EOF |
3578 | +} |
3579 | + |
3580 | +log_error() { |
3581 | + echo ERROR: $* >> ${RESDIR}/runner-errors.txt |
3582 | +} |
3583 | + |
3584 | +setup_test() { |
3585 | + app=$1 |
3586 | + label=$2 |
3587 | + odir=$3 |
3588 | + { |
3589 | + pkgs=$(${BASEDIR}/jenkins/testconfig.py packages -a $app) |
3590 | + if [ "$label" = "setup" ] ; then |
3591 | + if [ -z "${SKIP_TESTCONFIG}" ]; then |
3592 | + adb-shell sudo apt-get install -yq --force-yes $pkgs |
3593 | + fi |
3594 | + else |
3595 | + if [ -z "${SKIP_TESTCONFIG}" ]; then |
3596 | + #Always remove dbus-x11 because it causes |
3597 | + #problems when we leave it around |
3598 | + pkgs="$pkgs dbus-x11" |
3599 | + adb-shell sudo apt-get autoremove --purge -y $pkgs || /bin/true |
3600 | + fi |
3601 | + fi |
3602 | + echo $? > ${odir}/setup_${label}.rc |
3603 | + } 2>&1 | tee ${odir}/setup_${label}.log |
3604 | +} |
3605 | + |
3606 | +system_settle() { |
3607 | + [ -z $NOSETTLE ] || return 0 |
3608 | + |
3609 | + label=$1 |
3610 | + odir=$2 |
3611 | + rc=0 |
3612 | + timeout=120s |
3613 | + if [ "$label" = "before" ] ; then |
3614 | + timeout=300s |
3615 | + fi |
3616 | + |
3617 | + settle=${BASEDIR}/tests/systemsettle/systemsettle.sh |
3618 | + { |
3619 | + export UTAH_PROBE_DIR=${odir} # needed for log file location |
3620 | + timeout $timeout $settle -c5 -d6 -p 95 -l $label || rc=1 |
3621 | + echo $rc > ${odir}/settle_${label}.rc |
3622 | + } 2>&1 | tee ${odir}/settle_${label}.log |
3623 | +} |
3624 | + |
3625 | +test_app() { |
3626 | + app=$1 |
3627 | + |
3628 | + odir=${RESDIR}/${app} |
3629 | + [ -d $odir ] && rm -rf $odir |
3630 | + mkdir -p $odir || return 1 |
3631 | + |
3632 | + system_settle before $odir |
3633 | + phablet-config autopilot --dbus-probe enable || \ |
3634 | + (log_error "'autopilot dbus-probe enable' failed"; return 1) |
3635 | + adb-shell /home/phablet/bin/check-clickhook-rules || \ |
3636 | + (log_error "some click profiles missing autopilot rules") |
3637 | + |
3638 | + setup_test $app setup $odir |
3639 | + |
3640 | + NOSHELL="" |
3641 | + [ "$app" = "unity8" ] && NOSHELL="-n" |
3642 | + EXTRA="" |
3643 | + # Use --timeout-profile=long only if we are using the emulator |
3644 | + [ -z $USE_EMULATOR ] || EXTRA="-A '--timeout-profile=long'" |
3645 | + |
3646 | + phablet-test-run \ |
3647 | + $NOSHELL $EXTRA \ |
3648 | + -o ${odir} -f subunit \ |
3649 | + -a /var/crash -a /home/phablet/.cache/upstart \ |
3650 | + -a /var/log/syslog \ |
3651 | + -A --timeout-profile=long \ |
3652 | + -v $app || true |
3653 | + adb shell rm -rf /tmp/ci-logs |
3654 | + adb shell mkdir /tmp/ci-logs |
3655 | + adb shell sudo install -o phablet \ |
3656 | + -m 666 /var/log/upstart/whoopsie.log /tmp/ci-logs |
3657 | + adb-shell "sudo system-image-cli --info > /tmp/ci-logs/system-image-cli.log" |
3658 | + adb-shell "dpkg -l > /tmp/ci-logs/dpkg-l.log" |
3659 | + adb pull /tmp/ci-logs ${odir} |
3660 | + |
3661 | + system_settle after $odir |
3662 | + setup_test $app teardown $odir |
3663 | + if [ -f ${odir}/test_results.subunit ] ; then |
3664 | + cat ${odir}/test_results.subunit | subunit2junitxml > ${odir}/test_results.xml |
3665 | + fi |
3666 | + ${BASEDIR}/scripts/combine_results ${odir} |
3667 | +} |
3668 | + |
3669 | +reboot_wait() { |
3670 | + if [ -z $QUICK ] ; then |
3671 | + if ! reboot-and-unlock.sh; then |
3672 | + log_error "Failed to unlock screen, retrying..." |
3673 | + reboot-and-unlock.sh || log_error "Screen unlock still fails, continuing anyway" |
3674 | + fi |
3675 | + FILES="/var/crash/* /home/phablet/.cache/upstart/*.log*" |
3676 | + if ! adb shell "sudo rm -rf $FILES" ; then |
3677 | + log_error "unable to remove crash and log files, retrying" |
3678 | + adb wait-for-device |
3679 | + adb shell "sudo rm -rf $FILES" |
3680 | + fi |
3681 | + else |
3682 | + echo "SKIPPING phone reboot..." |
3683 | + fi |
3684 | +} |
3685 | + |
3686 | +if [ -z $USE_EMULATOR ] ; then |
3687 | +grab_powerd() { |
3688 | + echo "grabbing powerd cli locks..." |
3689 | + adb shell sudo powerd-cli active & |
3690 | + PIDS="$!" |
3691 | + adb shell sudo powerd-cli display on & |
3692 | + PIDS="$PIDS $!" |
3693 | +} |
3694 | + |
3695 | +release_powerd() { |
3696 | + if [ -n "$PIDS" ] ; then |
3697 | + echo "killing child pids: $PIDS" |
3698 | + for p in $PIDS ; do |
3699 | + kill $p || true |
3700 | + done |
3701 | + PIDS="" |
3702 | + fi |
3703 | + adb shell sudo pkill powerd-cli |
3704 | +} |
3705 | + |
3706 | +else |
3707 | +grab_powerd() { |
3708 | + #emulator does not use powerd, so this is noop |
3709 | + return 0 |
3710 | +} |
3711 | + |
3712 | +release_powerd() { |
3713 | + #emulator does not use powerd, so this is noop |
3714 | + return 0 |
3715 | +} |
3716 | +fi |
3717 | + |
3718 | +dashboard_update() { |
3719 | + # only try and update the dashboard if we are configured to |
3720 | + [ -z $DASHBOARD_KEY ] && return 0 |
3721 | + [ -z $DASHBOARD_BUILD ] && return 0 |
3722 | + [ -z $DASHBOARD_IMAGE ] && return 0 |
3723 | + ${BASEDIR}/scripts/dashboard.py $* \ |
3724 | + --image $DASHBOARD_IMAGE \ |
3725 | + --build $DASHBOARD_BUILD || true |
3726 | +} |
3727 | + |
3728 | +dashboard_result_running() { |
3729 | + dashboard_update result-running --test $1 |
3730 | +} |
3731 | + |
3732 | +dashboard_result_syncing() { |
3733 | + xunit=${RESDIR}/${app}/test_results.xml |
3734 | + [ -f $xunit ] || return 0 |
3735 | + |
3736 | + # save a utah.yaml version of the results so the dashboard can process |
3737 | + cat $xunit | ${BASEDIR}/scripts/junit2utah.py > ${RESDIR}/${app}/utah.yaml |
3738 | + dashboard_update result-syncing --test $1 --results ${RESDIR}/${app}/utah.yaml |
3739 | +} |
3740 | + |
3741 | +main() { |
3742 | + [ -d $RESDIR ] || mkdir -p $RESDIR |
3743 | + |
3744 | + set -x |
3745 | + |
3746 | + for app in $APPS ; do |
3747 | + set +x |
3748 | + echo "========================================================" |
3749 | + echo "= testing $app" |
3750 | + echo "========================================================" |
3751 | + set -x |
3752 | + dashboard_result_running $app |
3753 | + reboot_wait |
3754 | + |
3755 | + grab_powerd |
3756 | + |
3757 | + if ! test_app $app ; then |
3758 | + log_error "testing $app, retrying" |
3759 | + # we sometimes see sporatic adb failures that seem to |
3760 | + # related to MTP. This adds a retry for the test. |
3761 | + # test_app only fails on a device error (not a test |
3762 | + # case error) |
3763 | + adb wait-for-device |
3764 | + test_app $app |
3765 | + fi |
3766 | + dashboard_result_syncing $app |
3767 | + |
3768 | + release_powerd |
3769 | + done |
3770 | +} |
3771 | + |
3772 | +while getopts s:a:o:QSh opt; do |
3773 | + case $opt in |
3774 | + h) |
3775 | + usage |
3776 | + exit 0 |
3777 | + ;; |
3778 | + s) |
3779 | + export ANDROID_SERIAL=$OPTARG |
3780 | + ;; |
3781 | + o) |
3782 | + RESDIR=$OPTARG |
3783 | + ;; |
3784 | + a) |
3785 | + APPS="$APPS $OPTARG" |
3786 | + ;; |
3787 | + Q) |
3788 | + QUICK=1 |
3789 | + ;; |
3790 | + S) |
3791 | + NOSETTLE=1 |
3792 | + ;; |
3793 | + esac |
3794 | +done |
3795 | + |
3796 | +if [ -z $ANDROID_SERIAL ] ; then |
3797 | + # ensure we only have one device attached |
3798 | + lines=$(adb devices | wc -l) |
3799 | + if [ $lines -gt 3 ] ; then |
3800 | + echo "ERROR: More than one device attached, please use -s option" |
3801 | + echo |
3802 | + usage |
3803 | + exit 1 |
3804 | + fi |
3805 | +fi |
3806 | +if [ -z "$APPS" ] ; then |
3807 | + echo "ERROR: No app specified" |
3808 | + usage |
3809 | + exit 1 |
3810 | +fi |
3811 | + |
3812 | +trap release_powerd TERM INT EXIT |
3813 | +if [ -n "$USE_EMULATOR" ] ; then |
3814 | + echo "disabling system-settle testing for emulator" |
3815 | + NOSETTLE=1 |
3816 | +fi |
3817 | +main |
3818 | |
3819 | === added file 'scripts/run-emulator' |
3820 | --- scripts/run-emulator 1970-01-01 00:00:00 +0000 |
3821 | +++ scripts/run-emulator 2015-06-03 20:37:20 +0000 |
3822 | @@ -0,0 +1,97 @@ |
3823 | +#!/usr/bin/python |
3824 | + |
3825 | +import logging |
3826 | +import os |
3827 | +import subprocess |
3828 | +import sys |
3829 | +import time |
3830 | + |
3831 | +FULL_RETRIES = 3 |
3832 | +# unity8 takes a long time because it needs apparmor to start. apparmor is |
3833 | +# slow because its parsing all the profiles on first boot. qemu seems to |
3834 | +# be inconsisent, sometimes this takes 8 tries, sometimes >40. |
3835 | +UNITY_RETRIES = 50 |
3836 | +UNITY_WAIT = 20 |
3837 | +ADB_RETRIES = 3 |
3838 | +ADB_WAIT = 120 |
3839 | + |
3840 | +EMULATOR_ARCH = os.environ.get('EMULATOR_ARCH', 'x86') |
3841 | + |
3842 | + |
3843 | +class RetryException(Exception): |
3844 | + pass |
3845 | + |
3846 | + |
3847 | +def _kill(): |
3848 | + logging.info('killing all emulator pids') |
3849 | + with open('/dev/null', 'w') as f: |
3850 | + subprocess.call(['killall', 'ubuntu-emulator'], stderr=f) |
3851 | + time.sleep(1) |
3852 | + subprocess.call(['killall', 'emulator-' + EMULATOR_ARCH], stderr=f) |
3853 | + time.sleep(1) |
3854 | + |
3855 | + |
3856 | +def _launch(): |
3857 | + logging.info('launching emulator...') |
3858 | + subprocess.Popen(['ubuntu-emulator', 'run', os.environ['ANDROID_SERIAL']]) |
3859 | + |
3860 | + |
3861 | +def _adb_wait(retries, timeout): |
3862 | + timeout = '%ds' % timeout |
3863 | + for i in range(retries): |
3864 | + logging.info('waiting for emulator via adb (%d of %d)...', i, retries) |
3865 | + rc = subprocess.call(['timeout', timeout, 'adb', 'wait-for-device']) |
3866 | + if rc == 0: |
3867 | + return |
3868 | + # the emulator isn't always being detected by the adb-server |
3869 | + # running kill-server works around this. NOTE: this is only |
3870 | + # safe when run on a slave hooked up to a single emulator |
3871 | + logging.info('emulator not found, restarting adbd') |
3872 | + subprocess.check_call(['adb', 'kill-server']) |
3873 | + return RetryException('emulator not found via adb') |
3874 | + |
3875 | + |
3876 | +def _unity_wait(retries, timeout): |
3877 | + for i in range(retries): |
3878 | + logging.info('waiting for unity8 (%d of %d)...', i, retries) |
3879 | + time.sleep(timeout) |
3880 | + try: |
3881 | + out = subprocess.check_output( |
3882 | + ['adb', 'shell', 'sudo -i -u phablet status unity8']) |
3883 | + if 'start/running' in out: |
3884 | + return |
3885 | + except subprocess.CalledProcessError: |
3886 | + logging.info('adb shell failed, retrying') |
3887 | + raise RetryException('unity8 not running on device') |
3888 | + |
3889 | + |
3890 | +def main(): |
3891 | + for i in range(FULL_RETRIES): |
3892 | + try: |
3893 | + _kill() |
3894 | + _launch() |
3895 | + _adb_wait(ADB_RETRIES, ADB_WAIT) |
3896 | + logging.info('emulator is running, waiting on unity8') |
3897 | + if EMULATOR_ARCH == 'arm': |
3898 | + logging.info('sleeping for 160s to wait for ARM emulator') |
3899 | + time.sleep(160) |
3900 | + _unity_wait(UNITY_RETRIES, UNITY_WAIT) |
3901 | + logging.info('emulator is booted and ready') |
3902 | + return 0 |
3903 | + except RetryException as e: |
3904 | + logging.warn('emulator failed to boot: %s', e.message) |
3905 | + logging.warn('kill and retry %d more times', FULL_RETRIES - i) |
3906 | + next |
3907 | + logging.error('emulator failed to boot') |
3908 | + _kill() |
3909 | + return 1 |
3910 | + |
3911 | +if __name__ == '__main__': |
3912 | + handler = logging.StreamHandler(stream=sys.stderr) |
3913 | + formatter = logging.Formatter( |
3914 | + '%(asctime)s %(levelname)s: %(message)s', datefmt='%H:%M:%S') |
3915 | + handler.setFormatter(formatter) |
3916 | + l = logging.getLogger('') |
3917 | + l.addHandler(handler) |
3918 | + l.setLevel(logging.INFO) |
3919 | + exit(main()) |
3920 | |
3921 | === added file 'scripts/run-mp.sh' |
3922 | --- scripts/run-mp.sh 1970-01-01 00:00:00 +0000 |
3923 | +++ scripts/run-mp.sh 2015-06-03 20:37:20 +0000 |
3924 | @@ -0,0 +1,44 @@ |
3925 | +#!/bin/bash |
3926 | +set -ex |
3927 | + |
3928 | +# These are all set via jenkins when running in that context |
3929 | +if [ -z "${ANDROID_SERIAL}" ] || [ -z "${package_archive}" ] || \ |
3930 | + [ -z "${test_packages}" ] || [ -z "${test_suite}" ]; then |
3931 | + echo "Missing an env variable: " |
3932 | + echo " ANDROID_SERIAL, package_archive, test_packages or test_suite" |
3933 | + exit 1 |
3934 | +fi |
3935 | + |
3936 | +BASEDIR=$(dirname $(readlink -f $0))/.. |
3937 | +ARCHIVE_TMP=$(mktemp -d) |
3938 | +trap 'rm -rf "${ARCHIVE_TMP}"' EXIT HUP INT TERM |
3939 | + |
3940 | +wget -O "${ARCHIVE_TMP}/archive.zip" ${package_archive} |
3941 | +unzip "${ARCHIVE_TMP}/archive.zip" -d "${ARCHIVE_TMP}" |
3942 | +package_dir="${ARCHIVE_TMP}/archive" |
3943 | + |
3944 | +# This is the list of packages to be installed from the archive |
3945 | +# It's manually generated and supplied via the lp:cupstream2distro-config |
3946 | +# configuration files when executed by jenkins. |
3947 | +package_list="" |
3948 | +for package in ${test_packages}; do |
3949 | + package_list="-p ${package} ${package_list}" |
3950 | +done |
3951 | + |
3952 | +# This is a list of test suites to execute. It's normally just one. |
3953 | +suite_list="" |
3954 | +for suite in ${test_suite}; do |
3955 | + suite_list="-a ${suite} ${suite_list}" |
3956 | +done |
3957 | + |
3958 | +# The provision.sh and run-smoke scripts can install extra packages to meet |
3959 | +# the needs of image testing. Since we are installing specific packages from |
3960 | +# a local archive, this needs to be disabled. |
3961 | +export SKIP_CLICK=1 |
3962 | +export SKIP_TESTCONFIG=1 |
3963 | + |
3964 | +# Provision the device and run the test suite. |
3965 | +${BASEDIR}/scripts/provision.sh -s ${ANDROID_SERIAL} \ |
3966 | + -n ${HOME}/.ubuntu-ci/wifi.conf \ |
3967 | + -D ${package_dir} ${package_list} |
3968 | +${BASEDIR}/scripts/run-smoke -s ${ANDROID_SERIAL} -n ${suite_list} |
3969 | |
3970 | === added file 'scripts/run-smoke' |
3971 | --- scripts/run-smoke 1970-01-01 00:00:00 +0000 |
3972 | +++ scripts/run-smoke 2015-06-03 20:37:20 +0000 |
3973 | @@ -0,0 +1,408 @@ |
3974 | +#!/usr/bin/python |
3975 | + |
3976 | +import argparse |
3977 | +import datetime |
3978 | +import logging |
3979 | +import os |
3980 | +import shutil |
3981 | +import subprocess |
3982 | + |
3983 | +import yaml |
3984 | + |
3985 | +from phabletutils.environment import detect_device |
3986 | + |
3987 | +import dashboard |
3988 | +import statsd |
3989 | + |
3990 | +EMULATOR = os.environ.get('USE_EMULATOR') |
3991 | +if EMULATOR: |
3992 | + def fake_detect(serial, device=None): |
3993 | + log.info('faking detect device for emulator') |
3994 | + return 'emulator' |
3995 | + |
3996 | + # detect_device doesn't support the emulator |
3997 | + globals()['detect_device'] = fake_detect |
3998 | + if 'ANDROID_SERIAL' not in os.environ: |
3999 | + # we need something here or "serial required" logic fails |
4000 | + os.environ['ANDROID_SERIAL'] = 'emulator-5554' |
4001 | + |
4002 | +log = logging.getLogger() |
4003 | +script_dir = os.path.dirname(__file__) |
4004 | +res_dir = os.path.join(os.getcwd(), 'clientlogs') |
4005 | + |
4006 | +dashboard_api = dashboard.API() |
4007 | + |
4008 | + |
4009 | +class SerialAction(argparse.Action): |
4010 | + def __call__(self, parser, namespace, values, option_string=None): |
4011 | + log.info('android serial: %s', values[0]) |
4012 | + os.environ['ANDROID_SERIAL'] = values[0] |
4013 | + |
4014 | + |
4015 | +class DebugAction(argparse.Action): |
4016 | + def __call__(self, parser, namespace, values, option_string=None): |
4017 | + log.setLevel(level=logging.DEBUG) |
4018 | + log.debug('debug logging enabled') |
4019 | + |
4020 | + |
4021 | +def _serial_required(): |
4022 | + required = 'ANDROID_SERIAL' not in os.environ |
4023 | + if required: |
4024 | + try: |
4025 | + out = subprocess.check_output(['adb', 'devices']) |
4026 | + required = (len(out.decode().split('\n')) != 4) |
4027 | + except subprocess.CalledProcessError as e: |
4028 | + logging.debug('error getting adb devices: %s', e) |
4029 | + return required |
4030 | + |
4031 | + |
4032 | +def _get_parser(): |
4033 | + parser = argparse.ArgumentParser( |
4034 | + description='Run the complete test-execution-service suite.') |
4035 | + |
4036 | + parser.add_argument('-s', '--serial', action=SerialAction, nargs=1, |
4037 | + required=_serial_required(), |
4038 | + help='Android serial if more than one device present') |
4039 | + parser.add_argument('--debug', action=DebugAction, nargs=0, |
4040 | + help='''Enable debug logging.''') |
4041 | + |
4042 | + parser.add_argument('--install-url', |
4043 | + help='''Flash with image from previous jenkins job. |
4044 | + This option will check if the device already has image |
4045 | + noted from this URL and will skip provisioning. The URL |
4046 | + should be the path the job like: |
4047 | + http://q-jenkins:8080/job/<your job>/<build number>''') |
4048 | + parser.add_argument('-p', '--package', action='append', |
4049 | + help='Additional packages to install on target.') |
4050 | + parser.add_argument('-P', '--ppa', action='append', |
4051 | + help='Additional PPA to configure on target.') |
4052 | + parser.add_argument('-a', '--app', action='append', |
4053 | + help='Autopilot tests tor run.') |
4054 | + parser.add_argument('-t', '--test', action='append', |
4055 | + help='UTAH tests tor run.') |
4056 | + parser.add_argument('-r', '--revision', help='Image revision to install.') |
4057 | + parser.add_argument('-n', '--no-provision', action='store_true', |
4058 | + help='Skip provisioning of the target device') |
4059 | + parser.add_argument('--hooks-dir', |
4060 | + help='''A directory containing scripts to be run after |
4061 | + the target has been provisioned and before testing.''') |
4062 | + parser.add_argument('--image-server', |
4063 | + help='Server to pass to phablet-flash') |
4064 | + parser.add_argument('--image-opt', |
4065 | + help='Options to pass to phablet-flash') |
4066 | + parser.add_argument('--image-type', default='touch', |
4067 | + help='''Image type being tested. This can be changed |
4068 | + to 'touch_sf4p' so that SurfaceFlinger will be used |
4069 | + instead of Mir. default=%(default)s''') |
4070 | + parser.add_argument('--num-workers', type=int, default=1, |
4071 | + help='''The total number of workers available for |
4072 | + running tests.''') |
4073 | + parser.add_argument('--worker-idx', type=int, default=0, |
4074 | + help='The worker to allocate testing work to.') |
4075 | + return parser |
4076 | + |
4077 | + |
4078 | +def _arg_from_env(args, attr, envkey, array): |
4079 | + val = os.environ.get(envkey, False) |
4080 | + if val: |
4081 | + if array: |
4082 | + setattr(args, attr, val.split()) |
4083 | + else: |
4084 | + setattr(args, attr, val) |
4085 | + del os.environ[envkey] |
4086 | + |
4087 | + |
4088 | +def _merge_env(args): |
4089 | + '''When run in Jenkins everything comes as environment variables. |
4090 | + |
4091 | + Its makes a much simpler job this way. While command line args are |
4092 | + much easier for a user. |
4093 | + ''' |
4094 | + _arg_from_env(args, 'app', 'APPS', True) |
4095 | + _arg_from_env(args, 'test', 'TESTS', True) |
4096 | + _arg_from_env(args, 'package', 'PACKAGES', True) |
4097 | + _arg_from_env(args, 'ppa', 'PPAS', True) |
4098 | + _arg_from_env(args, 'image_opt', 'IMAGE_OPT', False) |
4099 | + _arg_from_env(args, 'image_server', 'IMAGE_SERVER', False) |
4100 | + _arg_from_env(args, 'image_type', 'IMAGE_TYPE', False) |
4101 | + _arg_from_env(args, 'install_url', 'INSTALL_URL', False) |
4102 | + _arg_from_env(args, 'revision', 'REVISION', False) |
4103 | + _arg_from_env(args, 'num_workers', 'workers', False) |
4104 | + _arg_from_env(args, 'worker_idx', 'worker_idx', False) |
4105 | + |
4106 | + |
4107 | +def _assert_args(args): |
4108 | + if args.install_url: |
4109 | + # this means you shouldn't specify packages, ppas, image server, |
4110 | + # or image options |
4111 | + if args.package or args.ppa or args.image_opt or args.image_server: |
4112 | + msg = 'ERROR: --install-url can\'t be used with ' \ |
4113 | + '--package, -ppa, --image_server, or --image_opt' |
4114 | + print(msg) |
4115 | + return False |
4116 | + |
4117 | + # don't bother the install_url check, a user might be copy/pasting and |
4118 | + # doesn't hurt. Its just good to not encourage it. |
4119 | + _merge_env(args) |
4120 | + |
4121 | + script = os.path.join(script_dir, '../jenkins/testconfig.py') |
4122 | + if args.package and args.package[0] == 'ALL': |
4123 | + logging.info('Discovering all required dependencies') |
4124 | + out = subprocess.check_output( |
4125 | + [script, 'packages', '-i', args.image_type]) |
4126 | + args.package = [x for x in out.decode().split()] |
4127 | + |
4128 | + if args.app and args.app[0] == 'ALL': |
4129 | + logging.info('Discovering all autopilot tests') |
4130 | + device_type = os.environ.get('DEVICE_TYPE') |
4131 | + if device_type is None: |
4132 | + device_type = detect_device(None) |
4133 | + out = subprocess.check_output( |
4134 | + [script, 'apps', '-i', args.image_type, '-d', device_type, |
4135 | + '-t', str(args.num_workers), '-w', str(args.worker_idx)]) |
4136 | + args.app = [x for x in out.decode().split()] |
4137 | + logging.info('Autopilot test list: {}'.format(' '.join(args.app))) |
4138 | + |
4139 | + if args.test and args.test[0].startswith('ALL'): |
4140 | + logging.info('Discovering all UTAH tests') |
4141 | + device_type = os.environ.get('DEVICE_TYPE') |
4142 | + if device_type is None: |
4143 | + device_type = detect_device(None) |
4144 | + argv = [script, 'utah', '-i', args.image_type, '-d', device_type, |
4145 | + '-t', str(args.num_workers), '-w', str(args.worker_idx)] |
4146 | + if args.test[0] == 'ALL_INCLUDING_AUTOPILOT': |
4147 | + argv.append('-a') |
4148 | + out = subprocess.check_output(argv) |
4149 | + args.test = [x for x in out.decode().split()] |
4150 | + logging.info('Utah test list: {}'.format(' '.join(args.test))) |
4151 | + |
4152 | + logging.debug('ARGS: %r', args) |
4153 | + |
4154 | + statsd.gauge_it('PACKAGES', args.package) |
4155 | + statsd.gauge_it('APPS', args.app) |
4156 | + statsd.gauge_it('TESTS', args.test) |
4157 | + |
4158 | + return True |
4159 | + |
4160 | + |
4161 | +def _run(args, ignore_error=False): |
4162 | + try: |
4163 | + logging.info('Running: %s', ' '.join(args)) |
4164 | + subprocess.check_call(args) |
4165 | + except subprocess.CalledProcessError: |
4166 | + if ignore_error: |
4167 | + logging.error('failed to run %r, continuing', args) |
4168 | + else: |
4169 | + exit(1) |
4170 | + |
4171 | + |
4172 | +def _image_info(): |
4173 | + info = subprocess.check_output(['adb', 'shell', 'sudo', |
4174 | + 'system-image-cli', '-i']) |
4175 | + v_ver = u_ver = d_ver = channel = None |
4176 | + for line in info.split('\n'): |
4177 | + if not line.strip(): |
4178 | + continue |
4179 | + key, val = line.split(':', 1) |
4180 | + if key == 'version version': |
4181 | + v_ver = val.strip() |
4182 | + elif key == 'version ubuntu': |
4183 | + u_ver = val.strip() |
4184 | + elif key == 'version device': |
4185 | + d_ver = val.strip() |
4186 | + elif key == 'channel': |
4187 | + channel = val.strip() |
4188 | + ver = '%s:%s:%s' % (v_ver, u_ver, d_ver) |
4189 | + # required for the jenkins job's build description |
4190 | + print('= TOUCH IMAGE VERSION:' + ver) |
4191 | + return ver, channel |
4192 | + |
4193 | + |
4194 | +def _assert_image(args): |
4195 | + log.info('checking if device has proper image ...') |
4196 | + os.environ['INSTALL_URL'] = args.install_url |
4197 | + _run([os.path.join(script_dir, 'assert-image')]) |
4198 | + |
4199 | + |
4200 | +def _write_utah(start, end, passed): |
4201 | + passes = failures = rc = 0 |
4202 | + if passed: |
4203 | + passes = 1 |
4204 | + else: |
4205 | + rc = failures = 1 |
4206 | + |
4207 | + delta = '%s' % (end - start) |
4208 | + start = start.strftime('%Y-%m-%d %H:%M:%S') |
4209 | + data = { |
4210 | + 'name': 'install-and-boot', |
4211 | + 'errors': 0, |
4212 | + 'failures': failures, |
4213 | + 'passes': passes, |
4214 | + 'fetch_errors': 0, |
4215 | + 'uname': 'n/a', |
4216 | + 'media-info': 'n/a', |
4217 | + 'install_type': 'n/a', |
4218 | + 'arch': 'n/a', |
4219 | + 'release': 'n/a', |
4220 | + 'build_number': 'n/a', |
4221 | + 'runlist': 'n/a', |
4222 | + 'ran_at': start, |
4223 | + 'commands': [{ |
4224 | + 'cmd_type': 'testcase_test', |
4225 | + 'command': 'provision', |
4226 | + 'returncode': rc, |
4227 | + 'start_time': start, |
4228 | + 'time_delta': delta, |
4229 | + 'stderr': '', |
4230 | + 'stdout': '', |
4231 | + 'testcase': 'boot', |
4232 | + 'testsuite': 'install-and-boot', |
4233 | + }] |
4234 | + } |
4235 | + path = os.path.join(res_dir, 'install-and-boot') |
4236 | + if not os.path.exists(path): |
4237 | + os.mkdir(path) |
4238 | + with open(os.path.join(path, 'utah.yaml'), 'w') as f: |
4239 | + f.write(yaml.safe_dump(data, default_flow_style=False)) |
4240 | + |
4241 | + |
4242 | +def _post_install_hooks(args): |
4243 | + if not args.hooks_dir: |
4244 | + return |
4245 | + log.info('running post install hooks ...') |
4246 | + if not os.path.isdir(args.hooks_dir): |
4247 | + log.warn('hooks directory (%s) does not exist ... skipping', |
4248 | + args.hooks_dir) |
4249 | + for hook in sorted(os.listdir(args.hooks_dir)): |
4250 | + s = os.stat(os.path.join(args.hooks_dir, hook)) |
4251 | + if s.st_mode & os.path.stat.S_IXUSR == 0: |
4252 | + log.warn('skipping hook (%s) - not executable', hook) |
4253 | + continue |
4254 | + log.info('executing hook: %s', hook) |
4255 | + hook = os.path.join(args.hooks_dir, hook) |
4256 | + subprocess.check_call([hook]) |
4257 | + |
4258 | + |
4259 | +def _provision(args): |
4260 | + log.info('provisioning device ...') |
4261 | + if args.image_opt: |
4262 | + log.debug('overriding IMAGE_OPT with: %s', args.image_opt) |
4263 | + os.environ['IMAGE_OPT'] = args.image_opt |
4264 | + if args.image_server: |
4265 | + log.debug('overriding IMAGE_SERVER with: %s', args.image_server) |
4266 | + os.environ['IMAGE_SERVER'] = args.image_server |
4267 | + |
4268 | + cargs = [os.path.join(script_dir, 'provision.sh'), '-i', args.image_type] |
4269 | + |
4270 | + if args.package: |
4271 | + for p in args.package: |
4272 | + cargs.extend(['-p', p]) |
4273 | + if args.ppa: |
4274 | + for p in args.ppa: |
4275 | + cargs.extend(['-P', p]) |
4276 | + if not args.ppa and not args.package: |
4277 | + # All tests require a writeable system the -p and -P args |
4278 | + # implicitly create a writable system. so we have to ensure here: |
4279 | + cargs.append('-w') |
4280 | + if args.revision: |
4281 | + cargs.extend(['-r', args.revision]) |
4282 | + |
4283 | + with statsd.time_it('provision'): |
4284 | + start = datetime.datetime.utcnow() |
4285 | + passed = False |
4286 | + try: |
4287 | + _run(cargs) |
4288 | + _post_install_hooks(args) |
4289 | + passed = True |
4290 | + finally: |
4291 | + end = datetime.datetime.utcnow() |
4292 | + _write_utah(start, end, passed) |
4293 | + |
4294 | + |
4295 | +def _test_autopilot(args, build, image): |
4296 | + if args.app: |
4297 | + if build: |
4298 | + os.environ['DASHBOARD_BUILD'] = build |
4299 | + if image: |
4300 | + os.environ['DASHBOARD_IMAGE'] = image |
4301 | + cargs = [os.path.join(script_dir, 'run-autopilot-tests.sh')] |
4302 | + for app in args.app: |
4303 | + cargs.extend(['-a', app]) |
4304 | + with statsd.time_it('APPS'): |
4305 | + _run(cargs) |
4306 | + |
4307 | + |
4308 | +def _sync_results(build, image, test, fname): |
4309 | + with open(fname) as f: |
4310 | + d = yaml.safe_load(f) |
4311 | + dashboard_api.result_syncing(image, build, test, d) |
4312 | + |
4313 | + |
4314 | +def _test_utah(args, build, image): |
4315 | + if args.test: |
4316 | + cargs = [os.path.join(script_dir, 'jenkins.sh')] |
4317 | + with statsd.time_it('TESTS'): |
4318 | + for test in args.test: |
4319 | + os.environ['RESDIR'] = os.path.join(res_dir, test) |
4320 | + dashboard_api.result_running(image, build, test) |
4321 | + _run(cargs + ['-a', test, '-p', '/tmp/results'], |
4322 | + ignore_error=True) |
4323 | + fname = os.path.join(res_dir, test, 'utah.yaml') |
4324 | + _sync_results(build, image, test, fname) |
4325 | + |
4326 | + |
4327 | +def _image_add(args): |
4328 | + build_number, channel = _image_info() |
4329 | + # get the release series (ex. trusty, utopic) |
4330 | + # this is set in the job environment variable IMAGE_SERIES |
4331 | + release = os.environ.get('IMAGE_SERIES') |
4332 | + if release: |
4333 | + return dashboard_api.image_add(build_number, release, args.image_type, |
4334 | + detect_device(None), 'ubuntu') |
4335 | + |
4336 | + |
4337 | +def main(args): |
4338 | + with statsd.time_it('main'): |
4339 | + if os.path.exists(res_dir): |
4340 | + logging.info('deleting old result directory: %s', res_dir) |
4341 | + shutil.rmtree(res_dir) |
4342 | + os.mkdir(res_dir) |
4343 | + |
4344 | + job_name = os.environ.get('JOB_NAME', '') |
4345 | + job_number = os.environ.get('BUILD_NUMBER', '') |
4346 | + build = dashboard_api.build_add(job_name, job_number) |
4347 | + |
4348 | + if args.no_provision: |
4349 | + logging.info('Skipping the provisioning step as requested') |
4350 | + elif args.install_url: |
4351 | + _assert_image(args) |
4352 | + else: |
4353 | + _provision(args) |
4354 | + |
4355 | + # TODO - this should be incororated into provision and assert_image |
4356 | + # so that the status is updated *before* flashing rather than after |
4357 | + image = _image_add(args) |
4358 | + |
4359 | + if args.test: |
4360 | + for x in args.test: |
4361 | + dashboard_api.result_queue(image, build, x) |
4362 | + if args.app: |
4363 | + for x in args.app: |
4364 | + dashboard_api.result_queue(image, build, x) |
4365 | + |
4366 | + _test_utah(args, build, image) |
4367 | + _test_autopilot(args, build, image) |
4368 | + |
4369 | + return 0 |
4370 | + |
4371 | + |
4372 | +if __name__ == '__main__': |
4373 | + logging.basicConfig(level=logging.INFO) |
4374 | + log.name = 'run-smoke' |
4375 | + dashboard.log = logging.getLogger('dashboard') |
4376 | + |
4377 | + args = _get_parser().parse_args() |
4378 | + if not _assert_args(args): |
4379 | + exit(1) |
4380 | + |
4381 | + exit(main(args)) |
4382 | |
4383 | === added file 'scripts/run-touch-upgrade.sh' |
4384 | --- scripts/run-touch-upgrade.sh 1970-01-01 00:00:00 +0000 |
4385 | +++ scripts/run-touch-upgrade.sh 2015-06-03 20:37:20 +0000 |
4386 | @@ -0,0 +1,46 @@ |
4387 | +#!/bin/bash |
4388 | + |
4389 | +## This is the script jenkins should run to test upgrading a system image |
4390 | +## in the lab. |
4391 | +## Intersting environment variables that must be set: |
4392 | +## ANDROID_SERIAL - specify another android device |
4393 | +## RUNLIST - the path the runlist |
4394 | +## NETWORK_FILE - specify an alternative network file (passed to runlist) |
4395 | +## UPGRADE_FROM - the revision to upgrade from, eg -1 (passed to runlist) |
4396 | + |
4397 | +set -eux |
4398 | + |
4399 | +BASEDIR=$(dirname $(readlink -f $0)) |
4400 | + |
4401 | +RESDIR=`pwd`/clientlogs |
4402 | + |
4403 | +UTAH_PHABLET_CMD="${UTAH_PHABLET_CMD-/usr/share/utah/examples/run_utah_phablet.py}" |
4404 | +RUNLIST=${RUNLIST-"`pwd`/smoke-touch-apps/upgrade/master.run"} |
4405 | +ANDROID_SERIAL="${ANDROID_SERIAL-015d1884b20c1c0f}" #doanac's nexus7 at home |
4406 | +NETWORK_FILE="${NETWORK_FILE-/home/ubuntu/magners-wifi}" |
4407 | + |
4408 | +rm -rf clientlogs |
4409 | +mkdir clientlogs |
4410 | + |
4411 | +export ANDROID_SERIAL=$ANDROID_SERIAL |
4412 | + |
4413 | +set +e |
4414 | +sudo NETWORK_FILE=$NETWORK_FILE \ |
4415 | + $UTAH_PHABLET_CMD -s $ANDROID_SERIAL \ |
4416 | + --from-host --skip-install --skip-utah --skip-network -l $RUNLIST \ |
4417 | + --results-dir=$RESDIR |
4418 | +EXITCODE=$? |
4419 | + |
4420 | +UTAHFILE=$RESDIR/utah.yaml |
4421 | +if ! `grep "^errors: [!0]" < $UTAHFILE >/dev/null` ; then |
4422 | + echo "errors found" |
4423 | + EXITCODE=1 |
4424 | +fi |
4425 | +if ! `grep "^failures: [!0]" < $UTAHFILE >/dev/null` ; then |
4426 | + echo "failures found" |
4427 | + EXITCODE=2 |
4428 | +fi |
4429 | +echo "Results Summary" |
4430 | +echo "---------------" |
4431 | +egrep '^(errors|failures|passes|fetch_errors):' $UTAHFILE |
4432 | +exit $EXITCODE |
4433 | |
4434 | === added file 'scripts/statsd.py' |
4435 | --- scripts/statsd.py 1970-01-01 00:00:00 +0000 |
4436 | +++ scripts/statsd.py 2015-06-03 20:37:20 +0000 |
4437 | @@ -0,0 +1,35 @@ |
4438 | +#!/usr/bin/python |
4439 | + |
4440 | +import os |
4441 | +import contextlib |
4442 | +import time |
4443 | + |
4444 | + |
4445 | +_namespace = os.environ.get('STATSD_KEY') |
4446 | +if _namespace: |
4447 | + from txstatsd.client import UdpStatsDClient |
4448 | + from txstatsd.metrics.timermetric import TimerMetric |
4449 | + from txstatsd.metrics.gaugemetric import GaugeMetric |
4450 | + _host = os.environ.get('SERVER', 'snakefruit.canonical.com') |
4451 | + _port = int(os.environ.get('PORT', '10041')) |
4452 | + _client = UdpStatsDClient(_host, _port) |
4453 | + _client.connect() |
4454 | + |
4455 | + |
4456 | +@contextlib.contextmanager |
4457 | +def time_it(key): |
4458 | + start = time.time() |
4459 | + try: |
4460 | + yield |
4461 | + finally: |
4462 | + if _namespace: |
4463 | + m = TimerMetric(_client, _namespace + '.' + key) |
4464 | + m.mark(time.time() - start) |
4465 | + |
4466 | + |
4467 | +def gauge_it(key, array): |
4468 | + val = 0 |
4469 | + if array: |
4470 | + val = len(array) |
4471 | + if _namespace: |
4472 | + GaugeMetric(_client, _namespace + '.' + key).mark(val) |
4473 | |
4474 | === added directory 'selftests' |
4475 | === added file 'selftests/test_junit2utah.py' |
4476 | --- selftests/test_junit2utah.py 1970-01-01 00:00:00 +0000 |
4477 | +++ selftests/test_junit2utah.py 2015-06-03 20:37:20 +0000 |
4478 | @@ -0,0 +1,49 @@ |
4479 | +import StringIO |
4480 | +import os |
4481 | +import sys |
4482 | +import unittest |
4483 | + |
4484 | +here = os.path.dirname(__file__) |
4485 | +sys.path.append(os.path.join(here, '../scripts')) |
4486 | + |
4487 | +import junit2utah |
4488 | + |
4489 | +RESULT = ''' |
4490 | +<testsuite errors="0" failures="1" name="" tests="4" time="0.001"> |
4491 | +<testcase classname="classname" name="testFails" time="0.000"> |
4492 | +<failure type="testtools.testresult.real._StringException"> |
4493 | +testtools.testresult.real._StringException: Traceback (most recent call last): |
4494 | + File "junit2utah.py", line 14, in testFails |
4495 | + self.assertFalse(True) |
4496 | + File "/usr/lib/python2.7/unittest/case.py", line 418, in assertFalse |
4497 | + raise self.failureException(msg) |
4498 | +AssertionError: True is not false |
4499 | +</failure> |
4500 | +</testcase> |
4501 | +<testcase classname="classname" name="testPasses" time="0.100"/> |
4502 | +<testcase classname="classname" name="testPasses2" time="0.200"/> |
4503 | +<testcase classname="classname" name="testSkip" time="0.000"> |
4504 | +<skip>ensure skip works</skip> |
4505 | +</testcase> |
4506 | +</testsuite> |
4507 | +''' |
4508 | + |
4509 | + |
4510 | +class TestJunit2Utah(unittest.TestCase): |
4511 | + def testFull(self): |
4512 | + stream = StringIO.StringIO(RESULT) |
4513 | + results = junit2utah._get_results(stream) |
4514 | + self.assertEquals(3, results['passes']) |
4515 | + self.assertEquals(1, results['failures']) |
4516 | + self.assertEquals(0, results['errors']) |
4517 | + |
4518 | + tcs = results['commands'] |
4519 | + self.assertEqual('classname', tcs[0]['testsuite']) |
4520 | + self.assertEqual('testFails', tcs[0]['testcase']) |
4521 | + self.assertIn('AssertionError', tcs[0]['stderr']) |
4522 | + |
4523 | + self.assertEqual('0.200', tcs[2]['time_delta']) |
4524 | + |
4525 | + self.assertEqual('classname', tcs[3]['testsuite']) |
4526 | + self.assertEqual('testSkip', tcs[3]['testcase']) |
4527 | + self.assertEqual('ensure skip works', tcs[3]['stdout']) |
4528 | |
4529 | === added file 'selftests/test_reboot_and_wait.py' |
4530 | --- selftests/test_reboot_and_wait.py 1970-01-01 00:00:00 +0000 |
4531 | +++ selftests/test_reboot_and_wait.py 2015-06-03 20:37:20 +0000 |
4532 | @@ -0,0 +1,66 @@ |
4533 | +# Ubuntu Test Cases for Touch |
4534 | +# Copyright 2013 Canonical Ltd. |
4535 | + |
4536 | +# This program is free software: you can redistribute it and/or modify it |
4537 | +# under the terms of the GNU General Public License version 3, as published |
4538 | +# by the Free Software Foundation. |
4539 | + |
4540 | +# This program is distributed in the hope that it will be useful, but |
4541 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
4542 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
4543 | +# PURPOSE. See the GNU General Public License for more details. |
4544 | + |
4545 | +# You should have received a copy of the GNU General Public License along |
4546 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
4547 | + |
4548 | +from __future__ import print_function |
4549 | + |
4550 | +import imp |
4551 | +import mock |
4552 | +import os |
4553 | +import unittest |
4554 | + |
4555 | + |
4556 | +class TestRebootAndWait(unittest.TestCase): |
4557 | + |
4558 | + """Simple set of tests to make sure the reboot-and-wait command works.""" |
4559 | + |
4560 | + def setUp(self): |
4561 | + # do some trickery to load this as module |
4562 | + module = 'reboot-and-wait' |
4563 | + fname = os.path.join(os.path.dirname(__file__), '../scripts', module) |
4564 | + m = imp.load_source(module, fname) |
4565 | + self._main = m.main |
4566 | + self._get_parser = m._get_arg_parser |
4567 | + |
4568 | + @mock.patch('phabletutils.device.AndroidBridge.reboot') |
4569 | + def testRebootFail(self, reboot): |
4570 | + reboot.side_effect = RuntimeError('foo') |
4571 | + args = self._get_parser().parse_args([]) |
4572 | + with self.assertRaisesRegexp(RuntimeError, 'foo'): |
4573 | + self._main(args) |
4574 | + |
4575 | + @mock.patch('phabletutils.device.AndroidBridge.reboot') |
4576 | + @mock.patch('phabletutils.device.AndroidBridge.wait_for_device') |
4577 | + def testWaitForDeviceFail(self, wait_for, reboot): |
4578 | + wait_for.side_effect = RuntimeError('foo') |
4579 | + args = self._get_parser().parse_args([]) |
4580 | + with self.assertRaisesRegexp(RuntimeError, 'foo'): |
4581 | + self._main(args) |
4582 | + reboot.assert_called_once_with() |
4583 | + |
4584 | + @mock.patch('phabletutils.device.AndroidBridge.reboot') |
4585 | + @mock.patch('phabletutils.device.AndroidBridge.wait_for_device') |
4586 | + @mock.patch('phabletutils.device.AndroidBridge.wait_for_network') |
4587 | + def testRetries(self, wait_for_net, wait_for_dev, reboot): |
4588 | + args = self._get_parser().parse_args([]) |
4589 | + wait_for_net.side_effect = RuntimeError('foo') |
4590 | + self.assertEquals(1, self._main(args)) |
4591 | + self.assertEquals(args.num_tries, reboot.call_count) |
4592 | + |
4593 | + # now make sure it can recover after a single network failure |
4594 | + reboot.reset_mock() |
4595 | + wait_for_net.reset_mock() |
4596 | + wait_for_net.side_effect = [RuntimeError('foo'), None] |
4597 | + self.assertEquals(0, self._main(args)) |
4598 | + self.assertEquals(2, reboot.call_count) |
4599 | |
4600 | === added file 'selftests/test_run_smoke.py' |
4601 | --- selftests/test_run_smoke.py 1970-01-01 00:00:00 +0000 |
4602 | +++ selftests/test_run_smoke.py 2015-06-03 20:37:20 +0000 |
4603 | @@ -0,0 +1,148 @@ |
4604 | +# Ubuntu Test Cases for Touch |
4605 | +# Copyright 2013 Canonical Ltd. |
4606 | + |
4607 | +# This program is free software: you can redistribute it and/or modify it |
4608 | +# under the terms of the GNU General Public License version 3, as published |
4609 | +# by the Free Software Foundation. |
4610 | + |
4611 | +# This program is distributed in the hope that it will be useful, but |
4612 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
4613 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
4614 | +# PURPOSE. See the GNU General Public License for more details. |
4615 | + |
4616 | +# You should have received a copy of the GNU General Public License along |
4617 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
4618 | + |
4619 | +import imp |
4620 | +import mock |
4621 | +import os |
4622 | +import subprocess |
4623 | +import sys |
4624 | +import unittest |
4625 | + |
4626 | + |
4627 | +class TestRunSmoke(unittest.TestCase): |
4628 | + |
4629 | + """Simple set of tests to make sure the smoke-run command works.""" |
4630 | + |
4631 | + def setUp(self): |
4632 | + # load the module |
4633 | + module = 'run-smoke' |
4634 | + path = os.path.join(os.path.dirname(__file__), '../scripts') |
4635 | + sys.path.append(path) |
4636 | + fname = os.path.join(path, module) |
4637 | + self.run_smoke = imp.load_source(module, fname) |
4638 | + if 'ANDROID_SERIAL' in os.environ: |
4639 | + del os.environ['ANDROID_SERIAL'] |
4640 | + |
4641 | + @mock.patch.dict('os.environ') |
4642 | + @mock.patch('subprocess.check_output') |
4643 | + def testSerialRequired(self, check_output): |
4644 | + '''Ensure android serial is required when appropriate''' |
4645 | + check_output.return_value = '1'.encode() |
4646 | + self.assertTrue(self.run_smoke._serial_required()) |
4647 | + |
4648 | + check_output.return_value = '1\n2\n3\n4'.encode() |
4649 | + self.assertFalse(self.run_smoke._serial_required()) |
4650 | + |
4651 | + check_output.return_value = '1\n2\n3\n4\n5'.encode() |
4652 | + self.assertTrue(self.run_smoke._serial_required()) |
4653 | + |
4654 | + # make sure serial isn't required if specified in env |
4655 | + os.environ['ANDROID_SERIAL'] = 'foo' |
4656 | + check_output.return_value = '1\n2\n3\n4\n5'.encode() |
4657 | + self.assertFalse(self.run_smoke._serial_required()) |
4658 | + |
4659 | + @mock.patch.dict('os.environ') |
4660 | + @mock.patch('statsd.gauge_it') |
4661 | + def testAssertArgs(self, gauge): |
4662 | + '''Ensure install-url is used properly''' |
4663 | + patterns = [ |
4664 | + (['--install-url', 'x', '-p', 'x'], False), |
4665 | + (['--install-url', 'x', '-P', 'x'], False), |
4666 | + (['--install-url', 'x', '--image-opt', 'x'], False), |
4667 | + (['-p', 'x', '-P', 'x', '--image-opt', 'x'], True), |
4668 | + ] |
4669 | + # Fake the device type |
4670 | + os.environ['DEVICE_TYPE'] = 'mako' |
4671 | + for pat, val in patterns: |
4672 | + args = self.run_smoke._get_parser().parse_args(['-s', 'foo'] + pat) |
4673 | + self.assertEqual(val, self.run_smoke._assert_args(args)) |
4674 | + |
4675 | + # ensure the -p ALL pulls in all packages |
4676 | + gauge.reset_mock() |
4677 | + args = self.run_smoke._get_parser().parse_args(['-p', 'ALL']) |
4678 | + self.assertTrue(self.run_smoke._assert_args(args)) |
4679 | + self.assertTrue(len(args.package) > 1) |
4680 | + args = gauge.call_args_list[0][0] |
4681 | + self.assertEqual('PACKAGES', args[0]) |
4682 | + self.assertLess(1, len(args[1])) |
4683 | + |
4684 | + args = gauge.call_args_list[1][0] |
4685 | + self.assertEqual('APPS', args[0]) |
4686 | + self.assertIsNone(args[1]) |
4687 | + args = gauge.call_args_list[2][0] |
4688 | + self.assertEqual('TESTS', args[0]) |
4689 | + self.assertIsNone(args[1]) |
4690 | + |
4691 | + # don't bother checking gauge calls for the remaining "ALL" tests |
4692 | + |
4693 | + # ensure the -a ALL pulls in all APPS |
4694 | + args = self.run_smoke._get_parser().parse_args(['-a', 'ALL']) |
4695 | + self.assertTrue(self.run_smoke._assert_args(args)) |
4696 | + self.assertTrue(len(args.app) > 1) |
4697 | + |
4698 | + # ensure the -t ALL pulls in all TESTS |
4699 | + args = self.run_smoke._get_parser().parse_args(['-t', 'ALL']) |
4700 | + self.assertTrue(self.run_smoke._assert_args(args)) |
4701 | + self.assertTrue(len(args.test) > 1) |
4702 | + |
4703 | + def testAssertArgsEnv(self): |
4704 | + '''Ensure we pull in environment variables that jenkins uses.''' |
4705 | + with mock.patch.dict('os.environ'): |
4706 | + os.environ['APPS'] = 'apps' |
4707 | + os.environ['TESTS'] = 'tests' |
4708 | + os.environ['PACKAGES'] = 'packages' |
4709 | + os.environ['PPAS'] = 'ppas' |
4710 | + os.environ['IMAGE_TYPE'] = 'type' |
4711 | + os.environ['INSTALL_URL'] = 'url' |
4712 | + os.environ['IMAGE_OPT'] = 'opts opts' |
4713 | + os.environ['ANDROID_SERIAL'] = 'foo' |
4714 | + |
4715 | + args = self.run_smoke._get_parser().parse_args([]) |
4716 | + self.assertTrue(self.run_smoke._assert_args(args)) |
4717 | + |
4718 | + self.assertEqual(args.app, ['apps']) |
4719 | + self.assertEqual(args.test, ['tests']) |
4720 | + self.assertEqual(args.package, ['packages']) |
4721 | + self.assertEqual(args.ppa, ['ppas']) |
4722 | + self.assertEqual(args.image_type, 'type') |
4723 | + self.assertEqual(args.install_url, 'url') |
4724 | + self.assertEqual(args.image_opt, 'opts opts') |
4725 | + |
4726 | + @mock.patch('__builtin__.open') |
4727 | + @mock.patch('os.mkdir') |
4728 | + def testProvision(self, a, b): |
4729 | + orig = os.environ.get('IMAGE_OPT', '') |
4730 | + with mock.patch.object(self.run_smoke, '_run') as run: |
4731 | + args = self.run_smoke._get_parser().parse_args( |
4732 | + ['-s', 'foo', '--image-opt', 'FOOBAR', '-p', '1', '-p', '2']) |
4733 | + self.run_smoke._provision(args) |
4734 | + self.assertTrue(run.called) |
4735 | + val = os.environ.get('IMAGE_OPT') |
4736 | + os.environ['IMAGE_OPT'] = orig |
4737 | + self.assertEqual('FOOBAR', val) |
4738 | + |
4739 | + def testUtahTests(self): |
4740 | + args = self.run_smoke._get_parser().parse_args( |
4741 | + ['-s', 'foo', '-t', 'a', '-t', 'b']) |
4742 | + with mock.patch.object(self.run_smoke, '_run') as run: |
4743 | + with mock.patch.dict('os.environ'): |
4744 | + run.side_effects = [subprocess.CalledProcessError, None] |
4745 | + with mock.patch.object(self.run_smoke, '_sync_results') as sr: |
4746 | + self.run_smoke._test_utah(args, None, None) |
4747 | + p = os.path.join(self.run_smoke.res_dir, 'b') |
4748 | + # ensuring b ran means, that we handled the failure of test |
4749 | + # 'a' and that the environment is setup correctly |
4750 | + self.assertEqual(os.environ['RESDIR'], p) |
4751 | + self.assertTrue(sr.called) |
4752 | |
4753 | === added file 'selftests/test_statsd.py' |
4754 | --- selftests/test_statsd.py 1970-01-01 00:00:00 +0000 |
4755 | +++ selftests/test_statsd.py 2015-06-03 20:37:20 +0000 |
4756 | @@ -0,0 +1,64 @@ |
4757 | +# Ubuntu Test Cases for Touch |
4758 | +# Copyright 2013 Canonical Ltd. |
4759 | + |
4760 | +# This program is free software: you can redistribute it and/or modify it |
4761 | +# under the terms of the GNU General Public License version 3, as published |
4762 | +# by the Free Software Foundation. |
4763 | + |
4764 | +# This program is distributed in the hope that it will be useful, but |
4765 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
4766 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
4767 | +# PURPOSE. See the GNU General Public License for more details. |
4768 | + |
4769 | +# You should have received a copy of the GNU General Public License along |
4770 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
4771 | + |
4772 | +import os |
4773 | +import mock |
4774 | +import unittest |
4775 | +import sys |
4776 | +import time |
4777 | + |
4778 | +path = os.path.join(os.path.dirname(__file__), '../scripts') |
4779 | +sys.path.append(path) |
4780 | + |
4781 | +import statsd |
4782 | + |
4783 | + |
4784 | +class TestStatsd(unittest.TestCase): |
4785 | + |
4786 | + def setUp(self): |
4787 | + self.origkey = os.environ.get('STATSD_KEY') |
4788 | + os.environ['STATSD_KEY'] = 'prefix' |
4789 | + reload(statsd) |
4790 | + |
4791 | + def tearDown(self): |
4792 | + if not self.origkey: |
4793 | + del os.environ['STATSD_KEY'] |
4794 | + else: |
4795 | + os.environ['STATSD_KEY'] = self.origkey |
4796 | + reload(statsd) |
4797 | + |
4798 | + """Simple set of tests to make sure the statsd calls work.""" |
4799 | + |
4800 | + @mock.patch('txstatsd.metrics.metric.Metric.write') |
4801 | + def testGaugeIt(self, _statsd): |
4802 | + statsd.gauge_it('foo', None) |
4803 | + _statsd.assert_called_with('prefix.foo:0|g') |
4804 | + _statsd.reset_mock() |
4805 | + |
4806 | + statsd.gauge_it('foo', []) |
4807 | + _statsd.assert_called_with('prefix.foo:0|g') |
4808 | + _statsd.reset_mock() |
4809 | + |
4810 | + statsd.gauge_it('foo', [1, 2, 3]) |
4811 | + _statsd.assert_called_with('prefix.foo:3|g') |
4812 | + _statsd.reset_mock() |
4813 | + |
4814 | + @mock.patch('txstatsd.metrics.metric.Metric.write') |
4815 | + def testTimeIt(self, _statsd): |
4816 | + with statsd.time_it('foo'): |
4817 | + time.sleep(0.1) |
4818 | + # should have a timing of about 100ms |
4819 | + self.assertRegexpMatches( |
4820 | + _statsd.call_args[0][0], 'prefix.foo:100.[\d+]|ms') |
4821 | |
4822 | === added directory 'tests' |
4823 | === added directory 'tests/bootfail' |
4824 | === added file 'tests/bootfail/README' |
4825 | --- tests/bootfail/README 1970-01-01 00:00:00 +0000 |
4826 | +++ tests/bootfail/README 2015-06-03 20:37:20 +0000 |
4827 | @@ -0,0 +1,15 @@ |
4828 | +!!CAUTION!! |
4829 | +Do not execute this test on a device without manual control. |
4830 | +The test will render the device unbootable and disable adb. |
4831 | +Manual control is required to return the device to fastboot. |
4832 | + |
4833 | +To run the tests: |
4834 | + |
4835 | +$ export ANDROID_SERIAL=[adb device id] |
4836 | +$ phablet-config writable-image |
4837 | + |
4838 | +$ adt-run --no-built-binaries --unbuilt-tree bootfail -o log-dir --- \ |
4839 | + ssh -s /usr/share/autopkgtest/ssh-setup/adb -- -s $ANDROID_SERIAL |
4840 | + |
4841 | +Exit code is '0' if it passed. |
4842 | +Logs are stored under log-dir as specified with the '-o' option. |
4843 | |
4844 | === added directory 'tests/bootfail/debian' |
4845 | === added file 'tests/bootfail/debian/changelog' |
4846 | --- tests/bootfail/debian/changelog 1970-01-01 00:00:00 +0000 |
4847 | +++ tests/bootfail/debian/changelog 2015-06-03 20:37:20 +0000 |
4848 | @@ -0,0 +1,5 @@ |
4849 | +bootfail (0.1) vivid; urgency=medium |
4850 | + |
4851 | + * Initial release. |
4852 | + |
4853 | + -- Francis Ginther <francis.ginther@canonical.com> Mon, 26 Jan 2015 21:34:20 -0600 |
4854 | |
4855 | === added file 'tests/bootfail/debian/compat' |
4856 | --- tests/bootfail/debian/compat 1970-01-01 00:00:00 +0000 |
4857 | +++ tests/bootfail/debian/compat 2015-06-03 20:37:20 +0000 |
4858 | @@ -0,0 +1,1 @@ |
4859 | +9 |
4860 | |
4861 | === added file 'tests/bootfail/debian/control' |
4862 | --- tests/bootfail/debian/control 1970-01-01 00:00:00 +0000 |
4863 | +++ tests/bootfail/debian/control 2015-06-03 20:37:20 +0000 |
4864 | @@ -0,0 +1,10 @@ |
4865 | +Source: bootfail |
4866 | +Section: misc |
4867 | +Maintainer: Canonical CI Engineering <canonical-ci-engineering@lists.launchpad.net> |
4868 | +Build-Depends: debhelper (>= 9), |
4869 | +Standards-Version: 3.9.4 |
4870 | + |
4871 | +Package: bootfail |
4872 | +Architecture: all |
4873 | +Description: A boottest dep8 test package designed to fail |
4874 | + A boottest dep8 test package designed to fail. |
4875 | |
4876 | === added file 'tests/bootfail/debian/rules' |
4877 | --- tests/bootfail/debian/rules 1970-01-01 00:00:00 +0000 |
4878 | +++ tests/bootfail/debian/rules 2015-06-03 20:37:20 +0000 |
4879 | @@ -0,0 +1,4 @@ |
4880 | +#!/usr/bin/make -f |
4881 | + |
4882 | +%: |
4883 | + dh $@ |
4884 | |
4885 | === added directory 'tests/bootfail/debian/tests' |
4886 | === added file 'tests/bootfail/debian/tests/bootfail' |
4887 | --- tests/bootfail/debian/tests/bootfail 1970-01-01 00:00:00 +0000 |
4888 | +++ tests/bootfail/debian/tests/bootfail 2015-06-03 20:37:20 +0000 |
4889 | @@ -0,0 +1,15 @@ |
4890 | +#!/bin/bash |
4891 | + |
4892 | +rc=0 |
4893 | + |
4894 | +if [ "${ADT_REBOOT_MARK}" == "reboot_mark" ]; then |
4895 | + ls > "${ADT_ARTIFACTS}"/ls.txt |
4896 | + rc=$? |
4897 | +else |
4898 | + # CAUITON! The following will render a phone device unbootable |
4899 | + # It also requires manual control to reset the device into fastboot |
4900 | + sudo apt-get remove -y --force-yes lxc |
4901 | + /tmp/autopkgtest-reboot reboot_mark |
4902 | + echo "Are we having fun yet?" |
4903 | +fi |
4904 | +exit $rc |
4905 | |
4906 | === added file 'tests/bootfail/debian/tests/control' |
4907 | --- tests/bootfail/debian/tests/control 1970-01-01 00:00:00 +0000 |
4908 | +++ tests/bootfail/debian/tests/control 2015-06-03 20:37:20 +0000 |
4909 | @@ -0,0 +1,3 @@ |
4910 | +Tests: bootfail |
4911 | +Depends: |
4912 | +Restrictions: needs-root |
4913 | |
4914 | === added directory 'tests/bootspeed' |
4915 | === added directory 'tests/bootspeed/bootchart' |
4916 | === added file 'tests/bootspeed/bootchart/run.py' |
4917 | --- tests/bootspeed/bootchart/run.py 1970-01-01 00:00:00 +0000 |
4918 | +++ tests/bootspeed/bootchart/run.py 2015-06-03 20:37:20 +0000 |
4919 | @@ -0,0 +1,73 @@ |
4920 | +#!/usr/bin/python |
4921 | + |
4922 | +import datetime |
4923 | +import json |
4924 | +import os |
4925 | +import shutil |
4926 | +import subprocess |
4927 | + |
4928 | + |
4929 | +def _get_dashboard_data(timing_file): |
4930 | + info = subprocess.check_output(['adb', 'shell', 'system-image-cli', '-i']) |
4931 | + data = { |
4932 | + 'image_md5': 'n/a', |
4933 | + 'machine_mac': 'ff:ff:ff:ff:ff:ff', |
4934 | + 'ran_at': datetime.datetime.now().isoformat(), |
4935 | + 'kernel_init': 0.0, |
4936 | + } |
4937 | + for line in info.split('\r\n'): |
4938 | + if not line: |
4939 | + continue |
4940 | + key, val = line.split(':', 1) |
4941 | + val = val.strip() |
4942 | + if key == 'device name': |
4943 | + data['image_arch'] = val |
4944 | + elif key == 'channel': |
4945 | + # get 'touch' and 'trusty' from something like: |
4946 | + # ubuntu-touch/trusty-proposed |
4947 | + variant, release = val.split('/') |
4948 | + data['image_variant'] = variant.split('-')[1] |
4949 | + data['image_release'] = release.split('-')[0] |
4950 | + elif key == 'version version': |
4951 | + data['number'] = val |
4952 | + elif key == 'version ubuntu': |
4953 | + data['version_ubuntu'] = val |
4954 | + elif key == 'version device': |
4955 | + data['version_device'] = val |
4956 | + data['build_number'] = '%s:%s:%s' % ( |
4957 | + data['number'], data['version_ubuntu'], data['version_device']) |
4958 | + |
4959 | + with open(timing_file) as f: |
4960 | + # the timings file is sequence of readings (in hundredths of a second): |
4961 | + # line 0 - the total boot time |
4962 | + # line X - the time from boot until the given annotation *started* |
4963 | + timings = [float(x) / 100.0 for x in f.read().split('\n') if x] |
4964 | + data['boot'] = timings[0] |
4965 | + data['kernel'] = timings[1] |
4966 | + data['plumbing'] = timings[2] - timings[1] |
4967 | + data['xorg'] = timings[3] - timings[2] |
4968 | + data['desktop'] = timings[0] - timings[3] |
4969 | + return data |
4970 | + |
4971 | + |
4972 | +def chart(results_dir): |
4973 | + timings = os.path.join(results_dir, 'timings') |
4974 | + os.environ['CHARTOPTS'] = ' '.join([ |
4975 | + '--crop-after=unity8', |
4976 | + '--annotate=mountall', |
4977 | + '--annotate=lightdm', |
4978 | + '--annotate=unity8', |
4979 | + '--annotate-file=%s' % timings, |
4980 | + ]) |
4981 | + subprocess.check_call(['phablet-bootchart', '-n', '-k', |
4982 | + '-w', '/home/ubuntu/magners-wifi', |
4983 | + '-o', results_dir]) |
4984 | + data = _get_dashboard_data(timings) |
4985 | + with open(os.path.join(results_dir, 'boot.json'), 'w') as f: |
4986 | + json.dump(data, f, indent=4) |
4987 | + |
4988 | +if __name__ == '__main__': |
4989 | + # run_utah_phablet will pass us a "UTAH_PROBE_DIR": |
4990 | + resdir = os.environ.get('UTAH_PROBE_DIR', '/tmp/results') |
4991 | + for x in range(3): |
4992 | + chart(os.path.join(resdir, str(x))) |
4993 | |
4994 | === added file 'tests/bootspeed/bootchart/setup.sh' |
4995 | --- tests/bootspeed/bootchart/setup.sh 1970-01-01 00:00:00 +0000 |
4996 | +++ tests/bootspeed/bootchart/setup.sh 2015-06-03 20:37:20 +0000 |
4997 | @@ -0,0 +1,4 @@ |
4998 | +#!/bin/bash |
4999 | + |
5000 | +${TARGET_PREFIX} apt-get install bootchart |