lin
2025-08-20 171e08343a9b6df6c31197f5b4800e8004800f5b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#!/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_PERIOD = 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, shell=None, dut=None):
        """Performs any actions necessary to return if file is allowed to be absent
 
        Args:
            shell: shell object, to run commands on the device side
            dut: AndroidDevice object to access functions and properties of that object
 
        Returns:
            boolean, True if file is allowed to be absent.
        """
        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