• 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 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" % (line, rule))
156        return list(parsed)
157
158    def parse_contents(self, file_contents):
159        """Using the internal parser, parse the contents.
160
161        Args:
162            file_contents: string, entire contents of a file
163
164        Returns:
165            list, a parsed representation of the file
166
167        Raises:
168            SyntaxError: if the file could not be parsed
169        """
170        return self.parser.parse(file_contents, lexer=self.lexer)
171
172    @abstractmethod
173    def get_path(self):
174        """Returns the full path of this proc file (string)."""
175        pass
176
177    def prepare_test(self, shell):
178        """Performs any actions necessary before testing the proc file.
179
180        Args:
181            shell: shell object, for preparation that requires device access
182
183        Returns:
184            boolean, True if successful.
185        """
186        return True
187
188    def result_correct(self, parse_result):
189        """Returns: True if the parsed result meets the requirements (boolean)."""
190        return True
191
192    def test_format(self):
193        """Returns:
194            boolean, True if the file should be read and its format tested.
195                     False if only the existence and permission should be tested.
196        """
197        return True
198
199    def get_permission_checker(self):
200        """Gets the function handle to use for validating file permissions.
201
202        Return the function that will check if the permissions are correct.
203        By default, return the IsReadOnly function from file_utils.
204
205        Returns:
206            function which takes one argument (the unix file permission bits
207            in octal format) and returns True if the permissions are correct,
208            False otherwise.
209        """
210        return file_utils.IsReadOnly
211