Home | History | Annotate | Download | only in proc
      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 
     18 import os
     19 import parse
     20 import sys
     21 
     22 from abc import ABCMeta
     23 from abc import abstractmethod
     24 from ply import lex
     25 from ply import yacc
     26 from vts.utils.python.file import target_file_utils
     27 
     28 
     29 def 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 
     50 def 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 
     65 class 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_STRING = r'[a-zA-Z\(\)_0-9\-@]+'
     98 
     99     t_TAB = r'\t'
    100     t_SPACE = r'[ ]'
    101 
    102     def t_DASH(self, t):
    103         r'\-'
    104         return t
    105 
    106     def t_NEWLINE(self, t):
    107         r'\n'
    108         t.lexer.lineno += len(t.value)
    109         return t
    110 
    111     t_ignore = ''
    112 
    113     def t_error(self, t):
    114         raise SyntaxError("Illegal character '%s' in line %d '%s'" % \
    115                 (t.value[0], t.lexer.lineno, t.value.split()[0]))
    116 
    117     p_SPACEs = repeat_rule('SPACE', zero_ok=True)
    118 
    119     def p_error(self, p):
    120         raise SyntaxError("Parsing error at token %s in line %d" %
    121                           (p, p.lexer.lineno))
    122 
    123     def p_empty(self, p):
    124         'empty :'
    125         pass
    126 
    127     def __init__(self):
    128         self.tokens = [
    129             t_name[2:] for t_name in dir(self)
    130             if len(t_name) > 2 and t_name[:2] == 't_'
    131         ]
    132         self.tokens.remove('error')
    133         self.tokens.remove('ignore')
    134         self.lexer = lex.lex(module=self)
    135         # (Change logger output stream if debugging)
    136         self.parser = yacc.yacc(module=self, write_tables=False, \
    137                 errorlog=yacc.PlyLogger(sys.stderr)) #open(os.devnull, 'w')))
    138 
    139     def parse_line(self, rule, line, custom={}):
    140         """Parse a line of text with the parse library.
    141 
    142         Args:
    143             line: string, a line of text
    144             rule: string, a format rule. See parse documentation
    145             custom: dict, maps to custom type conversion functions
    146 
    147         Returns:
    148             list, information parsed from the line
    149 
    150         Raises:
    151             SyntaxError: if the line could not be parsed.
    152         """
    153         parsed = parse.parse(rule, line, custom)
    154         if parsed is None:
    155             raise SyntaxError("Failed to parse line %s according to rule %s" %
    156                               (line, rule))
    157         return list(parsed)
    158 
    159     def parse_contents(self, file_contents):
    160         """Using the internal parser, parse the contents.
    161 
    162         Args:
    163             file_contents: string, entire contents of a file
    164 
    165         Returns:
    166             list, a parsed representation of the file
    167 
    168         Raises:
    169             SyntaxError: if the file could not be parsed
    170         """
    171         return self.parser.parse(file_contents, lexer=self.lexer)
    172 
    173     @abstractmethod
    174     def get_path(self):
    175         """Returns the full path of this proc file (string)."""
    176         pass
    177 
    178     def prepare_test(self, shell):
    179         """Performs any actions necessary before testing the proc file.
    180 
    181         Args:
    182             shell: shell object, for preparation that requires device access
    183 
    184         Returns:
    185             boolean, True if successful.
    186         """
    187         return True
    188 
    189     def result_correct(self, parse_result):
    190         """Returns: True if the parsed result meets the requirements (boolean)."""
    191         return True
    192 
    193     def test_format(self):
    194         """Returns:
    195             boolean, True if the file should be read and its format tested.
    196                      False if only the existence and permission should be tested.
    197         """
    198         return True
    199 
    200     def get_permission_checker(self):
    201         """Gets the function handle to use for validating file permissions.
    202 
    203         Return the function that will check if the permissions are correct.
    204         By default, return the IsReadOnly function from target_file_utils.
    205 
    206         Returns:
    207             function which takes one argument (the unix file permission bits
    208             in octal format) and returns True if the permissions are correct,
    209             False otherwise.
    210         """
    211         return target_file_utils.IsReadOnly
    212