1#!/usr/bin/env python 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import os 19import parse 20import sys 21 22from abc import ABCMeta 23from abc import abstractmethod 24from ply import lex 25from ply import yacc 26from vts.utils.python.file import target_file_utils 27 28 29def repeat_rule(to_repeat, zero_ok=False): 30 ''' 31 From a given rule, generates a rule that allows consecutive items 32 of that rule. Instances are collected in a list. 33 ''' 34 35 def p_multiple(self, p): 36 if len(p) == 2 and zero_ok: 37 p[0] = [] 38 elif len(p) == 2: 39 p[0] = [p[1]] 40 else: 41 p[0] = p[1] + [p[2]] 42 43 func = p_multiple 44 format_tuple = (to_repeat, to_repeat, to_repeat, 'empty' 45 if zero_ok else to_repeat) 46 func.__doc__ = '%ss : %ss %s \n| %s' % format_tuple 47 return func 48 49 50def literal_token(tok): 51 ''' 52 A compact function to specify literal string tokens when. 53 they need to take precedence over a generic string, 54 Among these tokens precedence is decided in alphabetic order. 55 ''' 56 57 def t_token(self, t): 58 return t 59 60 func = t_token 61 func.__doc__ = tok 62 return func 63 64 65class KernelProcFileTestBase(object): 66 """ 67 An abstract test for the formatting of a procfs file. Individual 68 files can inherit from this class. 69 70 New parsing rules can be defined in the form of p_RULENAME, and 71 similarly new tokens can be defined as t_TOKENNAME. 72 73 Child class should also specify a `start` variable to give the starting rule. 74 """ 75 76 __metaclass__ = ABCMeta 77 78 def t_HEX_LITERAL(self, t): 79 r'0x[a-f0-9]+' 80 t.value = int(t.value, 0) 81 return t 82 83 def t_FLOAT(self, t): 84 r'([0-9]+[.][0-9]*|[0-9]*[.][0-9]+)' 85 t.value = float(t.value) 86 return t 87 88 def t_NUMBER(self, t): 89 r'\d+' 90 t.value = int(t.value) 91 return t 92 93 t_PATH = r'/[^\0]+' 94 t_COLON = r':' 95 t_EQUALS = r'=' 96 t_COMMA = r',' 97 t_PERIOD = r'\.' 98 t_STRING = r'[a-zA-Z\(\)_0-9\-@]+' 99 100 t_TAB = r'\t' 101 t_SPACE = r'[ ]' 102 103 def t_DASH(self, t): 104 r'\-' 105 return t 106 107 def t_NEWLINE(self, t): 108 r'\n' 109 t.lexer.lineno += len(t.value) 110 return t 111 112 t_ignore = '' 113 114 def t_error(self, t): 115 raise SyntaxError("Illegal character '%s' in line %d '%s'" % \ 116 (t.value[0], t.lexer.lineno, t.value.split()[0])) 117 118 p_SPACEs = repeat_rule('SPACE', zero_ok=True) 119 120 def p_error(self, p): 121 raise SyntaxError("Parsing error at token %s in line %d" % 122 (p, p.lexer.lineno)) 123 124 def p_empty(self, p): 125 'empty :' 126 pass 127 128 def __init__(self): 129 self.tokens = [ 130 t_name[2:] for t_name in dir(self) 131 if len(t_name) > 2 and t_name[:2] == 't_' 132 ] 133 self.tokens.remove('error') 134 self.tokens.remove('ignore') 135 self.lexer = lex.lex(module=self) 136 # (Change logger output stream if debugging) 137 self.parser = yacc.yacc(module=self, write_tables=False, \ 138 errorlog=yacc.PlyLogger(sys.stderr)) #open(os.devnull, 'w'))) 139 140 def set_api_level(self, dut): 141 self.api_level = dut.getLaunchApiLevel(strict=False) 142 143 def parse_line(self, rule, line, custom={}): 144 """Parse a line of text with the parse library. 145 146 Args: 147 line: string, a line of text 148 rule: string, a format rule. See parse documentation 149 custom: dict, maps to custom type conversion functions 150 151 Returns: 152 list, information parsed from the line 153 154 Raises: 155 SyntaxError: if the line could not be parsed. 156 """ 157 parsed = parse.parse(rule, line, custom) 158 if parsed is None: 159 raise SyntaxError("Failed to parse line %s according to rule %s" % 160 (line, rule)) 161 return list(parsed) 162 163 def parse_contents(self, file_contents): 164 """Using the internal parser, parse the contents. 165 166 Args: 167 file_contents: string, entire contents of a file 168 169 Returns: 170 list, a parsed representation of the file 171 172 Raises: 173 SyntaxError: if the file could not be parsed 174 """ 175 return self.parser.parse(file_contents, lexer=self.lexer) 176 177 @abstractmethod 178 def get_path(self): 179 """Returns the full path of this proc file (string).""" 180 pass 181 182 def prepare_test(self, shell, dut): 183 """Performs any actions necessary before testing the proc file. 184 185 Args: 186 shell: shell object, for preparation that requires device access 187 188 Returns: 189 boolean, True if successful. 190 """ 191 return True 192 193 def file_optional(self, shell=None, dut=None): 194 """Performs any actions necessary to return if file is allowed to be absent 195 196 Args: 197 shell: shell object, to run commands on the device side 198 dut: AndroidDevice object to access functions and properties of that object 199 200 Returns: 201 boolean, True if file is allowed to be absent. 202 """ 203 return False 204 205 def result_correct(self, parse_result): 206 """Returns: True if the parsed result meets the requirements (boolean).""" 207 return True 208 209 def test_format(self): 210 """Returns: 211 boolean, True if the file should be read and its format tested. 212 False if only the existence and permission should be tested. 213 """ 214 return True 215 216 def get_permission_checker(self): 217 """Gets the function handle to use for validating file permissions. 218 219 Return the function that will check if the permissions are correct. 220 By default, return the IsReadOnly function from target_file_utils. 221 222 Returns: 223 function which takes one argument (the unix file permission bits 224 in octal format) and returns True if the permissions are correct, 225 False otherwise. 226 """ 227 return target_file_utils.IsReadOnly 228