• 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_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