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 parse_line(self, rule, line, custom={}): 141 """Parse a line of text with the parse library. 142 143 Args: 144 line: string, a line of text 145 rule: string, a format rule. See parse documentation 146 custom: dict, maps to custom type conversion functions 147 148 Returns: 149 list, information parsed from the line 150 151 Raises: 152 SyntaxError: if the line could not be parsed. 153 """ 154 parsed = parse.parse(rule, line, custom) 155 if parsed is None: 156 raise SyntaxError("Failed to parse line %s according to rule %s" % 157 (line, rule)) 158 return list(parsed) 159 160 def parse_contents(self, file_contents): 161 """Using the internal parser, parse the contents. 162 163 Args: 164 file_contents: string, entire contents of a file 165 166 Returns: 167 list, a parsed representation of the file 168 169 Raises: 170 SyntaxError: if the file could not be parsed 171 """ 172 return self.parser.parse(file_contents, lexer=self.lexer) 173 174 @abstractmethod 175 def get_path(self): 176 """Returns the full path of this proc file (string).""" 177 pass 178 179 def prepare_test(self, shell, dut): 180 """Performs any actions necessary before testing the proc file. 181 182 Args: 183 shell: shell object, for preparation that requires device access 184 185 Returns: 186 boolean, True if successful. 187 """ 188 return True 189 190 def file_optional(self, shell=None, dut=None): 191 """Performs any actions necessary to return if file is allowed to be absent 192 193 Args: 194 shell: shell object, to run commands on the device side 195 dut: AndroidDevice object to access functions and properties of that object 196 197 Returns: 198 boolean, True if file is allowed to be absent. 199 """ 200 return False 201 202 def result_correct(self, parse_result): 203 """Returns: True if the parsed result meets the requirements (boolean).""" 204 return True 205 206 def test_format(self): 207 """Returns: 208 boolean, True if the file should be read and its format tested. 209 False if only the existence and permission should be tested. 210 """ 211 return True 212 213 def get_permission_checker(self): 214 """Gets the function handle to use for validating file permissions. 215 216 Return the function that will check if the permissions are correct. 217 By default, return the IsReadOnly function from target_file_utils. 218 219 Returns: 220 function which takes one argument (the unix file permission bits 221 in octal format) and returns True if the permissions are correct, 222 False otherwise. 223 """ 224 return target_file_utils.IsReadOnly 225