1# Copyright 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7import time
8
9from commands import *
10
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.common_lib.cros import chromedriver
13from selenium.webdriver.common.keys import Keys
14from autotest_lib.client.cros.graphics import graphics_utils
15from selenium.webdriver.common.action_chains import ActionChains
16from selenium.common.exceptions import WebDriverException
17
18TIMEOUT_TO_COPY = 1800  # in Secs. This timeout is for files beyond 1GB
19SEARCH_BUTTON_ID = "search-button"
20SEARCH_BOX_CSS = "div#search-box"
21PAPER_CONTAINTER = "paper-input-container"
22DELETE_BUTTON_ID = "delete-button"
23FILE_LIST_ID = "file-list"
24LABLE_ENTRY_CSS = "span.label.entry-name"
25CR_DIALOG_CLASS = "cr-dialog-ok"
26USER_LOCATION = "/home/chronos/user"
27# Using graphics_utils to simulate below keys
28OPEN_FILES_APPLICATION_KEYS = ["KEY_RIGHTSHIFT", "KEY_LEFTALT", "KEY_M"]
29SWITCH_TO_APP_KEY_COMBINATION = ["KEY_LEFTALT", 'KEY_TAB']
30SELECT_ALL_KEY_COMBINATION = ["KEY_LEFTCTRL", "KEY_A"]
31PASTE_KEY_COMBINATION = ["KEY_LEFTCTRL", "KEY_V"]
32GOOGLE_DRIVE = 'My Drive'
33
34
35class files_CopyFileToGoogleDriveUI(graphics_utils.GraphicsTest):
36
37    """Copy a file from Downloads folder to Google drive"""
38
39    version = 1
40    TIME_DELAY = 5
41    _WAIT_TO_LOAD = 5
42
43    def initialize(self):
44        """Autotest initialize function"""
45        super(files_CopyFileToGoogleDriveUI, self).initialize(
46                raise_error_on_hang=True)
47
48    def cleanup(self):
49        """Autotest cleanup function"""
50        if self._GSC:
51            keyvals = self._GSC.get_memory_difference_keyvals()
52            for key, val in keyvals.iteritems():
53                self.output_perf_value(
54                    description=key,
55                    value=val,
56                    units='bytes',
57                    higher_is_better=False)
58            self.write_perf_keyval(keyvals)
59        super(files_CopyFileToGoogleDriveUI, self).cleanup()
60        # If test fails then script will collect the screen shot to know at
61        # which instance failure occurred.
62        if not self.success:
63            graphics_utils.take_screenshot(os.path.join(self.debugdir),
64                                           "chrome")
65
66    def switch_to_app(self, driver, title):
67        """Switching to application using title
68
69        @param driver: chrome driver object
70        @param title: Title of the application
71        @return: True if the app is detected otherwise False
72        """
73        windows = driver.window_handles
74        logging.debug("Windows opened: %s", windows)
75        # Checking current window initially..
76        logging.debug("Current window is %s", driver.title)
77        if driver.title.strip().lower() == title.lower():
78            return True
79        # Switching to all opened windows to find out the required window
80        for window in windows:
81            try:
82                logging.debug("Switching to window")
83                driver.switch_to_window(window)
84                logging.debug("Switched to window: %s", driver.title)
85                time.sleep(2)
86                if driver.title.strip().lower() == title.lower():
87                    logging.info("%s application opened!", title)
88                    return True
89            except WebDriverException as we:
90                logging.debug("Webdriver exception occurred. Exception: %s",
91                              str(we))
92            except Exception as e:
93                logging.debug("Exception: %s", str(e))
94        return False
95
96    def open_files_application(self, driver):
97        """Open and switch to files application using graphics_utils.py
98
99        @param driver: chrome driver object
100        """
101        logging.info("Opening files application")
102        graphics_utils.press_keys(OPEN_FILES_APPLICATION_KEYS)
103        time.sleep(self._WAIT_TO_LOAD)
104        try:
105            self.switch_to_files(driver)
106        except Exception as e:
107            logging.error("Exception when switching files application.. %s",
108                          str(e))
109            logging.error("Failed to find files application. Trying again.")
110            graphics_utils.press_keys(OPEN_FILES_APPLICATION_KEYS)
111            time.sleep(self._WAIT_TO_LOAD)
112            self.switch_to_files(driver)
113
114    def switch_to_files(self, driver, title="Downloads"):
115        """Switch to files application
116
117        @param driver: chrome driver object
118        @param title: Title of the Files application
119        """
120        logging.debug("Switching/Focus on the Files app")
121        if self.switch_to_app(driver, title):
122            logging.info("Focused on Files application")
123            graphics_utils.press_keys(SWITCH_TO_APP_KEY_COMBINATION)
124            time.sleep(1)
125        else:
126            raise error.TestFail("Failed to open on Files application")
127
128    def check_folder_opened(self, driver, title):
129        """Check the selected folder is opened or not
130
131        @param driver: chrome driver object
132        @param title: Folder name
133        @return: Returns True if expected folder is opened otherwise False
134        """
135        logging.info("Actual files application title is %s", driver.title)
136        logging.info("Expected files application title is %s", title)
137        if driver.title == title:
138            return True
139        return False
140
141    def open_folder(self, driver, folder):
142        """Open given folder
143
144        @param driver: chrome driver object
145        @param folder: Directory name
146        """
147        folder_webelements = driver.find_elements_by_css_selector(
148            LABLE_ENTRY_CSS)
149        for element in folder_webelements:
150            try:
151                logging.debug("Found folder name: %s", element.text.strip())
152                if folder == element.text.strip():
153                    element.click()
154                    time.sleep(3)
155                    if self.check_folder_opened(driver, element.text.strip()):
156                        logging.info("Folder is opened!")
157                        return
158            except Exception as e:
159                logging.error("Exception when getting Files application "
160                              "folders %s", str(e))
161        raise error.TestError("Folder :%s is not opened or found", folder)
162
163    def list_files(self, driver):
164        """List files in the folder
165
166        @param driver: chrome driver object
167        @return: Returns list of files
168        """
169        return driver.find_element_by_id(
170            FILE_LIST_ID).find_elements_by_tag_name('li')
171
172    def search_file(self, driver, file_name):
173        """Search given file in Files application
174
175        @param driver: chrome driver object
176        @param file_name: Required file
177        """
178        driver.find_element_by_id(SEARCH_BUTTON_ID).click()
179        search_box_element = driver.find_element_by_css_selector(
180            SEARCH_BOX_CSS)
181        search_box_element.find_element_by_css_selector(
182            PAPER_CONTAINTER).find_element_by_tag_name('input').clear()
183        search_box_element.find_element_by_css_selector(
184            PAPER_CONTAINTER).find_element_by_tag_name('input').send_keys(
185            file_name)
186
187    def copy_file(self, driver, source, destination, file_name, clean=True):
188        """Copy file from one directory to another
189
190        @param driver: chrome driver object
191        @param source: Directory name from where to copy
192        @param destination: Directory name to where to copy
193        @param file_name: File to copy
194        @param clean: Cleans destination if True otherwise nothing
195        """
196        self.open_folder(driver, source)
197        self.search_file(driver, file_name)
198        files = self.list_files(driver)
199        action_chains = ActionChains(driver)
200
201        for item in files:
202            logging.info("Selecting file to copy in %s", file_name)
203            item.click()
204            file_size = item.text.split()[1].strip()
205            file_size_units = item.text.split()[2].strip()
206            logging.debug("Select copy")
207            action_chains.move_to_element(item) \
208                .click(item).key_down(Keys.CONTROL) \
209                .send_keys("c") \
210                .key_up(Keys.CONTROL) \
211                .perform()
212            self.open_folder(driver, destination)
213            if clean:
214                drive_files = self.list_files(driver)
215                if len(drive_files) != 0:
216                    logging.info("Removing existing files from %s",
217                                 destination)
218                    drive_files[0].click()
219                    logging.debug("Select all files/dirs")
220                    graphics_utils.press_keys(SELECT_ALL_KEY_COMBINATION)
221                    time.sleep(0.2)
222                    driver.find_element_by_id(DELETE_BUTTON_ID).click()
223                    driver.find_element_by_class_name(CR_DIALOG_CLASS).click()
224                    time.sleep(self.TIME_DELAY)
225            logging.debug("Pressing control+v to paste the file in required "
226                          "location")
227            graphics_utils.press_keys(PASTE_KEY_COMBINATION)
228            time.sleep(self.TIME_DELAY)
229            # Take dummy values initially
230            required_file_size = "0"
231            required_file_size_units = "KB"
232            required_file = None
233            # wait till the data copied
234            start_time = time.time()
235            while required_file_size != file_size and \
236                required_file_size_units != file_size_units and \
237                    (time.time() - start_time <= TIMEOUT_TO_COPY):
238                drive_files_during_copy = self.list_files(driver)
239                if len(drive_files_during_copy) == 0:
240                    raise error.TestError("File copy not started!")
241                for i_item in drive_files_during_copy:
242                    if i_item.text.strip().split()[0].strip() == file_name:
243                        logging.info("File found %s", i_item.text.split()[
244                            0].strip())
245                        required_file = file
246                if not required_file:
247                    raise error.TestError("No such file/directory in drive, "
248                                          "%s", required_file)
249                logging.info(required_file.text.split())
250                required_file_size = required_file.text.split()[1]
251                required_file_size_units = required_file.text.split()[2]
252                time.sleep(5)
253                logging.debug("%s %s data copied" % (required_file_size,
254                                                     required_file_size_units))
255            # Validation starts here
256            found = False
257            drive_files_after_copy = self.list_files(driver)
258            for copied_file in drive_files_after_copy:
259                logging.debug("File in destination: %s",
260                              copied_file.text.strip())
261                if copied_file.find_element_by_class_name(
262                        'entry-name').text.strip() == file_name:
263                    found = True
264                    break
265
266            if found:
267                logging.info("Copied the file successfully!")
268            else:
269                raise error.TestFail("File not transferred successfully!")
270
271    def catch_info_or_error_messages(self, driver):
272        """Logic to catch the error
273
274        @param driver: chrome driver object
275        """
276        errors = []
277        try:
278            driver.find_element_by_css_selector(
279                'div.button-frame').find_element_by_class_name('open').click()
280        except Exception as e:
281            logging.info("Error in open error messages")
282            logging.info(str(e))
283        error_elements = driver.find_elements_by_css_selector(
284            'div.progress-frame')
285        if len(error_elements) != 0:
286            for error_element in error_elements:
287                info_text = error_element.find_element_by_tag_name(
288                    'label').text
289                if info_text != "":
290                    errors.append(info_text)
291        return errors
292
293    def create_file(self, filename):
294        """Create a file"""
295        status, output = getstatusoutput('dd if=/dev/zero of=%s bs=%s '
296                                         'count=1 iflag=fullblock' %
297                                         (filename, 1024))
298        if status:
299            raise error.TestError("Failed to create file")
300
301    def run_once(self, username=None, password=None, source="Downloads",
302                 file_name='test.dat'):
303        """Copy file to Google Drive in Files application
304
305        @param username: Real user(Not default autotest user)
306        @param password: Password for the user.
307        @param source: From where to copy file
308        @param file_name: File name
309        """
310        self.success = False  # Used to capture the screenshot if the TC fails
311        with chromedriver.chromedriver(username=username,
312                                       password=password,
313                                       disable_default_apps=False,
314                                       gaia_login=True) as cr_instance:
315            driver = cr_instance.driver
316            self.open_files_application(driver)
317            self.create_file(os.path.join(os.path.join(USER_LOCATION,
318                                                       source), file_name))
319            self.copy_file(driver, source, GOOGLE_DRIVE, file_name)
320            errors = self.catch_info_or_error_messages(driver)
321            if len(errors):
322                raise error.TestFail("Test failed with the following"
323                                     " errors. %s", errors)
324        self.success = True
325