1# Copyright 2013 The Chromium 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 5"""Handles generating profiles and transferring them to/from mobile devices.""" 6 7import logging 8import optparse 9import os 10import shutil 11import sys 12import tempfile 13 14from telemetry.core import browser_options 15from telemetry.core import discover 16from telemetry.core import util 17from telemetry.page import page_runner 18from telemetry.page import profile_creator 19from telemetry.page import test_expectations 20 21 22def _DiscoverProfileCreatorClasses(): 23 profile_creators_dir = os.path.abspath(os.path.join(util.GetBaseDir(), 24 os.pardir, 'perf', 'profile_creators')) 25 base_dir = os.path.abspath(os.path.join(profile_creators_dir, os.pardir)) 26 27 profile_creators_unfiltered = discover.DiscoverClasses( 28 profile_creators_dir, base_dir, profile_creator.ProfileCreator) 29 30 # Remove '_creator' suffix from keys. 31 profile_creators = {} 32 for test_name, test_class in profile_creators_unfiltered.iteritems(): 33 assert test_name.endswith('_creator') 34 test_name = test_name[:-len('_creator')] 35 profile_creators[test_name] = test_class 36 return profile_creators 37 38 39def GenerateProfiles(profile_creator_class, profile_creator_name, options): 40 """Generate a profile""" 41 expectations = test_expectations.TestExpectations() 42 test = profile_creator_class() 43 44 temp_output_directory = tempfile.mkdtemp() 45 options.output_profile_path = temp_output_directory 46 47 results = page_runner.Run(test, test.page_set, expectations, options) 48 49 if results.errors or results.failures: 50 logging.warning('Some pages failed.') 51 if results.errors or results.failures: 52 logging.warning('Failed pages:\n%s', 53 '\n'.join(zip(*results.errors + results.failures)[0])) 54 return 1 55 56 # Everything is a-ok, move results to final destination. 57 generated_profiles_dir = os.path.abspath(options.output_dir) 58 if not os.path.exists(generated_profiles_dir): 59 os.makedirs(generated_profiles_dir) 60 out_path = os.path.join(generated_profiles_dir, profile_creator_name) 61 if os.path.exists(out_path): 62 shutil.rmtree(out_path) 63 64 # A profile may contain pseudo files like sockets which can't be copied 65 # around by bots. 66 def IsPseudoFile(directory, paths): 67 ignore_list = [] 68 for path in paths: 69 full_path = os.path.join(directory, path) 70 if (not os.path.isfile(full_path) and 71 not os.path.isdir(full_path) and 72 not os.path.islink(full_path)): 73 logging.warning('Ignoring pseudo file: %s' % full_path) 74 ignore_list.append(path) 75 return ignore_list 76 shutil.copytree(temp_output_directory, out_path, ignore=IsPseudoFile) 77 shutil.rmtree(temp_output_directory) 78 sys.stderr.write("SUCCESS: Generated profile copied to: '%s'.\n" % out_path) 79 80 return 0 81 82 83def AddCommandLineArgs(parser): 84 page_runner.AddCommandLineArgs(parser) 85 86 profile_creators = _DiscoverProfileCreatorClasses().keys() 87 legal_profile_creators = '|'.join(profile_creators) 88 group = optparse.OptionGroup(parser, 'Profile generation options') 89 group.add_option('--profile-type-to-generate', 90 dest='profile_type_to_generate', 91 default=None, 92 help='Type of profile to generate. ' 93 'Supported values: %s' % legal_profile_creators) 94 group.add_option('--output-dir', 95 dest='output_dir', 96 help='Generated profile is placed in this directory.') 97 parser.add_option_group(group) 98 99 100def ProcessCommandLineArgs(parser, args): 101 page_runner.ProcessCommandLineArgs(parser, args) 102 103 if not args.profile_type_to_generate: 104 parser.error("Must specify --profile-type-to-generate option.") 105 106 profile_creators = _DiscoverProfileCreatorClasses().keys() 107 if args.profile_type_to_generate not in profile_creators: 108 legal_profile_creators = '|'.join(profile_creators) 109 parser.error("Invalid profile type, legal values are: %s." % 110 legal_profile_creators) 111 112 if not args.browser_type: 113 parser.error("Must specify --browser option.") 114 115 if not args.output_dir: 116 parser.error("Must specify --output-dir option.") 117 118 if args.browser_options.dont_override_profile: 119 parser.error("Can't use existing profile when generating profile.") 120 121 122def Main(): 123 options = browser_options.BrowserFinderOptions() 124 parser = options.CreateParser( 125 "%%prog <--profile-type-to-generate=...> <--browser=...>" 126 " <--output-directory>") 127 AddCommandLineArgs(parser) 128 _, _ = parser.parse_args() 129 ProcessCommandLineArgs(parser, options) 130 131 # Generate profile. 132 profile_creators = _DiscoverProfileCreatorClasses() 133 profile_creator_class = profile_creators[options.profile_type_to_generate] 134 return GenerateProfiles(profile_creator_class, 135 options.profile_type_to_generate, options) 136