aboutsummaryrefslogtreecommitdiffstats
path: root/test/testthd.c
blob: ac3fe5dd63ec5b63b47563e18319dc2df5e8625e (plain)
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
225
226
227
228
229
230
231
232
233
234
235
/*
    ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
                 2011,2012 Giovanni Di Sirio.

    This file is part of ChibiOS/RT.

    ChibiOS/RT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    ChibiOS/RT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ch.h"
#include "test.h"

/**
 * @page test_threads Threads and Scheduler test
 *
 * File: @ref testthd.c
 *
 * <h2>Description</h2>
 * This module implements the test sequence for the @ref scheduler,
 * @ref threads and @ref time subsystems.<br>
 * Note that the tests on those subsystems are formally required but most of
 * their functionality is already demonstrated because the test suite itself
 * depends on them, anyway double check is good.
 *
 * <h2>Objective</h2>
 * Objective of the test module is to cover 100% of the subsystems code.
 *
 * <h2>Preconditions</h2>
 * None.
 *
 * <h2>Test Cases</h2>
 * - @subpage test_threads_001
 * - @subpage test_threads_002
 * - @subpage test_threads_003
 * - @subpage test_threads_004
 * .
 * @file testthd.c
 * @brief Threads and Scheduler test source file
 * @file testthd.h
 * @brief Threads and Scheduler test header file
 */

/**
 * @page test_threads_001 Ready List functionality #1
 *
 * <h2>Description</h2>
 * Five threads, with increasing priority, are enqueued in the ready list
 * and atomically executed.<br>
 * The test expects the threads to perform their operations in increasing
 * priority order regardless of the initial order.
 */

static msg_t thread(void *p) {

  test_emit_token(*(char *)p);
  return 0;
}

static void thd1_execute(void) {

  threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-5, thread, "E");
  threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-4, thread, "D");
  threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-3, thread, "C");
  threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()-2, thread, "B");
  threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()-1, thread, "A");
  test_wait_threads();
  test_assert_sequence(1, "ABCDE");
}

ROMCONST struct testcase testthd1 = {
  "Threads, enqueuing test #1",
  NULL,
  NULL,
  thd1_execute
};

/**
 * @page test_threads_002 Ready List functionality #2
 *
 * <h2>Description</h2>
 * Five threads, with pseudo-random priority, are enqueued in the ready list
 * and atomically executed.<br>
 * The test expects the threads to perform their operations in increasing
 * priority order regardless of the initial order.
 */

static void thd2_execute(void) {

  threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-4, thread, "D");
  threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-5, thread, "E");
  threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()-1, thread, "A");
  threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()-2, thread, "B");
  /* Done this way for coverage of chThdCreateI() and chThdResume().*/
  chSysLock();
  threads[2] = chThdCreateI(wa[2], WA_SIZE, chThdGetPriority()-3, thread, "C");
  chSysUnlock();
  chThdResume(threads[2]);
  test_wait_threads();
  test_assert_sequence(1, "ABCDE");
}

ROMCONST struct testcase testthd2 = {
  "Threads, enqueuing test #2",
  NULL,
  NULL,
  thd2_execute
};

/**
 * @page test_threads_003 Threads priority change test
 *
 * <h2>Description</h2>
 * A series of priority changes are performed on the current thread in order
 * to verify that the priority change happens as expected.<br>
 * If the @p CH_USE_MUTEXES option is enabled then the priority changes are
 * also tested under priority inheritance boosted priority state.
 */

static void thd3_execute(void) {
  tprio_t prio, p1;

  prio = chThdGetPriority();
  p1 = chThdSetPriority(prio + 1);
  test_assert(1, p1 == prio,
              "unexpected returned priority level");
  test_assert(2, chThdGetPriority() == prio + 1,
              "unexpected priority level");
  p1 = chThdSetPriority(p1);
  test_assert(3, p1 == prio + 1,
              "unexpected returned priority level");
  test_assert(4, chThdGetPriority() == prio,
              "unexpected priority level");

#if CH_USE_MUTEXES || defined(__DOXYGEN__)
  /* Simulates a priority boost situation (p_prio > p_realprio).*/
  chSysLock();
  chThdSelf()->p_prio += 2;
  chSysUnlock();
  test_assert(5, chThdGetPriority() == prio + 2,
              "unexpected priority level");

  /* Tries to raise but below the boost level. */
  p1 = chThdSetPriority(prio + 1);
  test_assert(6, p1 == prio,
              "unexpected returned priority level");
  test_assert(7, chThdSelf()->p_prio == prio + 2,
              "unexpected priority level");
  test_assert(8, chThdSelf()->p_realprio == prio + 1,
              "unexpected returned real priority level");

  /* Tries to raise above the boost level. */
  p1 = chThdSetPriority(prio + 3);
  test_assert(9, p1 == prio + 1,
              "unexpected returned priority level");
  test_assert(10, chThdSelf()->p_prio == prio + 3,
              "unexpected priority level");
  test_assert(11, chThdSelf()->p_realprio == prio + 3,
              "unexpected real priority level");

  chSysLock();
  chThdSelf()->p_prio = prio;
  chThdSelf()->p_realprio = prio;
  chSysUnlock();
#endif
}

ROMCONST struct testcase testthd3 = {
  "Threads, priority change",
  NULL,
  NULL,
  thd3_execute
};

/**
 * @page test_threads_004 Threads delays test
 *
 * <h2>Description</h2>
 * Delay APIs and associated macros are tested, the invoking thread is verified
 * to wake up at the exact expected time.
 */

static void thd4_execute(void) {
  systime_t time;

  test_wait_tick();

  /* Timeouts in microseconds.*/
  time = chTimeNow();
  chThdSleepMicroseconds(100000);
  test_assert_time_window(1, time + US2ST(100000), time + US2ST(100000) + 1);

  /* Timeouts in milliseconds.*/
  time = chTimeNow();
  chThdSleepMilliseconds(100);
  test_assert_time_window(2, time + MS2ST(100), time + MS2ST(100) + 1);

  /* Timeouts in seconds.*/
  time = chTimeNow();
  chThdSleepSeconds(1);
  test_assert_time_window(3, time + S2ST(1), time + S2ST(1) + 1);

  /* Absolute timelines.*/
  time = chTimeNow() + MS2ST(100);
  chThdSleepUntil(time);
  test_assert_time_window(4, time, time + 1);
}

ROMCONST struct testcase testthd4 = {
  "Threads, delays",
  NULL,
  NULL,
  thd4_execute
};

/**
 * @brief   Test sequence for threads.
 */
ROMCONST struct testcase * ROMCONST patternthd[] = {
  &testthd1,
  &testthd2,
  &testthd3,
  &testthd4,
  NULL
};