Skip to content

Commit 159410b

Browse files
committed
Move greentea tests closure to library
1 parent 993ed3b commit 159410b

File tree

90 files changed

+1044
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1044
-0
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
Copyright (c) 2018-2019 Arm Limited and affiliates.
3+
SPDX-License-Identifier: Apache-2.0
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+
import time
18+
from mbed_host_tests import BaseHostTest
19+
20+
DEFAULT_SYNC_DELAY = 4.0
21+
22+
MSG_VALUE_WATCHDOG_PRESENT = 1
23+
MSG_VALUE_DUMMY = '0'
24+
MSG_VALUE_RESET_REASON_GET = 'get'
25+
MSG_VALUE_RESET_REASON_CLEAR = 'clear'
26+
MSG_VALUE_DEVICE_RESET_NVIC = 'nvic'
27+
MSG_VALUE_DEVICE_RESET_WATCHDOG = 'watchdog'
28+
29+
MSG_KEY_DEVICE_READY = 'ready'
30+
MSG_KEY_RESET_REASON_RAW = 'reason_raw'
31+
MSG_KEY_RESET_REASON = 'reason'
32+
MSG_KEY_DEVICE_RESET = 'reset'
33+
MSG_KEY_SYNC = '__sync'
34+
MSG_KEY_RESET_COMPLETE = 'reset_complete'
35+
36+
RESET_REASONS = {
37+
'POWER_ON': '0',
38+
'PIN_RESET': '1',
39+
'BROWN_OUT': '2',
40+
'SOFTWARE': '3',
41+
'WATCHDOG': '4',
42+
'LOCKUP': '5',
43+
'WAKE_LOW_POWER': '6',
44+
'ACCESS_ERROR': '7',
45+
'BOOT_ERROR': '8',
46+
'MULTIPLE': '9',
47+
'PLATFORM': '10',
48+
'UNKNOWN': '11'
49+
}
50+
51+
52+
def raise_if_different(expected, actual, text=''):
53+
"""Raise a RuntimeError if actual is different than expected."""
54+
if expected != actual:
55+
raise RuntimeError('{}Got {!r}, expected {!r}'
56+
.format(text, actual, expected))
57+
58+
59+
class ResetReasonTest(BaseHostTest):
60+
"""Test for the Reset Reason HAL API.
61+
62+
Given a device supporting a Reset Reason API.
63+
When the device is restarted using various methods.
64+
Then the device returns a correct reset reason for every restart.
65+
"""
66+
67+
def __init__(self):
68+
super(ResetReasonTest, self).__init__()
69+
self.device_reasons = None
70+
self.device_has_watchdog = None
71+
self.raw_reset_reasons = set()
72+
self.sync_delay = DEFAULT_SYNC_DELAY
73+
self.test_steps_sequence = self.test_steps()
74+
# Advance the coroutine to it's first yield statement.
75+
self.test_steps_sequence.send(None)
76+
77+
def setup(self):
78+
sync_delay = self.get_config_item('forced_reset_timeout')
79+
self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY
80+
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
81+
self.register_callback(MSG_KEY_RESET_REASON_RAW, self.cb_reset_reason_raw)
82+
self.register_callback(MSG_KEY_RESET_REASON, self.cb_reset_reason)
83+
self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_reset_reason)
84+
self.register_callback(MSG_KEY_RESET_COMPLETE, self.cb_reset_reason)
85+
86+
def cb_device_ready(self, key, value, timestamp):
87+
"""Request a raw value of the reset_reason register.
88+
89+
Additionally, save the device's reset_reason capabilities
90+
and the watchdog status on the first call.
91+
"""
92+
if self.device_reasons is None:
93+
reasons, wdg_status = (int(i, base=16) for i in value.split(','))
94+
self.device_has_watchdog = (wdg_status == MSG_VALUE_WATCHDOG_PRESENT)
95+
self.device_reasons = [k for k, v in RESET_REASONS.items() if (reasons & 1 << int(v))]
96+
self.send_kv(MSG_KEY_RESET_REASON_RAW, MSG_VALUE_RESET_REASON_GET)
97+
98+
def cb_reset_reason_raw(self, key, value, timestamp):
99+
"""Verify that the raw reset_reason register value is unique.
100+
101+
Fail the test suite if the raw reset_reason value is not unique.
102+
Request a platform independent reset_reason otherwise.
103+
"""
104+
if value in self.raw_reset_reasons:
105+
self.log('TEST FAILED: The raw reset reason is not unique. '
106+
'{!r} is already present in {!r}.'
107+
.format(value, self.raw_reset_reasons))
108+
self.notify_complete(False)
109+
else:
110+
self.raw_reset_reasons.add(value)
111+
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_GET)
112+
113+
def cb_reset_reason(self, key, value, timestamp):
114+
"""Feed the test_steps coroutine with reset_reason value.
115+
116+
Pass the test suite if the coroutine yields True.
117+
Fail the test suite if the iterator stops or raises a RuntimeError.
118+
"""
119+
try:
120+
if self.test_steps_sequence.send(value):
121+
self.notify_complete(True)
122+
except (StopIteration, RuntimeError) as exc:
123+
self.log('TEST FAILED: {}'.format(exc))
124+
self.notify_complete(False)
125+
126+
def test_steps(self):
127+
"""Generate a sequence of test steps.
128+
129+
This coroutine calls yield to wait for the input from the device
130+
(the reset_reason). If the device gives the wrong response, the
131+
generator raises a RuntimeError exception and fails the test.
132+
"""
133+
# Ignore the first reason.
134+
__ignored_reset_reason = yield
135+
self.raw_reset_reasons.clear()
136+
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
137+
__ignored_clear_ack = yield
138+
139+
# Request a NVIC_SystemReset() call.
140+
expected_reason = 'SOFTWARE'
141+
if expected_reason not in self.device_reasons:
142+
self.log('Skipping the {} reset reason -- not supported.'.format(expected_reason))
143+
else:
144+
# Request a NVIC_SystemReset() call.
145+
self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_NVIC)
146+
__ignored_reset_ack = yield
147+
time.sleep(self.sync_delay)
148+
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
149+
reset_reason = yield
150+
raise_if_different(RESET_REASONS[expected_reason], reset_reason, 'Wrong reset reason. ')
151+
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
152+
__ignored_clear_ack = yield
153+
154+
# Reset the device using DAP.
155+
expected_reason = 'PIN_RESET'
156+
if expected_reason not in self.device_reasons:
157+
self.log('Skipping the {} reset reason -- not supported.'.format(expected_reason))
158+
else:
159+
self.reset()
160+
__ignored_reset_ack = yield # 'reset_complete'
161+
time.sleep(self.sync_delay)
162+
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
163+
reset_reason = yield
164+
raise_if_different(RESET_REASONS[expected_reason], reset_reason, 'Wrong reset reason. ')
165+
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
166+
__ignored_clear_ack = yield
167+
168+
# Start a watchdog timer and wait for it to reset the device.
169+
expected_reason = 'WATCHDOG'
170+
if expected_reason not in self.device_reasons or not self.device_has_watchdog:
171+
self.log('Skipping the {} reset reason -- not supported.'.format(expected_reason))
172+
else:
173+
self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_WATCHDOG)
174+
__ignored_reset_ack = yield
175+
time.sleep(self.sync_delay)
176+
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
177+
reset_reason = yield
178+
raise_if_different(RESET_REASONS[expected_reason], reset_reason, 'Wrong reset reason. ')
179+
180+
# The sequence is correct -- test passed.
181+
yield True
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""
2+
Copyright (c) 2011-2020, Arm Limited and affiliates
3+
SPDX-License-Identifier: Apache-2.0
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+
18+
from mbed_host_tests import BaseHostTest
19+
import time
20+
import calendar
21+
import datetime
22+
23+
class RTC_time_calc_test(BaseHostTest):
24+
"""
25+
This is the host part of the test to verify if:
26+
- _rtc_mktime function converts a calendar time into time since UNIX epoch as a time_t,
27+
- _rtc_localtime function converts a given time in seconds since epoch into calendar time.
28+
29+
The same algoritm to generate next calendar time to be tested is used by both parts of the test.
30+
We will check if correct time since UNIX epoch is calculated for the first and the last day
31+
of each month and across valid years.
32+
33+
Mbed part of the test sends calculated time since UNIX epoch.
34+
This part validates given value and responds to indicate pass or fail.
35+
Additionally it sends also encoded day of week and day of year which
36+
will be needed to verify _rtc_localtime.
37+
38+
Support for both types of RTC devices is provided:
39+
- RTCs which handles all leap years in the mentioned year range correctly. Leap year is determined by checking if
40+
the year counter value is divisible by 400, 100, and 4. No problem here.
41+
- RTCs which handles leap years correctly up to 2100. The RTC does a simple bit comparison to see if the two
42+
lowest order bits of the year counter are zero. In this case 2100 year will be considered
43+
incorrectly as a leap year, so the last valid point in time will be 28.02.2100 23:59:59 and next day will be
44+
29.02.2100 (invalid). So after 28.02.2100 the day counter will be off by a day.
45+
46+
"""
47+
48+
edge_date = datetime.datetime(2100, 2, 28, 0, 0, 0)
49+
50+
# Test the following years:
51+
# - first - 1970
52+
# - example not leap year (not divisible by 4)
53+
# - example leap year (divisible by 4 and by 100 and by 400)
54+
# - example leap year (divisible by 4 and not by 100)
55+
# - example not leap year (divisible by 4 and by 100)
56+
# - last fully supported - 2105
57+
years = [1970, 1971, 2000, 2096, 2100, 2105]
58+
year_id = 0
59+
60+
61+
62+
full_leap_year_support = False
63+
64+
RTC_FULL_LEAP_YEAR_SUPPORT = 0
65+
RTC_PARTIAL_LEAP_YEAR_SUPPORT = 1
66+
67+
def _set_leap_year_support(self, key, value, timestamp):
68+
if (int(value) == self.RTC_FULL_LEAP_YEAR_SUPPORT):
69+
self.full_leap_year_support = True
70+
else:
71+
self.full_leap_year_support = False
72+
73+
self.first = True
74+
self.date = datetime.datetime(1970, 1, 1, 23, 0, 0)
75+
self.year_id = 0
76+
77+
def _verify_timestamp(self, key, value, timestamp):
78+
# week day in python is counted from sunday(0) and on mbed side week day is counted from monday(0).
79+
# year day in python is counted from 1 and on mbed side year day is counted from 0.
80+
week_day = ((self.date.timetuple().tm_wday + 1) % 7)
81+
year_day = self.date.timetuple().tm_yday - 1
82+
83+
# Fix for RTC which not have full leap year support.
84+
if (not self.full_leap_year_support):
85+
if self.date >= self.edge_date:
86+
# After 28.02.2100 we should be one day off - add this day and store original
87+
date_org = self.date
88+
self.date += datetime.timedelta(days = 1)
89+
90+
# Adjust week day.
91+
week_day = ((self.date.timetuple().tm_wday + 1) % 7)
92+
93+
# Adjust year day.
94+
if (self.date.year == 2100):
95+
year_day = self.date.timetuple().tm_yday - 1
96+
else:
97+
year_day = date_org.timetuple().tm_yday - 1
98+
99+
# Last day in year
100+
if (self.date.month == 1 and self.date.day == 1):
101+
if (self.date.year == 2101):
102+
# Exception for year 2100 - ivalid handled by RTC without full leap year support
103+
year_day = 365
104+
else:
105+
year_day = date_org.timetuple().tm_yday - 1
106+
107+
t = (self.date.year , self.date.month, self.date.day, self.date.hour, self.date.minute, self.date.second, 0, 0, 0)
108+
109+
expected_timestamp = calendar.timegm(t)
110+
actual_timestamp = int(value) & 0xffffffff # convert to unsigned int
111+
112+
# encode week day and year day in the response
113+
response = (week_day << 16) | year_day
114+
115+
if (actual_timestamp == expected_timestamp):
116+
# response contains encoded week day and year day
117+
self.send_kv("passed", str(response))
118+
else:
119+
self.send_kv("failed", 0)
120+
print("expected = %d, result = %d" % (expected_timestamp , actual_timestamp))
121+
122+
# calculate next date
123+
if (self.first):
124+
days_range = calendar.monthrange(self.date.year, self.date.month)
125+
self.date = self.date.replace(day = days_range[1], minute = 59, second = 59)
126+
self.first = not self.first
127+
else:
128+
self.date += datetime.timedelta(days = 1)
129+
if (self.date.month == 1):
130+
self.year_id += 1
131+
if (len(self.years) == self.year_id):
132+
# All years were processed, no need to calc next date
133+
return
134+
self.date = self.date.replace(year = self.years[self.year_id])
135+
self.date = self.date.replace(day = 1, minute = 0, second = 0)
136+
self.first = not self.first
137+
138+
def setup(self):
139+
self.register_callback('timestamp', self._verify_timestamp)
140+
self.register_callback('leap_year_setup', self._set_leap_year_support)

0 commit comments

Comments
 (0)