|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright 2019, The Android Open Source Project | 
|  | # | 
|  | # 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. | 
|  |  | 
|  | """Helper util libraries for parsing logcat logs.""" | 
|  |  | 
|  | import asyncio | 
|  | import re | 
|  | from datetime import datetime | 
|  | from typing import Optional, Pattern | 
|  |  | 
|  | # local import | 
|  | import lib.print_utils as print_utils | 
|  |  | 
|  | def parse_logcat_datetime(timestamp: str) -> Optional[datetime]: | 
|  | """Parses the timestamp of logcat. | 
|  |  | 
|  | Params: | 
|  | timestamp: for example "2019-07-01 16:13:55.221". | 
|  |  | 
|  | Returns: | 
|  | a datetime of timestamp with the year now. | 
|  | """ | 
|  | try: | 
|  | # Match the format of logcat. For example: "2019-07-01 16:13:55.221", | 
|  | # because it doesn't have year, set current year to it. | 
|  | timestamp = datetime.strptime(timestamp, | 
|  | '%Y-%m-%d %H:%M:%S.%f') | 
|  | return timestamp | 
|  | except ValueError as ve: | 
|  | print_utils.debug_print('Invalid line: ' + timestamp) | 
|  | return None | 
|  |  | 
|  | def _is_time_out(timeout: datetime, line: str) -> bool: | 
|  | """Checks if the timestamp of this line exceeds the timeout. | 
|  |  | 
|  | Returns: | 
|  | true if the timestamp exceeds the timeout. | 
|  | """ | 
|  | # Get the timestampe string. | 
|  | cur_timestamp_str = ' '.join(re.split(r'\s+', line)[0:2]) | 
|  | timestamp = parse_logcat_datetime(cur_timestamp_str) | 
|  | if not timestamp: | 
|  | return False | 
|  |  | 
|  | return timestamp > timeout | 
|  |  | 
|  | async def _blocking_wait_for_logcat_pattern(timestamp: datetime, | 
|  | pattern: Pattern, | 
|  | timeout: datetime) -> Optional[str]: | 
|  | # Show the year in the timestampe. | 
|  | logcat_cmd = 'adb logcat -v UTC -v year -v threadtime -T'.split() | 
|  | logcat_cmd.append(str(timestamp)) | 
|  | print_utils.debug_print('[LOGCAT]:' + ' '.join(logcat_cmd)) | 
|  |  | 
|  | # Create subprocess | 
|  | process = await asyncio.create_subprocess_exec( | 
|  | *logcat_cmd, | 
|  | # stdout must a pipe to be accessible as process.stdout | 
|  | stdout=asyncio.subprocess.PIPE) | 
|  |  | 
|  | while (True): | 
|  | # Read one line of output. | 
|  | data = await process.stdout.readline() | 
|  | line = data.decode('utf-8').rstrip() | 
|  |  | 
|  | # 2019-07-01 14:54:21.946 27365 27392 I ActivityTaskManager: Displayed | 
|  | # com.android.settings/.Settings: +927ms | 
|  | # TODO: Detect timeouts even when there is no logcat output. | 
|  | if _is_time_out(timeout, line): | 
|  | print_utils.debug_print('DID TIMEOUT BEFORE SEEING ANYTHING (' | 
|  | 'timeout={timeout} seconds << {pattern} ' | 
|  | '>>'.format(timeout=timeout, pattern=pattern)) | 
|  | return None | 
|  |  | 
|  | if pattern.match(line): | 
|  | print_utils.debug_print( | 
|  | 'WE DID SEE PATTERN << "{}" >>.'.format(pattern)) | 
|  | return line | 
|  |  | 
|  | def blocking_wait_for_logcat_pattern(timestamp: datetime, | 
|  | pattern: Pattern, | 
|  | timeout: datetime) -> Optional[str]: | 
|  | """Selects the line that matches the pattern and within the timeout. | 
|  |  | 
|  | Returns: | 
|  | the line that matches the pattern and within the timeout. | 
|  | """ | 
|  | loop = asyncio.get_event_loop() | 
|  | result = loop.run_until_complete( | 
|  | _blocking_wait_for_logcat_pattern(timestamp, pattern, timeout)) | 
|  | return result |