287 lines
11 KiB
Python
287 lines
11 KiB
Python
#!/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 logging
|
|
import re
|
|
import uuid
|
|
|
|
from vts.runners.host import asserts
|
|
from vts.runners.host import base_test
|
|
from vts.runners.host import const
|
|
from vts.runners.host import test_runner
|
|
from vts.utils.python.controllers import android_device
|
|
from vts.utils.python.file import target_file_utils
|
|
|
|
|
|
class KernelApiSysfsTest(base_test.BaseTestClass):
|
|
'''Test cases which check sysfs files.'''
|
|
|
|
def setUpClass(self):
|
|
self.dut = self.android_devices[0]
|
|
self.shell = self.dut.shell
|
|
|
|
def ConvertToInteger(self, text):
|
|
'''Check whether a given text is interger.
|
|
|
|
Args:
|
|
text: object, usually a string representing the content of a file
|
|
|
|
Returns:
|
|
bool, True if is integer
|
|
'''
|
|
try:
|
|
return int(text)
|
|
except ValueError as e:
|
|
logging.exception(e)
|
|
asserts.fail('Content "%s" is not integer' % text)
|
|
|
|
def MatchRegex(self, regex, string):
|
|
'''Check whether a string completely matches a given regex.
|
|
|
|
Assertions will fail if given string is not a complete match.
|
|
|
|
Args:
|
|
regex: string, regex pattern to match
|
|
string: string, given string for matching
|
|
'''
|
|
pattern = re.compile(regex)
|
|
match = pattern.match(string)
|
|
message = 'String "%s" is not a complete match of regex "%s".' % (
|
|
string, regex)
|
|
asserts.assertTrue(match is not None, message)
|
|
asserts.assertEqual(match.start(), 0, message)
|
|
asserts.assertEqual(match.end(), len(string), message)
|
|
|
|
def GetPathPermission(self, path, assert_if_absent):
|
|
'''Get the permission bits of a path, catching IOError.'''
|
|
permission = ''
|
|
try:
|
|
permission = target_file_utils.GetPermission(path, self.shell)
|
|
except IOError as e:
|
|
if not assert_if_absent:
|
|
return None
|
|
logging.exception(e)
|
|
asserts.fail('Path "%s" does not exist or has invalid '
|
|
'permission bits' % path)
|
|
return permission
|
|
|
|
def IsReadOnly(self, path, assert_if_absent=True):
|
|
'''Check whether a given path is read only.
|
|
|
|
Assertion will fail if given path does not exist or is not read only.
|
|
'''
|
|
permission = self.GetPathPermission(path, assert_if_absent)
|
|
if permission is None and not assert_if_absent:
|
|
return
|
|
asserts.assertTrue(target_file_utils.IsReadOnly(permission),
|
|
'path %s is not read only' % path)
|
|
|
|
def IsReadWrite(self, path, assert_if_absent=True):
|
|
'''Check whether a given path is read-write.
|
|
|
|
Assertion will fail if given path does not exist or is not read-write.
|
|
'''
|
|
permission = self.GetPathPermission(path, assert_if_absent)
|
|
if permission is None and not assert_if_absent:
|
|
return
|
|
asserts.assertTrue(target_file_utils.IsReadWrite(permission),
|
|
'path %s is not read write' % path)
|
|
|
|
def tryReadFileContent(self, f, shell):
|
|
'''Attempt to read a file.
|
|
|
|
If the file does not exist None will be returned.
|
|
'''
|
|
try:
|
|
content = target_file_utils.ReadFileContent(f, self.shell)
|
|
except IOError as e:
|
|
return None
|
|
return content
|
|
|
|
def testAndroidUSB(self):
|
|
'''Check for the existence of required files in /sys/class/android_usb.
|
|
'''
|
|
state = '/sys/class/android_usb/android0/state'
|
|
self.IsReadOnly(state)
|
|
contents = target_file_utils.ReadFileContent(state, self.shell).strip()
|
|
asserts.assertTrue(contents in
|
|
['DISCONNECTED', 'CONNECTED', 'CONFIGURED'],
|
|
'%s does not contain an expected string' % state)
|
|
|
|
def testCpuOnlineFormat(self):
|
|
'''Check the format of cpu online file.
|
|
|
|
Confirm /sys/devices/system/cpu/online exists and is read-only.
|
|
Parse contents to ensure it is a comma-separated series of ranges
|
|
(%d-%d) and/or integers.
|
|
'''
|
|
filepath = '/sys/devices/system/cpu/online'
|
|
self.IsReadOnly(filepath)
|
|
content = target_file_utils.ReadFileContent(filepath, self.shell)
|
|
regex = '(\d+(-\d+)?)(,\d+(-\d+)?)*'
|
|
if content.endswith('\n'):
|
|
content = content[:-1]
|
|
self.MatchRegex(regex, content)
|
|
|
|
def testPerCpuCpufreq(self):
|
|
'''Check each cpu's scaling_cur_freq, scaling_min_freq, scaling_max_freq,
|
|
scaling_available_frequencies, and time_in_state files.
|
|
'''
|
|
f = '/sys/devices/system/cpu/present'
|
|
self.IsReadOnly(f)
|
|
present_cpus = target_file_utils.ReadFileContent(f, self.shell)
|
|
cpu_ranges = present_cpus.split(',')
|
|
cpu_list = []
|
|
|
|
for r in cpu_ranges:
|
|
m = re.match(r'(\d+)(-\d+)?', r)
|
|
asserts.assertTrue(m is not None,
|
|
'malformatted range in /sys/devices/system/cpu/present')
|
|
start_cpu = int(m.group(1))
|
|
if m.group(2) is None:
|
|
end_cpu = start_cpu
|
|
else:
|
|
end_cpu = int(m.group(2)[1:])
|
|
cpu_list += range(start_cpu, end_cpu+1)
|
|
|
|
for cpu in cpu_list:
|
|
f = '/sys/devices/system/cpu/cpu%s/cpufreq/scaling_cur_freq' % cpu
|
|
self.IsReadOnly(f, False)
|
|
content = self.tryReadFileContent(f, self.shell)
|
|
if content is not None:
|
|
self.ConvertToInteger(content)
|
|
|
|
f = '/sys/devices/system/cpu/cpu%s/cpufreq/scaling_min_freq' % cpu
|
|
self.IsReadWrite(f, False)
|
|
content = self.tryReadFileContent(f, self.shell)
|
|
if content is not None:
|
|
self.ConvertToInteger(content)
|
|
|
|
f = '/sys/devices/system/cpu/cpu%s/cpufreq/scaling_max_freq' % cpu
|
|
self.IsReadWrite(f, False)
|
|
content = self.tryReadFileContent(f, self.shell)
|
|
if content is not None:
|
|
self.ConvertToInteger(content)
|
|
|
|
f = '/sys/devices/system/cpu/cpu%s/cpufreq/scaling_available_frequencies' % cpu
|
|
self.IsReadOnly(f, False)
|
|
content = self.tryReadFileContent(f, self.shell)
|
|
if content is not None:
|
|
content = content.rstrip()
|
|
avail_freqs = content.split(' ')
|
|
for x in avail_freqs:
|
|
self.ConvertToInteger(x)
|
|
|
|
f = '/sys/devices/system/cpu/cpu%s/cpufreq/stats/time_in_state' % cpu
|
|
self.IsReadOnly(f, False)
|
|
content = self.tryReadFileContent(f, self.shell)
|
|
if content is not None:
|
|
for line in content:
|
|
values = line.split()
|
|
for v in values:
|
|
try:
|
|
unused = int(v)
|
|
except ValueError as e:
|
|
asserts.fail("Malformatted time_in_state file at %s" % f)
|
|
|
|
def testLastResumeReason(self):
|
|
'''Check /sys/kernel/wakeup_reasons/last_resume_reason.'''
|
|
filepath = '/sys/kernel/wakeup_reasons/last_resume_reason'
|
|
self.IsReadOnly(filepath)
|
|
|
|
def testKernelMax(self):
|
|
'''Check the value of /sys/devices/system/cpu/kernel_max.'''
|
|
filepath = '/sys/devices/system/cpu/kernel_max'
|
|
self.IsReadOnly(filepath)
|
|
content = target_file_utils.ReadFileContent(filepath, self.shell)
|
|
self.ConvertToInteger(content)
|
|
|
|
def testNetMTU(self):
|
|
'''Check for /sys/class/net/*/mtu.'''
|
|
dirlist = target_file_utils.FindFiles(self.shell, '/sys/class/net',
|
|
'*', '-maxdepth 1 -type l')
|
|
for entry in dirlist:
|
|
mtufile = entry + "/mtu"
|
|
self.IsReadWrite(mtufile)
|
|
content = target_file_utils.ReadFileContent(mtufile, self.shell)
|
|
self.ConvertToInteger(content)
|
|
|
|
def testRtcHctosys(self):
|
|
'''If RTC is present, check that at least one rtc exists with hctosys = 1.'''
|
|
rtcs = target_file_utils.FindFiles(self.shell, '/dev',
|
|
'rtc*', '-maxdepth 1')
|
|
if not rtcs:
|
|
return
|
|
|
|
rtclist = target_file_utils.FindFiles(self.shell, '/sys/class/rtc',
|
|
'rtc*', '-maxdepth 1 -type l')
|
|
for entry in rtclist:
|
|
content = target_file_utils.ReadFileContent(entry + "/hctosys",
|
|
self.shell)
|
|
try:
|
|
hctosys = int(content)
|
|
except ValueError as e:
|
|
continue
|
|
if hctosys == 1:
|
|
return
|
|
asserts.fail("No RTC with hctosys=1 present")
|
|
|
|
def testWakeLock(self):
|
|
'''Check that locking and unlocking a wake lock works.'''
|
|
_WAKE_LOCK_PATH = '/sys/power/wake_lock'
|
|
_WAKE_UNLOCK_PATH = '/sys/power/wake_unlock'
|
|
lock_name = 'KernelApiSysfsTestWakeLock' + uuid.uuid4().hex
|
|
|
|
# Enable wake lock
|
|
self.shell.Execute('echo %s > %s' % (lock_name, _WAKE_LOCK_PATH))
|
|
|
|
# Confirm wake lock is enabled
|
|
results = self.shell.Execute('cat %s' % _WAKE_LOCK_PATH)
|
|
active_sources = results[const.STDOUT][0].split()
|
|
asserts.assertTrue(lock_name in active_sources,
|
|
'active wake lock not reported in %s' % _WAKE_LOCK_PATH)
|
|
|
|
# Disable wake lock
|
|
self.shell.Execute('echo %s > %s' % (lock_name, _WAKE_UNLOCK_PATH))
|
|
|
|
# Confirm wake lock is no longer enabled
|
|
results = self.shell.Execute('cat %s' % _WAKE_LOCK_PATH)
|
|
active_sources = results[const.STDOUT][0].split()
|
|
asserts.assertTrue(lock_name not in active_sources,
|
|
'inactive wake lock reported in %s' % _WAKE_LOCK_PATH)
|
|
results = self.shell.Execute('cat %s' % _WAKE_UNLOCK_PATH)
|
|
inactive_sources = results[const.STDOUT][0].split()
|
|
asserts.assertTrue(lock_name in inactive_sources,
|
|
'inactive wake lock not reported in %s' % _WAKE_UNLOCK_PATH)
|
|
|
|
def testWakeupCount(self):
|
|
filepath = '/sys/power/wakeup_count'
|
|
self.IsReadWrite(filepath)
|
|
|
|
def testSysPowerState(self):
|
|
'''/sys/power/state controls the system sleep states.'''
|
|
filepath = '/sys/power/state'
|
|
self.IsReadWrite(filepath)
|
|
content = target_file_utils.ReadFileContent(filepath, self.shell)
|
|
allowed_states = ['freeze', 'mem', 'disk', 'standby']
|
|
for state in content.split():
|
|
if state not in allowed_states:
|
|
asserts.fail("Invalid system power state: %s" % state)
|
|
|
|
if __name__ == "__main__":
|
|
test_runner.main()
|