/* * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.android.apps.mytracks.smoketest; import com.google.android.apps.mytracks.TrackListActivity; import com.google.android.apps.mytracks.fragments.MapFragment; import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.maps.MapView; import com.google.android.maps.mytracks.R; import com.jayway.android.robotium.solo.Solo; import android.app.Activity; import android.app.Instrumentation; import android.test.ActivityInstrumentationTestCase2; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.ListView; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; /** * Makes a smoke test of MyTracks. It's a total end-to-end test. * * @author Youtao Liu */ public class MyTracksSmokeTest extends ActivityInstrumentationTestCase2 { private static final String TRACK_NAME_PREFIX = "testTrackName"; private static final String WAYPOINT_NAME = "testWaypoint"; private static final String STATISTICS_NAME = "testStatistics"; private static final String GPX = "gpx"; private static final String KML = "kml"; private static final String CSV = "csv"; private static final String TCX = "tcx"; private static final String MENU_MORE = "More"; private Instrumentation instrumentation; private TrackListActivity activityMyTracks; private Solo solo; private boolean checkedFirstLaunch = false; // Check whether the UI has an action bar which is related with the version of // Android OS. private boolean hasActionBar = false; private String trackName; public MyTracksSmokeTest() { super(TrackListActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); instrumentation = getInstrumentation(); activityMyTracks = getActivity(); solo = new Solo(instrumentation, activityMyTracks); // Checks if open MyTracks first time after install. If so, there would be a // welcome view with accept buttons. And makes sure only check once. if (!checkedFirstLaunch) { if ((getButtonOnScreen(activityMyTracks.getString(R.string.eula_accept)) != null)) { verifyFirstLaunch(); } setHasActionBar(); checkedFirstLaunch = true; } // Makes every track name is unique to make sure every check can be trusted. trackName = TRACK_NAME_PREFIX + System.currentTimeMillis(); } /** * Switches view from {@link MapFragment} to @ ChartFragment} , then changes * to @ StatsFragment} . Finally back to {@link MapFragment}. And check some * menus in these views. */ public void testSwitchViewsAndMenusOfView() { createSimpleTrack(3); solo.goBack(); solo.clickOnText(trackName); solo.clickOnText(activityMyTracks.getString(R.string.track_detail_chart_tab)); rotateAllActivities(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_chart_settings), true); assertTrue(solo.searchText(activityMyTracks.getString(R.string.chart_settings_by_distance))); solo.goBack(); solo.clickOnText(activityMyTracks.getString(R.string.track_detail_stats_tab)); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_satellite_mode), false); findMenuItem(activityMyTracks.getString(R.string.menu_sensor_state), true); assertTrue(solo.searchText(activityMyTracks.getString(R.string.sensor_state_last_sensor_time))); solo.goBack(); solo.clickOnText(activityMyTracks.getString(R.string.track_detail_map_tab)); sendKeys(KeyEvent.KEYCODE_MENU); assertTrue(findMenuItem(activityMyTracks.getString(R.string.menu_my_location), false)); } /** * Creates a track with markers and the setting to send to google. */ public void testCreateAndSendTrack() { // Create a track at first. createSimpleTrack(1); instrumentation.waitForIdleSync(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_send_google), true); solo.waitForText(activityMyTracks.getString(R.string.send_google_title)); rotateCurrentActivity(); ArrayList checkBoxs = solo.getCurrentCheckBoxes(); for (int i = 0; i < checkBoxs.size(); i++) { if (checkBoxs.get(i).isChecked()) { solo.clickOnCheckBox(i); } } instrumentation.waitForIdleSync(); solo.clickOnText(activityMyTracks.getString(R.string.send_google_send_now)); assertTrue(solo.waitForText(activityMyTracks .getString(R.string.send_google_no_service_selected))); // Stop here for do not really send. } /** * Tests saving a track to SD card as a GPX file. */ public void testSaveToSDCard_GPX() { createSimpleTrack(0); saveTrackToSdCard(GPX); } /** * Tests saving a track to SD card as a KML file. */ public void testSaveToSDCard_KML() { createSimpleTrack(0); saveTrackToSdCard(KML); } /** * Tests saving a track to SD card as a CSV file. */ public void testSaveToSDCard_CSV() { createSimpleTrack(0); saveTrackToSdCard(CSV); } /** * Tests saving a track to SD card as a TCX file. */ public void testSaveToSDCard_TCX() { createSimpleTrack(0); saveTrackToSdCard(TCX); } /** * Tests editing a track. */ public void testEditTrack() { createSimpleTrack(0); instrumentation.waitForIdleSync(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_edit), true); String newTrackName = TRACK_NAME_PREFIX + "_new" + System.currentTimeMillis(); String newType = "type" + newTrackName; String newDesc = "desc" + newTrackName; instrumentation.waitForIdleSync(); rotateAllActivities(); sendKeys(KeyEvent.KEYCODE_DEL); solo.enterText(0, newTrackName); sendKeys(KeyEvent.KEYCODE_TAB); solo.enterText(1, newType); sendKeys(KeyEvent.KEYCODE_TAB); solo.enterText(2, newDesc); solo.clickOnButton(activityMyTracks.getString(R.string.generic_save)); instrumentation.waitForIdleSync(); // Goes back to track list. solo.goBack(); assertTrue(solo.searchText(newTrackName)); assertTrue(solo.searchText(newDesc)); } /** * Tests the menu My Location. */ public void testGotoMyLocation() { createSimpleTrack(1); solo.sendKey(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_my_location), true); // TODO How to verify the location is shown on the map. } /** * Changes some settings and then reset them. */ public void testSetting() { sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_settings), true); instrumentation.waitForIdleSync(); // Rotates on the settings page. rotateAllActivities(); solo.waitForText(activityMyTracks.getString(R.string.settings_reset)); // Resets all settings at first. solo.clickOnText(activityMyTracks.getString(R.string.settings_reset)); solo.clickOnButton(activityMyTracks.getString(R.string.generic_ok)); // Changes a setting of display. solo.clickOnText(activityMyTracks.getString(R.string.settings_display)); solo.waitForText(activityMyTracks.getString(R.string.settings_display_metric)); // Rotates on a sub setting page. rotateAllActivities(); solo.waitForText(activityMyTracks.getString(R.string.settings_display_metric)); ArrayList displayCheckBoxs = solo.getCurrentCheckBoxes(); boolean useMetric = displayCheckBoxs.get(0).isChecked(); solo.clickOnCheckBox(0); solo.goBack(); // Changes a setting of sharing. solo.clickOnText(activityMyTracks.getString(R.string.settings_sharing)); solo.waitForText(activityMyTracks.getString(R.string.settings_sharing_allow_access)); ArrayList sharingCheckBoxs = solo.getCurrentCheckBoxes(); boolean newMapsPublic = sharingCheckBoxs.get(0).isChecked(); solo.clickOnCheckBox(0); // Rotates on a sub setting page when the value of a checkbox is changed. instrumentation.waitForIdleSync(); rotateAllActivities(); solo.waitForText(activityMyTracks.getString(R.string.settings_sharing_allow_access)); assertEquals(!newMapsPublic, sharingCheckBoxs.get(0).isChecked()); solo.goBack(); // Resets all settings. solo.clickOnText(activityMyTracks.getString(R.string.settings_reset)); solo.clickOnButton(activityMyTracks.getString(R.string.generic_ok)); // Checks settings. solo.clickOnText(activityMyTracks.getString(R.string.settings_display)); solo.waitForText(activityMyTracks.getString(R.string.settings_display_metric)); displayCheckBoxs = solo.getCurrentCheckBoxes(); assertEquals(useMetric, displayCheckBoxs.get(0).isChecked()); solo.goBack(); solo.clickOnText(activityMyTracks.getString(R.string.settings_sharing)); solo.waitForText(activityMyTracks.getString(R.string.settings_sharing_allow_access)); sharingCheckBoxs = solo.getCurrentCheckBoxes(); assertEquals(newMapsPublic, sharingCheckBoxs.get(0).isChecked()); } /** * Creates one track with a two locations, a way point and a statistics point. */ public void testCreateTrackWithMarkers() { solo.sendKey(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_delete_all), true); solo.clickOnButton(activityMyTracks.getString(R.string.generic_ok)); instrumentation.waitForIdleSync(); startRecording(); instrumentation.waitForIdleSync(); SmokeTestUtils.sendGps(2); createWaypointAndStatistics(); SmokeTestUtils.sendGps(2); // Backs to tracks list. solo.goBack(); instrumentation.waitForIdleSync(); rotateAllActivities(); stopRecording(false); solo.enterText(0, trackName); solo.clickOnButton(activityMyTracks.getString(R.string.generic_save)); instrumentation.waitForIdleSync(); // Checks the new track assertTrue(solo.waitForText(trackName, 1, 5000, true, false)); } /** * Deletes all tracks. */ public void testDeleteTracks() { createSimpleTrack(0); solo.goBack(); instrumentation.waitForIdleSync(); // There is at least one track. ArrayList trackListView = solo.getCurrentListViews(); assertTrue(trackListView.size() > 0); assertTrue(trackListView.get(0).getCount() > 0); solo.sendKey(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_delete_all), true); instrumentation.waitForIdleSync(); rotateAllActivities(); solo.clickOnButton(activityMyTracks.getString(R.string.generic_ok)); instrumentation.waitForIdleSync(); // There is no track now. trackListView = solo.getCurrentListViews(); assertEquals(0, trackListView.get(0).getCount()); } /** * Tests export and import tracks. *
    *
  • Create two tracks, one of them is empty(Have no Gps data).
  • *
  • Tests import when there is no track file.
  • *
  • Tests export tracks to Gpx files.
  • *
  • Tests import files to tracks.
  • *
  • Tests export tracks to Kml files.
  • *
*/ public void testExportAndImportTracks() { // Creates a new track with 3 gps data. createSimpleTrack(3); solo.goBack(); instrumentation.waitForIdleSync(); // Creates a empty track. createSimpleTrack(0); solo.goBack(); instrumentation.waitForIdleSync(); // Deletes all exported gpx and kml tracks. deleteExportedFiles(GPX); deleteExportedFiles(KML); int gpxFilesNumber = 0; File[] allGpxFiles = getExportedFiles(GPX); // For the first export, there is no MyTracks folder. if (allGpxFiles != null) { gpxFilesNumber = getExportedFiles(GPX).length; } // Gets track number in current track list of MyTracks. int trackNumber = solo.getCurrentListViews().get(0).getCount(); // No file to imported. sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_import_all), true); solo.waitForText(activityMyTracks.getString(R.string.import_no_file, FileUtils.buildExternalDirectoryPath(GPX))); solo.clickOnButton(activityMyTracks.getString(R.string.generic_ok)); // Clicks to export tracks(At least one track) to Gpx files. sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_export_all), true); solo.clickOnText(String.format(activityMyTracks.getString(R.string.menu_export_all_format), GPX.toUpperCase())); rotateAllActivities(); solo.waitForText(activityMyTracks.getString(R.string.export_success)); // Checks export file. assertEquals(gpxFilesNumber + trackNumber, getExportedFiles(GPX).length); instrumentation.waitForIdleSync(); // Clicks to import track. gpxFilesNumber = getExportedFiles(GPX).length; trackNumber = solo.getCurrentListViews().get(0).getCount(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_import_all), true); rotateAllActivities(); // Waits for the prefix of import success string is much faster than wait // the whole string. solo.waitForText(activityMyTracks.getString(R.string.import_success).split("%")[0]); // Checks import tracks should be equal with the sum of trackNumber and // gpxFilesNumber; solo.clickOnText(activityMyTracks.getString(R.string.generic_ok)); instrumentation.waitForIdleSync(); assertEquals(trackNumber + gpxFilesNumber, solo.getCurrentListViews().get(0).getCount()); // Clicks to export tracks(At least two tracks) to KML files. gpxFilesNumber = getExportedFiles(GPX).length; trackNumber = solo.getCurrentListViews().get(0).getCount(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_export_all), true); solo.clickOnText(String.format(activityMyTracks.getString(R.string.menu_export_all_format), KML.toUpperCase())); solo.waitForText(activityMyTracks.getString(R.string.export_success)); // Checks export file. assertEquals(gpxFilesNumber, getExportedFiles(GPX).length); assertEquals(trackNumber, getExportedFiles(KML).length); } /** * Tests the switch between satellite mode and map mode. */ public void testSatelliteAndMapView() { startRecording(); instrumentation.waitForIdleSync(); // Check current mode. boolean isMapMode = true; sendKeys(KeyEvent.KEYCODE_MENU); // If can not find switch menu in top menu, click More menu. if (!findMenuItem(activityMyTracks.getString(R.string.menu_satellite_mode), false)) { isMapMode = false; } // Switches to satellite mode if it's map mode now.. solo.clickOnText(activityMyTracks.getString(isMapMode ? R.string.menu_satellite_mode : R.string.menu_map_mode)); isMapMode = !isMapMode; ArrayList allViews = solo.getViews(); for (View view : allViews) { if (view instanceof MapView) { assertEquals(isMapMode, !(((MapView) view).isSatellite())); } } instrumentation.waitForIdleSync(); solo.sendKey(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_help), false); instrumentation.waitForIdleSync(); rotateAllActivities(); // Switches back. solo.clickOnText(activityMyTracks.getString(isMapMode ? R.string.menu_satellite_mode : R.string.menu_map_mode)); isMapMode = !isMapMode; allViews = solo.getViews(); for (View view : allViews) { if (view instanceof MapView) { assertEquals(isMapMode, !(((MapView) view).isSatellite())); } } // Waits for the TrackDetailActivity, or the stop button will can not be // found in next step. solo.waitForText(activityMyTracks.getString(R.string.track_detail_chart_tab), 0, 1000); stopRecording(true); } /** * Tests following items in More menu. *
    *
  • Tests the aggregated statistics activity.
  • *
  • Tests the Sensor state activity.
  • *
  • Tests the help menu.
  • *
*/ public void testSomeMenuItems() { // Menu in TrackListActivity. sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_aggregated_statistics), true); solo.waitForText(activityMyTracks.getString(R.string.stat_total_distance)); solo.goBack(); startRecording(); instrumentation.waitForIdleSync(); // Menu in TrackDetailActivity. sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_sensor_state), true); solo.waitForText(activityMyTracks.getString(R.string.sensor_state_last_sensor_time)); solo.goBack(); sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_help), true); solo.clickOnText(activityMyTracks.getString(R.string.help_about)); solo.clickOnText(activityMyTracks.getString(R.string.generic_ok)); solo.clickOnText(activityMyTracks.getString(R.string.generic_ok)); stopRecording(true); } /** * Creates a way point and a statistics during track recording. */ private void createWaypointAndStatistics() { sendKeys(KeyEvent.KEYCODE_MENU); findMenuItem(activityMyTracks.getString(R.string.menu_markers), true); solo.clickOnButton(activityMyTracks.getString(R.string.marker_list_insert_waypoint)); solo.clickOnText(activityMyTracks.getString(R.string.marker_detail_marker_name)); instrumentation.waitForIdleSync(); rotateAllActivities(); solo.enterText(0, WAYPOINT_NAME); solo.clickOnButton(activityMyTracks.getString(R.string.generic_save)); solo.clickOnButton(activityMyTracks.getString(R.string.marker_list_insert_statistics)); solo.clickOnText(activityMyTracks.getString(R.string.marker_detail_marker_name)); solo.enterText(0, STATISTICS_NAME); solo.clickOnButton(activityMyTracks.getString(R.string.generic_save)); assertTrue(solo.searchText(WAYPOINT_NAME)); assertTrue(solo.searchText(STATISTICS_NAME)); solo.goBack(); } @Override protected void tearDown() throws Exception { solo.finishOpenedActivities(); super.tearDown(); } /** * Accepts terms and configures units. */ private void verifyFirstLaunch() { solo.clickOnText(activityMyTracks.getString(R.string.eula_accept)); // Clicks for welcome. solo.clickOnText(activityMyTracks.getString(R.string.generic_ok)); // Clicks for choose units. solo.clickOnText(activityMyTracks.getString(R.string.generic_ok)); instrumentation.waitForIdleSync(); } /** * Creates a simple track which can be used by subsequent test. * * @param numberOfGpsData number of simulated Gps data */ private void createSimpleTrack(int numberOfGpsData) { startRecording(); SmokeTestUtils.sendGps(numberOfGpsData); stopRecording(true); } /** * Starts recoding track. */ private void startRecording() { if (hasActionBar) { Button startButton = getButtonOnScreen(activityMyTracks.getString(R.string.menu_record_track)); // In case a track is recording. if (startButton == null) { stopRecording(true); startButton = getButtonOnScreen(activityMyTracks.getString(R.string.menu_record_track)); } solo.clickOnView(startButton); } else { sendKeys(KeyEvent.KEYCODE_MENU); if (!solo.searchText(activityMyTracks.getString(R.string.menu_record_track))) { // Check if in TrackDetailActivity. if (solo.searchText(activityMyTracks.getString(R.string.menu_play))) { solo.goBack(); } else { // In case a track is recording. stopRecording(true); sendKeys(KeyEvent.KEYCODE_MENU); } } instrumentation.waitForIdleSync(); solo.clickOnText(activityMyTracks.getString(R.string.menu_record_track)); } } /** * Stops recoding track. * * @param isSave ture means should save this track */ private void stopRecording(boolean isSave) { instrumentation.waitForIdleSync(); if (hasActionBar) { solo.clickOnView(getButtonOnScreen(activityMyTracks.getString(R.string.menu_stop_recording))); } else { sendKeys(KeyEvent.KEYCODE_MENU); solo.clickOnText(activityMyTracks.getString(R.string.menu_stop_recording)); } if (isSave) { instrumentation.waitForIdleSync(); solo.enterText(0, trackName); solo.clickLongOnText(activityMyTracks.getString(R.string.generic_save)); } } /** * Deletes a kind of track in MyTracks folder. * * @param trackKind the kind of track */ private void deleteExportedFiles(String trackKind) { File[] allFiles = (new File(FileUtils.buildExternalDirectoryPath(trackKind.toLowerCase()))).listFiles(); if (allFiles != null) { for (File oneFile : allFiles) { oneFile.delete(); } } } /** * Gets a kind of exported files. * * @param trackKind the kind of track * @return files array of such kind of exported tracks */ private File[] getExportedFiles(final String trackKind) { String filePath = FileUtils.buildExternalDirectoryPath(trackKind); FileFilter filter = new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().indexOf("." + trackKind) > 0; } }; return (new File(filePath)).listFiles(filter); } /** * Saves a track to a kind of file in SD card. * * @param trackKind the kind of track */ private void saveTrackToSdCard(String trackKind) { deleteExportedFiles(trackKind); instrumentation.waitForIdleSync(); sendKeys(KeyEvent.KEYCODE_MENU); instrumentation.waitForIdleSync(); solo.clickOnText(activityMyTracks.getString(R.string.menu_save)); solo.clickOnText(trackKind.toUpperCase()); rotateAllActivities(); solo.waitForText(activityMyTracks.getString(R.string.generic_success_title)); assertEquals(1, getExportedFiles(trackKind).length); } /** * Checks if a button is existed in the screen. * * @param buttonName the name string of the button * @return the button to search, and null means can not find the button */ private Button getButtonOnScreen(String buttonName) { ArrayList