/* * 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.endtoendtest; import com.google.android.apps.mytracks.TrackListActivity; import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.maps.mytracks.R; import com.jayway.android.robotium.solo.Solo; import android.app.Activity; import android.app.Instrumentation; import android.content.res.Configuration; import android.view.KeyEvent; import android.widget.Button; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; /** * Provides utilities to smoke test. * * @author Youtao Liu */ public class EndToEndTestUtils { private static final String ANDROID_LOCAL_IP = "10.0.2.2"; // usually 5554. private static final int ANDROID_LOCAL_PORT = 5554; private static final int ORIENTATION_PORTRAIT = 1; private static final int ORIENTATION_LANDSCAPE = 0; // Pause 200ms between each send. static int PAUSE = 200; private static final float START_LONGITUDE = 51; private static final float START_LATITUDE = -1.3f; private static final float DELTA_LONGITUDE = 0.0005f; private static final float DELTA_LADITUDE = 0.0005f; static final String TRACK_NAME_PREFIX = "testTrackName"; static final String GPX = "gpx"; static final String KML = "kml"; static final String CSV = "csv"; static final String TCX = "tcx"; static final String MENU_MORE = "More"; static String TRACK_NAME; static Solo SOLO; static Instrumentation INSTRUMENTATION; static TrackListActivity ACTIVITYMYTRACKS; // Check whether the UI has an action bar which is related with the version of // Android OS. static boolean HAS_ACTIONBAR = false; static boolean CHECKED_FIRSTLAUNCH = false; private EndToEndTestUtils() {} /** * Sends Gps data to emulator. * * @param number send times */ public static void sendGps(int number) { if (number < 1) { return; } PrintStream out = null; Socket socket = null; try { socket = new Socket(ANDROID_LOCAL_IP, ANDROID_LOCAL_PORT); out = new PrintStream(socket.getOutputStream()); float longitude = START_LONGITUDE; float latitude = START_LATITUDE; for (int i = 0; i < number; i++) { Thread.sleep(PAUSE); out.println("geo fix " + latitude + " " + longitude); longitude += DELTA_LONGITUDE; latitude += DELTA_LADITUDE; } } catch (UnknownHostException e) { System.exit(-1); } catch (IOException e) { System.exit(-1); } catch (InterruptedException e) { System.exit(-1); } finally { if (out != null) { out.close(); } } } /** * A setup for all end-to-end tests. * * @param instrumentation the instrumentation is used for test * @param activityMyTracks the startup activity */ static void setupForAllTest(Instrumentation instrumentation, TrackListActivity activityMyTracks) { EndToEndTestUtils.INSTRUMENTATION = instrumentation; EndToEndTestUtils.ACTIVITYMYTRACKS = activityMyTracks; EndToEndTestUtils.SOLO = new Solo(EndToEndTestUtils.INSTRUMENTATION, EndToEndTestUtils.ACTIVITYMYTRACKS); // Check 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 (!EndToEndTestUtils.CHECKED_FIRSTLAUNCH) { if ((EndToEndTestUtils.getButtonOnScreen(EndToEndTestUtils.ACTIVITYMYTRACKS .getString(R.string.eula_accept)) != null)) { EndToEndTestUtils.verifyFirstLaunch(); } EndToEndTestUtils.CHECKED_FIRSTLAUNCH = true; EndToEndTestUtils.setHasActionBar(); } } /** * Rotates the given activity. * * @param activity a given activity */ static void rotateActivity(Activity activity) { activity .setRequestedOrientation(activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT); } /** * Accepts terms and configures units. */ static void verifyFirstLaunch() { SOLO.clickOnText(ACTIVITYMYTRACKS.getString(R.string.eula_accept)); // Click for welcome. SOLO.clickOnText(ACTIVITYMYTRACKS.getString(R.string.generic_ok)); // Click 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 */ static void createSimpleTrack(int numberOfGpsData) { startRecording(); EndToEndTestUtils.sendGps(numberOfGpsData); stopRecording(true); } /** * Starts recoding track. */ static void startRecording() { if (HAS_ACTIONBAR) { 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 { SOLO.sendKey(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); SOLO.sendKey(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 */ static void stopRecording(boolean isSave) { INSTRUMENTATION.waitForIdleSync(); if (HAS_ACTIONBAR) { SOLO.clickOnView(getButtonOnScreen(ACTIVITYMYTRACKS.getString(R.string.menu_stop_recording))); } else { SOLO.sendKey(KeyEvent.KEYCODE_MENU); INSTRUMENTATION.waitForIdleSync(); SOLO.clickOnText(ACTIVITYMYTRACKS.getString(R.string.menu_stop_recording)); } if (isSave) { INSTRUMENTATION.waitForIdleSync(); // Make every track name is unique to make sure every check can be // trusted. EndToEndTestUtils.TRACK_NAME = EndToEndTestUtils.TRACK_NAME_PREFIX + System.currentTimeMillis(); SOLO.enterText(0, TRACK_NAME); SOLO.clickLongOnText(ACTIVITYMYTRACKS.getString(R.string.generic_save)); } } /** * Deletes a kind of track in MyTracks folder. * * @param trackKind the kind of track */ static 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 */ static 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 */ static void saveTrackToSdCard(String trackKind) { deleteExportedFiles(trackKind); INSTRUMENTATION.waitForIdleSync(); SOLO.sendKey(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)); } /** * 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 */ static Button getButtonOnScreen(String buttonName) { ArrayList