Fix FullScreen crash in Webapp
[platform/framework/web/chromium-efl.git] / tools / auto-nav.py
1 # Copyright 2020 The Chromium Authors
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """
5 This script runs Chrome and automatically navigates through the given list of
6 URLs the specified number of times.
7
8 Usage: vpython3 auto-nav.py <chrome dir> <number of navigations> <url> <url> ...
9
10 Optional flags:
11 * --interval <seconds>, -i <seconds>: specify a number of seconds to wait
12                                       between navigations, e.g., -i=5
13 * --start_prompt, -s: start Chrome, then wait for the user to press Enter before
14                       starting auto-navigation
15 * --exit-prompt, -e: after auto-navigation, wait for the user to press Enter
16                      before shutting down chrome.exe
17 * --idlewakeups_dir: Windows only; specify the directory containing
18                      idlewakeups.exe to print measurements taken by IdleWakeups,
19                      e.g., --idlewakeups_dir=tools/win/IdleWakeups/x64/Debug
20
21 Optional flags to chrome.exe, example:
22 -- --user-data-dir=temp --disable-features=SomeFeature
23 Note: must be at end of command, following options terminator "--". The options
24 terminator stops command-line options from being interpreted as options for this
25 script, which would cause an unrecognized-argument error.
26 """
27
28 # [VPYTHON:BEGIN]
29 # python_version: "3.8"
30 # wheel: <
31 #   name: "infra/python/wheels/selenium-py2_py3"
32 #   version: "version:3.14.0"
33 # >
34 # wheel: <
35 #   name: "infra/python/wheels/urllib3-py2_py3"
36 #   version: "version:1.24.3"
37 # >
38 # wheel: <
39 #   name: "infra/python/wheels/psutil/${vpython_platform}"
40 #   version: "version:5.7.2"
41 # >
42 # [VPYTHON:END]
43
44 import argparse
45 import os
46 import subprocess
47 import sys
48 import time
49 import urllib
50
51 try:
52   import psutil
53   from selenium import webdriver
54 except ImportError:
55   print('Error importing required modules. Run with vpython3 instead of '
56         'python.')
57   sys.exit(1)
58
59 DEFAULT_INTERVAL = 1
60 EXIT_CODE_ERROR = 1
61
62 # Splits list |positional_args| into two lists: |urls| and |chrome_args|, where
63 # arguments starting with '-' are treated as chrome args, and the rest as URLs.
64 def ParsePositionalArgs(positional_args):
65   urls, chrome_args = [], []
66   for arg in positional_args:
67     if arg.startswith('-'):
68       chrome_args.append(arg)
69     else:
70       urls.append(arg)
71   return [urls, chrome_args]
72
73
74 # Returns an object containing the arguments parsed from this script's command
75 # line.
76 def ParseArgs():
77   # Customize usage and help to include options to be passed to chrome.exe.
78   usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--start_prompt]
79                    [--exit_prompt] [--idlewakeups_dir IDLEWAKEUPS_DIR]
80                    chrome_dir num_navigations url [url ...]
81                    [-- --chrome_option ...]'''
82   additional_help_text = '''optional arguments to chrome.exe, example:
83   -- --enable-features=MyFeature --browser-startup-dialog
84                         Must be at end of command, following the options
85                         terminator "--"'''
86   parser = argparse.ArgumentParser(
87       epilog=additional_help_text,
88       usage=usage_text,
89       formatter_class=argparse.RawDescriptionHelpFormatter)
90   parser.add_argument(
91       'chrome_dir', help='Directory containing chrome.exe and chromedriver.exe')
92   parser.add_argument('num_navigations',
93                       type=int,
94                       help='Number of times to navigate through list of URLs')
95   parser.add_argument('--interval',
96                       '-i',
97                       type=int,
98                       help='Seconds to wait between navigations; default is 1')
99   parser.add_argument('--start_prompt',
100                       '-s',
101                       action='store_true',
102                       help='Wait for confirmation before starting navigation')
103   parser.add_argument('--exit_prompt',
104                       '-e',
105                       action='store_true',
106                       help='Wait for confirmation before exiting chrome.exe')
107   parser.add_argument(
108       '--idlewakeups_dir',
109       help='Windows only; directory containing idlewakeups.exe, if using')
110   parser.add_argument(
111       'url',
112       nargs='+',
113       help='URL(s) to navigate, separated by spaces; must include scheme, '
114       'e.g., "https://"')
115   args = parser.parse_args()
116   args.url, chrome_args = ParsePositionalArgs(args.url)
117   if not args.url:
118     parser.print_usage()
119     print(os.path.basename(__file__) + ': error: missing URL argument')
120     exit(EXIT_CODE_ERROR)
121   for url in args.url:
122     if not urllib.parse.urlparse(url).scheme:
123       print(os.path.basename(__file__) +
124             ': error: URL is missing required scheme (e.g., "https://"): ' + url)
125       exit(EXIT_CODE_ERROR)
126   return [args, chrome_args]
127
128
129 # If |path| does not exist, prints a generic error plus optional |error_message|
130 # and exits.
131 def ExitIfNotFound(path, error_message=None):
132   if not os.path.exists(path):
133     print('File not found: {}.'.format(path))
134     if error_message:
135       print(error_message)
136     exit(EXIT_CODE_ERROR)
137
138
139 def main():
140   # Parse arguments and check that file paths received are valid.
141   args, chrome_args = ParseArgs()
142   ExitIfNotFound(os.path.join(args.chrome_dir, 'chrome.exe'),
143                  'Build target "chrome" to generate it first.')
144   chromedriver_exe = os.path.join(args.chrome_dir, 'chromedriver.exe')
145   ExitIfNotFound(chromedriver_exe,
146                  'Build target "chromedriver" to generate it first.')
147   if args.idlewakeups_dir:
148     idlewakeups_exe = os.path.join(args.idlewakeups_dir, 'idlewakeups.exe')
149     ExitIfNotFound(idlewakeups_exe)
150
151   # Start chrome.exe. Disable chrome.exe's extensive logging to make reading
152   # this script's output easier.
153   chrome_options = webdriver.ChromeOptions()
154   chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
155   for arg in chrome_args:
156     chrome_options.add_argument(arg)
157   driver = webdriver.Chrome(os.path.abspath(chromedriver_exe),
158                             options=chrome_options)
159
160   if args.start_prompt:
161     driver.get(args.url[0])
162     input('Press Enter to begin navigation...')
163
164   # Start IdleWakeups, if using, passing the browser process's ID as its target.
165   # IdleWakeups will monitor the browser process and its children. Other running
166   # chrome.exe processes (i.e., those not launched by this script) are excluded.
167   if args.idlewakeups_dir:
168     launched_processes = psutil.Process(
169         driver.service.process.pid).children(recursive=False)
170     if not launched_processes:
171       print('Error getting browser process ID for IdleWakeups.')
172       exit()
173     # Assume the first child process created by |driver| is the browser process.
174     idlewakeups = subprocess.Popen([
175         idlewakeups_exe,
176         str(launched_processes[0].pid), '--stop-on-exit', '--tabbed'
177     ],
178                                    stdout=subprocess.PIPE)
179
180   # Navigate through |args.url| list |args.num_navigations| times, then close
181   # chrome.exe.
182   interval = args.interval if args.interval else DEFAULT_INTERVAL
183   for _ in range(args.num_navigations):
184     for url in args.url:
185       driver.get(url)
186       time.sleep(interval)
187
188   if args.exit_prompt:
189     input('Press Enter to exit...')
190   driver.quit()
191
192   # Print IdleWakeups' output, if using.
193   if args.idlewakeups_dir:
194     print(idlewakeups.communicate()[0])
195
196
197 if __name__ == '__main__':
198   sys.exit(main())