| #!/usr/bin/env python |
| # |
| # Copyright (C) 2017 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. |
| # |
| |
| import os |
| import parse |
| import sys |
| |
| from abc import ABCMeta |
| from abc import abstractmethod |
| from ply import lex |
| from ply import yacc |
| from vts.utils.python.file import target_file_utils |
| |
| |
| def repeat_rule(to_repeat, zero_ok=False): |
| ''' |
| From a given rule, generates a rule that allows consecutive items |
| of that rule. Instances are collected in a list. |
| ''' |
| |
| def p_multiple(self, p): |
| if len(p) == 2 and zero_ok: |
| p[0] = [] |
| elif len(p) == 2: |
| p[0] = [p[1]] |
| else: |
| p[0] = p[1] + [p[2]] |
| |
| func = p_multiple |
| format_tuple = (to_repeat, to_repeat, to_repeat, 'empty' |
| if zero_ok else to_repeat) |
| func.__doc__ = '%ss : %ss %s \n| %s' % format_tuple |
| return func |
| |
| |
| def literal_token(tok): |
| ''' |
| A compact function to specify literal string tokens when. |
| they need to take precedence over a generic string, |
| Among these tokens precedence is decided in alphabetic order. |
| ''' |
| |
| def t_token(self, t): |
| return t |
| |
| func = t_token |
| func.__doc__ = tok |
| return func |
| |
| |
| class KernelProcFileTestBase(object): |
| """ |
| An abstract test for the formatting of a procfs file. Individual |
| files can inherit from this class. |
| |
| New parsing rules can be defined in the form of p_RULENAME, and |
| similarly new tokens can be defined as t_TOKENNAME. |
| |
| Child class should also specify a `start` variable to give the starting rule. |
| """ |
| |
| __metaclass__ = ABCMeta |
| |
| def t_HEX_LITERAL(self, t): |
| r'0x[a-f0-9]+' |
| t.value = int(t.value, 0) |
| return t |
| |
| def t_FLOAT(self, t): |
| r'([0-9]+[.][0-9]*|[0-9]*[.][0-9]+)' |
| t.value = float(t.value) |
| return t |
| |
| def t_NUMBER(self, t): |
| r'\d+' |
| t.value = int(t.value) |
| return t |
| |
| t_PATH = r'/[^\0]+' |
| t_COLON = r':' |
| t_EQUALS = r'=' |
| t_COMMA = r',' |
| t_STRING = r'[a-zA-Z\(\)_0-9\-@]+' |
| |
| t_TAB = r'\t' |
| t_SPACE = r'[ ]' |
| |
| def t_DASH(self, t): |
| r'\-' |
| return t |
| |
| def t_NEWLINE(self, t): |
| r'\n' |
| t.lexer.lineno += len(t.value) |
| return t |
| |
| t_ignore = '' |
| |
| def t_error(self, t): |
| raise SyntaxError("Illegal character '%s' in line %d '%s'" % \ |
| (t.value[0], t.lexer.lineno, t.value.split()[0])) |
| |
| p_SPACEs = repeat_rule('SPACE', zero_ok=True) |
| |
| def p_error(self, p): |
| raise SyntaxError("Parsing error at token %s in line %d" % |
| (p, p.lexer.lineno)) |
| |
| def p_empty(self, p): |
| 'empty :' |
| pass |
| |
| def __init__(self): |
| self.tokens = [ |
| t_name[2:] for t_name in dir(self) |
| if len(t_name) > 2 and t_name[:2] == 't_' |
| ] |
| self.tokens.remove('error') |
| self.tokens.remove('ignore') |
| self.lexer = lex.lex(module=self) |
| # (Change logger output stream if debugging) |
| self.parser = yacc.yacc(module=self, write_tables=False, \ |
| errorlog=yacc.PlyLogger(sys.stderr)) #open(os.devnull, 'w'))) |
| |
| def parse_line(self, rule, line, custom={}): |
| """Parse a line of text with the parse library. |
| |
| Args: |
| line: string, a line of text |
| rule: string, a format rule. See parse documentation |
| custom: dict, maps to custom type conversion functions |
| |
| Returns: |
| list, information parsed from the line |
| |
| Raises: |
| SyntaxError: if the line could not be parsed. |
| """ |
| parsed = parse.parse(rule, line, custom) |
| if parsed is None: |
| raise SyntaxError("Failed to parse line %s according to rule %s" % |
| (line, rule)) |
| return list(parsed) |
| |
| def parse_contents(self, file_contents): |
| """Using the internal parser, parse the contents. |
| |
| Args: |
| file_contents: string, entire contents of a file |
| |
| Returns: |
| list, a parsed representation of the file |
| |
| Raises: |
| SyntaxError: if the file could not be parsed |
| """ |
| return self.parser.parse(file_contents, lexer=self.lexer) |
| |
| @abstractmethod |
| def get_path(self): |
| """Returns the full path of this proc file (string).""" |
| pass |
| |
| def prepare_test(self, shell, dut): |
| """Performs any actions necessary before testing the proc file. |
| |
| Args: |
| shell: shell object, for preparation that requires device access |
| |
| Returns: |
| boolean, True if successful. |
| """ |
| return True |
| |
| def file_optional(self): |
| """Returns: True if file is allowed to be absent (boolean).""" |
| return False |
| |
| def result_correct(self, parse_result): |
| """Returns: True if the parsed result meets the requirements (boolean).""" |
| return True |
| |
| def test_format(self): |
| """Returns: |
| boolean, True if the file should be read and its format tested. |
| False if only the existence and permission should be tested. |
| """ |
| return True |
| |
| def get_permission_checker(self): |
| """Gets the function handle to use for validating file permissions. |
| |
| Return the function that will check if the permissions are correct. |
| By default, return the IsReadOnly function from target_file_utils. |
| |
| Returns: |
| function which takes one argument (the unix file permission bits |
| in octal format) and returns True if the permissions are correct, |
| False otherwise. |
| """ |
| return target_file_utils.IsReadOnly |