• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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