11# This Source Code Form is subject to the terms of the Mozilla Public
22# License, v. 2.0. If a copy of the MPL was not distributed with this
33# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4- import os
4+
5+ from __future__ import annotations
6+
57from logging import getLogger
68from pathlib import Path
79from tempfile import TemporaryDirectory , mkdtemp
10+ from typing import TYPE_CHECKING
811
912from ffpuppet import BrowserTimeoutError , LaunchError
1013from fxpoppet import ADBLaunchError , ADBProcess , ADBSession , Reason
1619from .target import Result , Target , TargetLaunchError , TargetLaunchTimeout
1720from .target_monitor import TargetMonitor
1821
22+ if TYPE_CHECKING :
23+ from collections .abc import Iterable , Mapping
24+ from typing import Any
25+
1926__author__ = "Tyson Smith"
2027__credits__ = ["Tyson Smith" , "Jesse Schwartzentruber" ]
2128
2229LOG = getLogger ("adb_target" )
2330
2431
32+ class ADBMonitor (TargetMonitor ):
33+ def __init__ (self , proc : ADBProcess ) -> None :
34+ self ._proc = proc
35+
36+ def clone_log (self , log_id : str , offset : int = 0 ) -> None :
37+ # TODO: unused... remove
38+ return None
39+
40+ def is_healthy (self ) -> bool :
41+ return self ._proc .is_healthy ()
42+
43+ def is_idle (self , threshold : int ) -> bool :
44+ return False
45+
46+ def is_running (self ) -> bool :
47+ return self ._proc .is_running ()
48+
49+ @property
50+ def launches (self ) -> int :
51+ return self ._proc .launches
52+
53+ def log_length (self , log_id : str ) -> int :
54+ # TODO: This needs to be implemented
55+ return 0
56+
57+
2558class ADBTarget (Target ):
2659 SUPPORTED_ASSETS = ("prefs" ,)
2760
@@ -35,50 +68,58 @@ class ADBTarget(Target):
3568 "use_rr" ,
3669 )
3770
38- def __init__ (self , binary , launch_timeout , log_limit , memory_limit , ** kwds ):
71+ def __init__ (
72+ self ,
73+ binary : Path ,
74+ launch_timeout : int ,
75+ log_limit : int ,
76+ memory_limit : int ,
77+ ** kwds : dict [str , Any ],
78+ ) -> None :
3979 super ().__init__ (binary , launch_timeout , log_limit , memory_limit )
40- self ._monitor = None
4180 # app will not close itself on Android
4281 self .forced_close = True
4382 self .use_rr = False
4483
4584 for unsupported in ("pernosco" , "rr" , "valgrind" , "xvfb" ):
46- if kwds .pop (unsupported , False ):
47- LOG .warning ("ADBTarget ignoring %r : not supported" , unsupported )
85+ if kwds .pop (unsupported , None ):
86+ LOG .warning ("ADBTarget ignoring '%s' : not supported" , unsupported )
4887 if kwds :
4988 LOG .warning ("ADBTarget ignoring unsupported arguments: %s" , ", " .join (kwds ))
5089
5190 LOG .debug ("opening a session and setting up the environment" )
52- self . _session = ADBSession .create (as_root = True , max_attempts = 10 , retry_delay = 15 )
53- if self . _session is None :
91+ session = ADBSession .create (as_root = True , max_attempts = 10 , retry_delay = 15 )
92+ if session is None :
5493 raise RuntimeError ("Could not create ADB Session!" )
94+ self ._session = session
5595 self ._package = ADBSession .get_package_name (self .binary )
56- self ._prefs = None
96+ if self ._package is None :
97+ raise RuntimeError ("Could not find package name." )
98+ self ._prefs : Path | None = None
5799 self ._proc = ADBProcess (self ._package , self ._session )
58- self ._session .symbols [self ._package ] = os .path .join (
59- os .path .dirname (self .binary ), "symbols"
60- )
100+ self ._monitor = ADBMonitor (self ._proc )
101+ self ._session .symbols [self ._package ] = self .binary .parent / "symbols"
61102
62- def _cleanup (self ):
103+ def _cleanup (self ) -> None :
63104 with self ._lock :
64105 if self ._proc is not None :
65106 self ._proc .cleanup ()
66107 if self ._session .connected :
67108 self ._session .reverse_remove ()
68109 self ._session .disconnect ()
69- if self ._prefs and os . path . isfile ( self ._prefs ):
70- os . remove ( self ._prefs )
110+ if self ._prefs and self ._prefs . is_file ( ):
111+ self ._prefs . unlink ( )
71112
72- def close (self , force_close = False ):
113+ def close (self , force_close : bool = False ) -> None :
73114 with self ._lock :
74115 if self ._proc is not None :
75116 self ._proc .close ()
76117
77118 @property
78- def closed (self ):
119+ def closed (self ) -> bool :
79120 return self ._proc .reason is not None
80121
81- def check_result (self , _ignored ) :
122+ def check_result (self , _ignored : Iterable [ str ]) -> Result :
82123 status = Result .NONE
83124 if not self ._proc .is_healthy ():
84125 self ._proc .close ()
@@ -92,24 +133,26 @@ def check_result(self, _ignored):
92133 status = Result .FOUND
93134 return status
94135
95- def create_report (self , is_hang = False , unstable = False ):
136+ def create_report (self , is_hang : bool = False , unstable : bool = False ) -> Report :
96137 logs = Path (mkdtemp (prefix = "logs_" , dir = grz_tmp ("logs" )))
97138 self .save_logs (logs )
98139 return Report (logs , self .binary , is_hang = is_hang , unstable = unstable )
99140
100- def dump_coverage (self , timeout = 0 ) :
101- return NotImplementedError ("Not supported" )
141+ def dump_coverage (self , timeout : int = 0 ) -> None :
142+ raise NotImplementedError ()
102143
103- def handle_hang (self , ignore_idle = True , ignore_timeout = False ):
144+ def handle_hang (
145+ self , ignore_idle : bool = True , ignore_timeout : bool = False
146+ ) -> bool :
104147 # TODO: attempt to detect idle hangs?
105148 self .close ()
106149 return False
107150
108- def https (self ):
151+ def https (self ) -> bool :
109152 # HTTPS support is not currently supported
110153 return False
111154
112- def launch (self , location ) :
155+ def launch (self , location : str ) -> None :
113156 env_mod = dict (self .environ )
114157 # disabled external network connections during testing
115158 env_mod ["MOZ_IN_AUTOMATION" ] = "1"
@@ -133,66 +176,35 @@ def launch(self, location):
133176 raise TargetLaunchTimeout (str (exc )) from None
134177 raise TargetLaunchError (str (exc ), self .create_report ()) from None
135178
136- def log_size (self ):
179+ def log_size (self ) -> int :
137180 LOG .debug ("log_size not currently implemented" )
138181 return 0
139182
140- def merge_environment (self , extra ) :
183+ def merge_environment (self , extra : Mapping [ str , str ]) -> None :
141184 output = dict (extra )
142185 output .update (self .environ )
143186 output .update (merge_sanitizer_options (self .environ , extra = extra ))
144187 self .environ = output
145188
146189 @property
147- def monitor (self ):
148- if self ._monitor is None :
149-
150- class _ADBMonitor (TargetMonitor ):
151- # pylint: disable=no-self-argument,protected-access
152- def clone_log (_ , * _a , ** _k ): # pylint: disable=arguments-differ
153- log_file = self ._proc .clone_log ()
154- if log_file is None :
155- return None
156- try :
157- with open (log_file , "rb" ) as log_fp :
158- return log_fp .read ()
159- finally :
160- os .remove (log_file )
161-
162- def is_healthy (_ ):
163- return self ._proc .is_healthy ()
164-
165- def is_idle (self , threshold ):
166- return False
167-
168- def is_running (_ ):
169- return self ._proc .is_running ()
170-
171- @property
172- def launches (_ ):
173- return self ._proc .launches
174-
175- def log_length (_ , * _a ): # pylint: disable=arguments-differ
176- # TODO: This needs to be implemented
177- return 0
178-
179- self ._monitor = _ADBMonitor ()
190+ def monitor (self ) -> ADBMonitor :
180191 return self ._monitor
181192
182- def process_assets (self ):
193+ def process_assets (self ) -> None :
183194 self ._prefs = self .asset_mgr .get ("prefs" )
184195 # generate temporary prefs.js with prefpicker
185196 if self ._prefs is None :
186197 LOG .debug ("using prefpicker to generate prefs.js" )
187198 with TemporaryDirectory (dir = grz_tmp ("target" )) as tmp_path :
188199 prefs = Path (tmp_path ) / "prefs.js"
189200 template = PrefPicker .lookup_template ("browser-fuzzing.yml" )
201+ assert template is not None
190202 PrefPicker .load_template (template ).create_prefsjs (prefs )
191203 self ._prefs = self .asset_mgr .add ("prefs" , prefs , copy = False )
192204
193- def reverse (self , remote , local ) :
205+ def reverse (self , remote : int , local : int ) -> None :
194206 # remote->device, local->desktop
195207 self ._session .reverse (remote , local )
196208
197- def save_logs (self , dst ) :
209+ def save_logs (self , dst : Path ) -> None :
198210 self ._proc .save_logs (dst )
0 commit comments