blob: 8fdf903ed3fc4b891c2daa49957c2f46a20016b0 [file] [log] [blame]
Andrew Boie08ce5a52016-02-22 13:28:10 -08001#!/usr/bin/env python3
Anas Nashifa792a3d2017-04-04 18:47:49 -04002# vim: set syntax=python ts=4 :
Andrew Boie6acbe632015-07-17 12:03:52 -07003"""Zephyr Sanity Tests
4
5This script scans for the set of unit test applications in the git
6repository and attempts to execute them. By default, it tries to
7build each test case on one platform per architecture, using a precedence
Paul Sokolovskyff70add2017-06-16 01:31:54 +03008list defined in an architecture configuration file, and if possible
Andrew Boie6acbe632015-07-17 12:03:52 -07009run the tests in the QEMU emulator.
10
Anas Nashifa792a3d2017-04-04 18:47:49 -040011Test cases are detected by the presence of a 'testcase.yaml' or a sample.yaml
12files in the application's project directory. This file may contain one or more
13blocks, each identifying a test scenario. The title of the block is a name for
14the test case, which only needs to be unique for the test cases specified in
15that testcase meta-data. The full canonical name for each test case is <path to
16test case>/<block>.
Andrew Boie6acbe632015-07-17 12:03:52 -070017
Anas Nashif3ba1d432017-12-05 15:28:44 -050018Each test block in the testcase meta data can define the following key/value
19pairs:
Andrew Boie6acbe632015-07-17 12:03:52 -070020
Anas Nashiffa695d22017-10-04 16:14:27 -040021 tags: <list of tags> (required)
Andrew Boie6acbe632015-07-17 12:03:52 -070022 A set of string tags for the testcase. Usually pertains to
23 functional domains but can be anything. Command line invocations
24 of this script can filter the set of tests to run based on tag.
25
Anas Nashifa792a3d2017-04-04 18:47:49 -040026 skip: <True|False> (default False)
Anas Nashif2bd99bc2015-10-12 13:10:57 -040027 skip testcase unconditionally. This can be used for broken tests.
28
Anas Nashifa792a3d2017-04-04 18:47:49 -040029 slow: <True|False> (default False)
Andrew Boie6bb087c2016-02-10 13:39:00 -080030 Don't run this test case unless --enable-slow was passed in on the
31 command line. Intended for time-consuming test cases that are only
32 run under certain circumstances, like daily builds. These test cases
33 are still compiled.
34
Anas Nashifa792a3d2017-04-04 18:47:49 -040035 extra_args: <list of extra arguments>
Sebastian Bøec2182612017-11-09 12:25:02 +010036 Extra cache entries to pass to CMake when building or running the
Andrew Boie6acbe632015-07-17 12:03:52 -070037 test case.
38
Anas Nashifebc329d2017-10-17 09:00:33 -040039 extra_configs: <list of extra configurations>
40 Extra configuration options to be merged with a master prj.conf
41 when building or running the test case.
42
Anas Nashifa792a3d2017-04-04 18:47:49 -040043 build_only: <True|False> (default False)
Andrew Boie6acbe632015-07-17 12:03:52 -070044 If true, don't try to run the test under QEMU even if the
45 selected platform supports it.
46
Anas Nashifa792a3d2017-04-04 18:47:49 -040047 build_on_all: <True|False> (default False)
48 If true, attempt to build test on all available platforms.
49
50 depends_on: <list of features>
51 A board or platform can announce what features it supports, this option
52 will enable the test only those platforms that provide this feature.
53
54 min_ram: <integer>
55 minimum amount of RAM needed for this test to build and run. This is
56 compared with information provided by the board metadata.
57
58 min_flash: <integer>
59 minimum amount of ROM needed for this test to build and run. This is
60 compared with information provided by the board metadata.
61
62 timeout: <number of seconds>
Andrew Boie6acbe632015-07-17 12:03:52 -070063 Length of time to run test in QEMU before automatically killing it.
64 Default to 60 seconds.
65
Anas Nashifa792a3d2017-04-04 18:47:49 -040066 arch_whitelist: <list of arches, such as x86, arm, arc>
Andrew Boie6acbe632015-07-17 12:03:52 -070067 Set of architectures that this test case should only be run for.
68
Anas Nashifa792a3d2017-04-04 18:47:49 -040069 arch_exclude: <list of arches, such as x86, arm, arc>
Anas Nashif30d13872015-10-05 10:02:45 -040070 Set of architectures that this test case should not run on.
71
Anas Nashifa792a3d2017-04-04 18:47:49 -040072 platform_whitelist: <list of platforms>
Anas Nashif30d13872015-10-05 10:02:45 -040073 Set of platforms that this test case should only be run for.
74
Anas Nashifa792a3d2017-04-04 18:47:49 -040075 platform_exclude: <list of platforms>
Anas Nashif30d13872015-10-05 10:02:45 -040076 Set of platforms that this test case should not run on.
Andrew Boie6acbe632015-07-17 12:03:52 -070077
Anas Nashifa792a3d2017-04-04 18:47:49 -040078 extra_sections: <list of extra binary sections>
Andrew Boie52fef672016-11-29 12:21:59 -080079 When computing sizes, sanitycheck will report errors if it finds
80 extra, unexpected sections in the Zephyr binary unless they are named
81 here. They will not be included in the size calculation.
82
Anas Nashifa792a3d2017-04-04 18:47:49 -040083 filter: <expression>
Andrew Boie3ea78922016-03-24 14:46:00 -070084 Filter whether the testcase should be run by evaluating an expression
85 against an environment containing the following values:
86
87 { ARCH : <architecture>,
88 PLATFORM : <platform>,
Javier B Perez79414542016-08-08 12:24:59 -050089 <all CONFIG_* key/value pairs in the test's generated defconfig>,
90 *<env>: any environment variable available
Andrew Boie3ea78922016-03-24 14:46:00 -070091 }
92
93 The grammar for the expression language is as follows:
94
95 expression ::= expression "and" expression
96 | expression "or" expression
97 | "not" expression
98 | "(" expression ")"
99 | symbol "==" constant
100 | symbol "!=" constant
101 | symbol "<" number
102 | symbol ">" number
103 | symbol ">=" number
104 | symbol "<=" number
105 | symbol "in" list
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700106 | symbol ":" string
Andrew Boie3ea78922016-03-24 14:46:00 -0700107 | symbol
108
109 list ::= "[" list_contents "]"
110
111 list_contents ::= constant
112 | list_contents "," constant
113
114 constant ::= number
115 | string
116
117
118 For the case where expression ::= symbol, it evaluates to true
119 if the symbol is defined to a non-empty string.
120
121 Operator precedence, starting from lowest to highest:
122
123 or (left associative)
124 and (left associative)
125 not (right associative)
126 all comparison operators (non-associative)
127
128 arch_whitelist, arch_exclude, platform_whitelist, platform_exclude
129 are all syntactic sugar for these expressions. For instance
130
131 arch_exclude = x86 arc
132
133 Is the same as:
134
135 filter = not ARCH in ["x86", "arc"]
Andrew Boie6acbe632015-07-17 12:03:52 -0700136
Andrew Boie7a87f9f2016-06-02 12:27:54 -0700137 The ':' operator compiles the string argument as a regular expression,
138 and then returns a true value only if the symbol's value in the environment
139 matches. For example, if CONFIG_SOC="quark_se" then
140
141 filter = CONFIG_SOC : "quark.*"
142
143 Would match it.
144
Anas Nashifa792a3d2017-04-04 18:47:49 -0400145The set of test cases that actually run depends on directives in the testcase
146filed and options passed in on the command line. If there is any confusion,
147running with -v or --discard-report can help show why particular test cases
148were skipped.
Andrew Boie6acbe632015-07-17 12:03:52 -0700149
150Metrics (such as pass/fail state and binary size) for the last code
151release are stored in scripts/sanity_chk/sanity_last_release.csv.
152To update this, pass the --all --release options.
153
Genaro Saucedo Tejada28bba922016-10-24 18:00:58 -0500154To load arguments from a file, write '+' before the file name, e.g.,
155+file_name. File content must be one or more valid arguments separated by
156line break instead of white spaces.
157
Andrew Boie6acbe632015-07-17 12:03:52 -0700158Most everyday users will run with no arguments.
159"""
160
Anas Nashifaae71d72018-04-21 22:26:48 -0500161import contextlib
Anas Nashifa4c368e2018-10-15 09:45:59 -0400162import string
Anas Nashifaae71d72018-04-21 22:26:48 -0500163import mmap
Andrew Boie6acbe632015-07-17 12:03:52 -0700164import argparse
165import os
166import sys
Andrew Boie6acbe632015-07-17 12:03:52 -0700167import re
Andrew Boie6acbe632015-07-17 12:03:52 -0700168import subprocess
169import multiprocessing
170import select
171import shutil
172import signal
173import threading
174import time
175import csv
Andrew Boie5d4eb782015-10-02 10:04:56 -0700176import glob
Anas Nashif73440ea2018-02-19 10:57:03 -0600177import serial
Daniel Leung6b170072016-04-07 12:10:25 -0700178import concurrent
179import concurrent.futures
Anas Nashifb3311ed2017-04-13 14:44:48 -0400180import xml.etree.ElementTree as ET
Anas Nashife6fcc012017-05-17 09:29:09 -0400181from xml.sax.saxutils import escape
Anas Nashif035799f2017-05-13 21:31:53 -0400182from collections import OrderedDict
183from itertools import islice
Anas Nashif1a5bba72018-01-05 08:07:45 -0500184from functools import cmp_to_key
Anas Nashife24350c2018-07-11 15:09:22 -0500185from pathlib import Path
186from distutils.spawn import find_executable
Andrew Boie6acbe632015-07-17 12:03:52 -0700187
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -0700188import logging
Anas Nashif3ba1d432017-12-05 15:28:44 -0500189from sanity_chk import scl
190from sanity_chk import expr_parser
191
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -0700192log_format = "%(levelname)s %(name)s::%(module)s.%(funcName)s():%(lineno)d: %(message)s"
Anas Nashif3ba1d432017-12-05 15:28:44 -0500193logging.basicConfig(format=log_format, level=30)
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -0700194
Oleg Zhurakivskyy42822082018-08-17 14:54:41 +0300195ZEPHYR_BASE = os.environ.get("ZEPHYR_BASE")
196if not ZEPHYR_BASE:
Anas Nashif427cdd32015-08-06 07:25:42 -0400197 sys.stderr.write("$ZEPHYR_BASE environment variable undefined.\n")
Andrew Boie6acbe632015-07-17 12:03:52 -0700198 exit(1)
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -0700199
Andrew Boie3ea78922016-03-24 14:46:00 -0700200sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/"))
201
Andrew Boie3ea78922016-03-24 14:46:00 -0700202
Andrew Boie6acbe632015-07-17 12:03:52 -0700203VERBOSE = 0
204LAST_SANITY = os.path.join(ZEPHYR_BASE, "scripts", "sanity_chk",
205 "last_sanity.csv")
Anas Nashifb3311ed2017-04-13 14:44:48 -0400206LAST_SANITY_XUNIT = os.path.join(ZEPHYR_BASE, "scripts", "sanity_chk",
Anas Nashif3ba1d432017-12-05 15:28:44 -0500207 "last_sanity.xml")
Andrew Boie6acbe632015-07-17 12:03:52 -0700208RELEASE_DATA = os.path.join(ZEPHYR_BASE, "scripts", "sanity_chk",
209 "sanity_last_release.csv")
Oleg Zhurakivskyyf3bc9672018-08-17 18:31:38 +0300210JOBS = multiprocessing.cpu_count() * 2
Andrew Boie6acbe632015-07-17 12:03:52 -0700211
212if os.isatty(sys.stdout.fileno()):
213 TERMINAL = True
214 COLOR_NORMAL = '\033[0m'
215 COLOR_RED = '\033[91m'
216 COLOR_GREEN = '\033[92m'
217 COLOR_YELLOW = '\033[93m'
218else:
219 TERMINAL = False
220 COLOR_NORMAL = ""
221 COLOR_RED = ""
222 COLOR_GREEN = ""
223 COLOR_YELLOW = ""
224
225class SanityCheckException(Exception):
226 pass
227
Anas Nashif3ba1d432017-12-05 15:28:44 -0500228
Andrew Boie6acbe632015-07-17 12:03:52 -0700229class SanityRuntimeError(SanityCheckException):
230 pass
231
Anas Nashif3ba1d432017-12-05 15:28:44 -0500232
Andrew Boie6acbe632015-07-17 12:03:52 -0700233class ConfigurationError(SanityCheckException):
234 def __init__(self, cfile, message):
235 self.cfile = cfile
236 self.message = message
237
238 def __str__(self):
239 return repr(self.cfile + ": " + self.message)
240
Anas Nashif3ba1d432017-12-05 15:28:44 -0500241
Andrew Boie6acbe632015-07-17 12:03:52 -0700242class MakeError(SanityCheckException):
243 pass
244
Anas Nashif3ba1d432017-12-05 15:28:44 -0500245
Andrew Boie6acbe632015-07-17 12:03:52 -0700246class BuildError(MakeError):
247 pass
248
Anas Nashif3ba1d432017-12-05 15:28:44 -0500249
Andrew Boie6acbe632015-07-17 12:03:52 -0700250class ExecutionError(MakeError):
251 pass
252
Anas Nashif3ba1d432017-12-05 15:28:44 -0500253
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -0800254log_file = None
255
Anas Nashif3ba1d432017-12-05 15:28:44 -0500256
Andrew Boie6acbe632015-07-17 12:03:52 -0700257# Debug Functions
Andrew Boie08ce5a52016-02-22 13:28:10 -0800258def info(what):
259 sys.stdout.write(what + "\n")
Paul Sokolovsky3ba08762017-10-27 14:53:24 +0300260 sys.stdout.flush()
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -0800261 if log_file:
262 log_file.write(what + "\n")
263 log_file.flush()
Andrew Boie6acbe632015-07-17 12:03:52 -0700264
Anas Nashif3ba1d432017-12-05 15:28:44 -0500265
Andrew Boie6acbe632015-07-17 12:03:52 -0700266def error(what):
267 sys.stderr.write(COLOR_RED + what + COLOR_NORMAL + "\n")
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -0800268 if log_file:
269 log_file(what + "\n")
270 log_file.flush()
Andrew Boie6acbe632015-07-17 12:03:52 -0700271
Anas Nashif3ba1d432017-12-05 15:28:44 -0500272
Andrew Boie08ce5a52016-02-22 13:28:10 -0800273def debug(what):
274 if VERBOSE >= 1:
275 info(what)
276
Anas Nashif3ba1d432017-12-05 15:28:44 -0500277
Andrew Boie6acbe632015-07-17 12:03:52 -0700278def verbose(what):
279 if VERBOSE >= 2:
Andrew Boie08ce5a52016-02-22 13:28:10 -0800280 info(what)
Andrew Boie6acbe632015-07-17 12:03:52 -0700281
Anas Nashif576be982017-12-23 20:20:27 -0500282class HarnessImporter:
283
284 def __init__(self, name):
285 sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/sanity_chk"))
286 module = __import__("harness")
287 if name:
288 my_class = getattr(module, name)
289 else:
290 my_class = getattr(module, "Test")
291
292 self.instance = my_class()
Anas Nashif3ba1d432017-12-05 15:28:44 -0500293
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300294class Handler:
Anas Nashif576be982017-12-23 20:20:27 -0500295 def __init__(self, instance):
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300296 """Constructor
297
298 @param name Arbitrary name of the created thread
Anas Nashif66bdb322017-11-25 17:20:07 -0500299 @param outdir Working directory, should be where handler pid file (qemu.pid for example)
300 gets created by the build system
301 @param log_fn Absolute path to write out handler's log data
302 @param timeout Kill the handler process if it doesn't finish up within
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300303 the given number of seconds
304 """
305 self.lock = threading.Lock()
306 self.state = "waiting"
Anas Nashif13773752018-07-06 18:20:23 -0500307 self.run = False
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300308 self.metrics = {}
Anas Nashifc8390f12017-11-25 17:14:12 -0500309 self.metrics["handler_time"] = 0
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300310 self.metrics["ram_size"] = 0
311 self.metrics["rom_size"] = 0
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300312
Anas Nashifdf7ee612018-07-07 06:09:01 -0500313 self.binary = None
Anas Nashif99f5a6c2018-07-07 08:45:53 -0500314 self.call_make_run = False
Anas Nashifdf7ee612018-07-07 06:09:01 -0500315
Anas Nashifd3384fb2018-02-22 06:44:16 -0600316 self.name = instance.name
317 self.instance = instance
318 self.timeout = instance.test.timeout
Anas Nashiff18ad9d2018-11-20 09:03:17 -0500319 self.sourcedir = instance.test.test_path
Anas Nashifd3384fb2018-02-22 06:44:16 -0600320 self.outdir = instance.outdir
Anas Nashif4a9f3e62018-07-06 18:58:18 -0500321 self.log = os.path.join(self.outdir, "handler.log")
Anas Nashifd3384fb2018-02-22 06:44:16 -0600322 self.returncode = 0
323 self.set_state("running", {})
324
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300325 def set_state(self, state, metrics):
326 self.lock.acquire()
327 self.state = state
328 self.metrics.update(metrics)
329 self.lock.release()
330
331 def get_state(self):
332 self.lock.acquire()
333 ret = (self.state, self.metrics)
334 self.lock.release()
335 return ret
336
Anas Nashifdf7ee612018-07-07 06:09:01 -0500337class BinaryHandler(Handler):
338 def __init__(self, instance):
339 """Constructor
340
341 @param instance Test Instance
342 """
343 super().__init__(instance)
344
345 self.valgrind = False
Alberto Escolar Piedras661bc822018-12-11 15:13:05 +0100346 self.terminated = False
Anas Nashifdf7ee612018-07-07 06:09:01 -0500347
348 def _output_reader(self, proc, harness):
349 log_out_fp = open(self.log, "wt")
350 for line in iter(proc.stdout.readline, b''):
351 verbose("OUTPUT: {0}".format(line.decode('utf-8').rstrip()))
352 log_out_fp.write(line.decode('utf-8'))
353 log_out_fp.flush()
354 harness.handle(line.decode('utf-8').rstrip())
355 if harness.state:
Alberto Escolar Piedras661bc822018-12-11 15:13:05 +0100356 try:
357 #POSIX arch based ztests end on their own,
358 #so let's give it up to 100ms to do so
359 proc.wait(0.1)
360 except subprocess.TimeoutExpired:
361 proc.terminate()
362 self.terminated = True
Anas Nashifdf7ee612018-07-07 06:09:01 -0500363 break
364
365 log_out_fp.close()
366
367 def handle(self):
Anas Nashifdf7ee612018-07-07 06:09:01 -0500368
369 harness_name = self.instance.test.harness.capitalize()
370 harness_import = HarnessImporter(harness_name)
371 harness = harness_import.instance
372 harness.configure(self.instance)
373
Anas Nashif99f5a6c2018-07-07 08:45:53 -0500374 if self.call_make_run:
375 if options.ninja:
376 generator_cmd = "ninja"
377 else:
378 generator_cmd = "make"
379 command = [generator_cmd, "-C", self.outdir, "run"]
380 else:
381 command = [self.binary]
Anas Nashifdf7ee612018-07-07 06:09:01 -0500382
383 if shutil.which("valgrind") and self.valgrind:
384 command = ["valgrind", "--error-exitcode=2",
Alberto Escolar Piedras3980e0c2018-12-12 17:41:04 +0100385 "--leak-check=full",
386 "--suppressions="+ZEPHYR_BASE+"/scripts/valgrind.supp",
387 "--log-file="+self.outdir+"/valgrind.log"
388 ] + command
Anas Nashifdf7ee612018-07-07 06:09:01 -0500389
390 with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
391 t = threading.Thread(target=self._output_reader, args=(proc, harness, ))
392 t.start()
393 t.join(self.timeout)
394 if t.is_alive():
395 proc.terminate()
Alberto Escolar Piedras661bc822018-12-11 15:13:05 +0100396 self.terminated = True
Anas Nashifdf7ee612018-07-07 06:09:01 -0500397 t.join()
Anas Nashifdf7ee612018-07-07 06:09:01 -0500398 proc.wait()
399 self.returncode = proc.returncode
Anas Nashifdf7ee612018-07-07 06:09:01 -0500400
401 if options.enable_coverage:
402 returncode = subprocess.call(["GCOV_PREFIX=" + self.outdir,
403 "gcov", self.sourcedir, "-b", "-s", self.outdir], shell=True)
404
Anas Nashif99f5a6c2018-07-07 08:45:53 -0500405 # FIME: This is needed when killing the simulator, the console is
406 # garbled and needs to be reset. Did not find a better way to do that.
407
408 subprocess.call(["stty", "sane"])
Anas Nashifdf7ee612018-07-07 06:09:01 -0500409 self.instance.results = harness.tests
Alberto Escolar Piedras661bc822018-12-11 15:13:05 +0100410 if self.terminated==False and self.returncode != 0:
411 #When a process is killed, the default handler returns 128 + SIGTERM
412 #so in that case the return code itself is not meaningful
413 self.set_state("error", {})
414 elif harness.state:
Anas Nashifdf7ee612018-07-07 06:09:01 -0500415 self.set_state(harness.state, {})
416 else:
Alberto Escolar Piedras661bc822018-12-11 15:13:05 +0100417 self.set_state("timeout", {})
Anas Nashif73440ea2018-02-19 10:57:03 -0600418
419class DeviceHandler(Handler):
420
421 def __init__(self, instance):
422 """Constructor
423
424 @param instance Test Instance
425 """
426 super().__init__(instance)
427
Anas Nashif73440ea2018-02-19 10:57:03 -0600428 def monitor_serial(self, ser, harness):
Anas Nashif4a9f3e62018-07-06 18:58:18 -0500429 log_out_fp = open(self.log, "wt")
Anas Nashif73440ea2018-02-19 10:57:03 -0600430
431 while ser.isOpen():
Anas Nashif61e21632018-04-08 13:30:16 -0500432 try:
433 serial_line = ser.readline()
434 except TypeError:
435 pass
436
Anas Nashif73440ea2018-02-19 10:57:03 -0600437 if serial_line:
Anas Nashifd3384fb2018-02-22 06:44:16 -0600438 sl = serial_line.decode('utf-8', 'ignore')
439 verbose("DEVICE: {0}".format(sl.rstrip()))
440
441 log_out_fp.write(sl)
442 log_out_fp.flush()
443 harness.handle(sl.rstrip())
Anas Nashif73440ea2018-02-19 10:57:03 -0600444 if harness.state:
445 ser.close()
446 break
447
448 log_out_fp.close()
449
450 def handle(self):
451 out_state = "failed"
452
Anas Nashifd3384fb2018-02-22 06:44:16 -0600453 if options.ninja:
454 generator_cmd = "ninja"
455 else:
456 generator_cmd = "make"
457
458 command = [generator_cmd, "-C", self.outdir, "flash"]
459
Anas Nashif73440ea2018-02-19 10:57:03 -0600460 device = options.device_serial
461 ser = serial.Serial(
462 device,
463 baudrate=115200,
464 parity=serial.PARITY_NONE,
465 stopbits=serial.STOPBITS_ONE,
466 bytesize=serial.EIGHTBITS,
467 timeout=self.timeout
468 )
469
470 ser.flush()
471
472 harness_name = self.instance.test.harness.capitalize()
473 harness_import = HarnessImporter(harness_name)
474 harness = harness_import.instance
475 harness.configure(self.instance)
476
477 t = threading.Thread(target=self.monitor_serial, args=(ser, harness))
478 t.start()
479
Anas Nashif61e21632018-04-08 13:30:16 -0500480 try:
481 subprocess.check_output(command, stderr=subprocess.PIPE)
482 except subprocess.CalledProcessError:
483 pass
Anas Nashif73440ea2018-02-19 10:57:03 -0600484
485 t.join(self.timeout)
486 if t.is_alive():
487 out_state = "timeout"
488 ser.close()
489
490 if ser.isOpen():
491 ser.close()
492
Anas Nashifd3384fb2018-02-22 06:44:16 -0600493 if out_state == "timeout":
494 for c in self.instance.test.cases:
495 if c not in harness.tests:
496 harness.tests[c] = "BLOCK"
Anas Nashif61e21632018-04-08 13:30:16 -0500497
498 self.instance.results = harness.tests
Anas Nashife0a6a0b2018-02-15 20:07:24 -0600499
Anas Nashif73440ea2018-02-19 10:57:03 -0600500 if harness.state:
501 self.set_state(harness.state, {})
502 else:
503 self.set_state(out_state, {})
504
Anas Nashif3ba1d432017-12-05 15:28:44 -0500505
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300506class QEMUHandler(Handler):
Andrew Boie6acbe632015-07-17 12:03:52 -0700507 """Spawns a thread to monitor QEMU output from pipes
508
Anas Nashif3ac7b3a2017-08-03 10:03:02 -0400509 We pass QEMU_PIPE to 'make run' and monitor the pipes for output.
Andrew Boie6acbe632015-07-17 12:03:52 -0700510 We need to do this as once qemu starts, it runs forever until killed.
511 Test cases emit special messages to the console as they run, we check
512 for these to collect whether the test passed or failed.
513 """
Andrew Boie6acbe632015-07-17 12:03:52 -0700514
515 @staticmethod
Anas Nashif576be982017-12-23 20:20:27 -0500516 def _thread(handler, timeout, outdir, logfile, fifo_fn, pid_fn, results, harness):
Andrew Boie6acbe632015-07-17 12:03:52 -0700517 fifo_in = fifo_fn + ".in"
518 fifo_out = fifo_fn + ".out"
519
520 # These in/out nodes are named from QEMU's perspective, not ours
521 if os.path.exists(fifo_in):
522 os.unlink(fifo_in)
523 os.mkfifo(fifo_in)
524 if os.path.exists(fifo_out):
525 os.unlink(fifo_out)
526 os.mkfifo(fifo_out)
527
528 # We don't do anything with out_fp but we need to open it for
529 # writing so that QEMU doesn't block, due to the way pipes work
530 out_fp = open(fifo_in, "wb")
531 # Disable internal buffering, we don't
532 # want read() or poll() to ever block if there is data in there
533 in_fp = open(fifo_out, "rb", buffering=0)
Andrew Boie08ce5a52016-02-22 13:28:10 -0800534 log_out_fp = open(logfile, "wt")
Andrew Boie6acbe632015-07-17 12:03:52 -0700535
536 start_time = time.time()
537 timeout_time = start_time + timeout
538 p = select.poll()
539 p.register(in_fp, select.POLLIN)
Anas Nashif39ae72b2018-08-29 01:45:38 -0400540 out_state = None
Andrew Boie6acbe632015-07-17 12:03:52 -0700541
542 metrics = {}
543 line = ""
544 while True:
545 this_timeout = int((timeout_time - time.time()) * 1000)
546 if this_timeout < 0 or not p.poll(this_timeout):
Anas Nashif39ae72b2018-08-29 01:45:38 -0400547 if not out_state:
548 out_state = "timeout"
Andrew Boie6acbe632015-07-17 12:03:52 -0700549 break
550
Genaro Saucedo Tejada2968b362016-08-09 15:11:53 -0500551 try:
552 c = in_fp.read(1).decode("utf-8")
553 except UnicodeDecodeError:
554 # Test is writing something weird, fail
555 out_state = "unexpected byte"
556 break
557
Andrew Boie6acbe632015-07-17 12:03:52 -0700558 if c == "":
559 # EOF, this shouldn't happen unless QEMU crashes
560 out_state = "unexpected eof"
561 break
562 line = line + c
563 if c != "\n":
564 continue
565
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300566 # line contains a full line of data output from QEMU
Andrew Boie6acbe632015-07-17 12:03:52 -0700567 log_out_fp.write(line)
568 log_out_fp.flush()
569 line = line.strip()
570 verbose("QEMU: %s" % line)
571
Anas Nashif576be982017-12-23 20:20:27 -0500572 harness.handle(line)
573 if harness.state:
Anas Nashif39ae72b2018-08-29 01:45:38 -0400574 # if we have registered a fail make sure the state is not
575 # overridden by a false success message coming from the
576 # testsuite
577 if out_state != 'failed':
578 out_state = harness.state
579
580 # if we get some state, that means test is doing well, we reset
581 # the timeout and wait for 5 more seconds just in case we have
582 # crashed after test has completed
583
584 if harness.type:
585 break
586 else:
587 timeout_time = time.time() + 2
Andrew Boie6acbe632015-07-17 12:03:52 -0700588
589 # TODO: Add support for getting numerical performance data
590 # from test cases. Will involve extending test case reporting
591 # APIs. Add whatever gets reported to the metrics dictionary
592 line = ""
593
Anas Nashifc8390f12017-11-25 17:14:12 -0500594 metrics["handler_time"] = time.time() - start_time
Andrew Boie6acbe632015-07-17 12:03:52 -0700595 verbose("QEMU complete (%s) after %f seconds" %
Anas Nashifc8390f12017-11-25 17:14:12 -0500596 (out_state, metrics["handler_time"]))
Andrew Boie6acbe632015-07-17 12:03:52 -0700597 handler.set_state(out_state, metrics)
598
599 log_out_fp.close()
600 out_fp.close()
601 in_fp.close()
602
603 pid = int(open(pid_fn).read())
604 os.unlink(pid_fn)
Andrew Boie08ce5a52016-02-22 13:28:10 -0800605 try:
606 os.kill(pid, signal.SIGTERM)
607 except ProcessLookupError:
608 # Oh well, as long as it's dead! User probably sent Ctrl-C
609 pass
610
Andrew Boie6acbe632015-07-17 12:03:52 -0700611 os.unlink(fifo_in)
612 os.unlink(fifo_out)
613
Anas Nashif576be982017-12-23 20:20:27 -0500614 def __init__(self, instance):
Andrew Boie6acbe632015-07-17 12:03:52 -0700615 """Constructor
616
Anas Nashifd3384fb2018-02-22 06:44:16 -0600617 @param instance Test instance
Andrew Boie6acbe632015-07-17 12:03:52 -0700618 """
Anas Nashif576be982017-12-23 20:20:27 -0500619
Anas Nashif576be982017-12-23 20:20:27 -0500620 super().__init__(instance)
Anas Nashif576be982017-12-23 20:20:27 -0500621
Andrew Boie6acbe632015-07-17 12:03:52 -0700622 self.results = {}
Anas Nashif13773752018-07-06 18:20:23 -0500623 self.run = True
Andrew Boie6acbe632015-07-17 12:03:52 -0700624
625 # We pass this to QEMU which looks for fifos with .in and .out
626 # suffixes.
Anas Nashif576be982017-12-23 20:20:27 -0500627 self.fifo_fn = os.path.join(instance.outdir, "qemu-fifo")
Andrew Boie6acbe632015-07-17 12:03:52 -0700628
Anas Nashif576be982017-12-23 20:20:27 -0500629 self.pid_fn = os.path.join(instance.outdir, "qemu.pid")
Andrew Boie6acbe632015-07-17 12:03:52 -0700630 if os.path.exists(self.pid_fn):
631 os.unlink(self.pid_fn)
632
Anas Nashif4a9f3e62018-07-06 18:58:18 -0500633 self.log_fn = self.log
Anas Nashif576be982017-12-23 20:20:27 -0500634
635 harness_import = HarnessImporter(instance.test.harness.capitalize())
636 harness = harness_import.instance
Anas Nashifd3384fb2018-02-22 06:44:16 -0600637 harness.configure(self.instance)
638 self.thread = threading.Thread(name=self.name, target=QEMUHandler._thread,
639 args=(self, self.timeout, self.outdir,
Jaakko Hannikainenca505f82016-08-22 15:03:46 +0300640 self.log_fn, self.fifo_fn,
Anas Nashif576be982017-12-23 20:20:27 -0500641 self.pid_fn, self.results, harness))
Anas Nashife0a6a0b2018-02-15 20:07:24 -0600642
643 self.instance.results = harness.tests
Andrew Boie6acbe632015-07-17 12:03:52 -0700644 self.thread.daemon = True
Anas Nashifd3384fb2018-02-22 06:44:16 -0600645 verbose("Spawning QEMU process for %s" % self.name)
Andrew Boie6acbe632015-07-17 12:03:52 -0700646 self.thread.start()
647
Andrew Boie6acbe632015-07-17 12:03:52 -0700648 def get_fifo(self):
649 return self.fifo_fn
650
Anas Nashif3ba1d432017-12-05 15:28:44 -0500651
Andrew Boie6acbe632015-07-17 12:03:52 -0700652class SizeCalculator:
Andrew Boie73b4ee62015-10-07 11:33:22 -0700653
Erwin Rolcb3d1272018-02-10 11:40:40 +0100654 alloc_sections = ["bss", "noinit", "app_bss", "app_noinit", "ccm_bss",
655 "ccm_noinit"]
Andrew Boie18ba1532017-01-17 13:47:06 -0800656 rw_sections = ["datas", "initlevel", "_k_task_list", "_k_event_list",
Allan Stephens3f5c74c2016-11-01 14:37:15 -0500657 "_k_memory_pool", "exceptions", "initshell",
Andrew Boiec2e01df2018-11-12 15:16:54 -0800658 "_static_thread_area", "_k_timer_area",
Andrew Boie506f15c2018-11-08 14:44:31 -0800659 "_k_mem_slab_area", "_k_mem_pool_area", "sw_isr_table",
Andrew Boie3ef0b562017-08-31 12:36:45 -0700660 "_k_sem_area", "_k_mutex_area", "_k_alert_area",
661 "_k_fifo_area", "_k_lifo_area", "_k_stack_area",
662 "_k_msgq_area", "_k_mbox_area", "_k_pipe_area",
Jukka Rissanen60492072018-02-07 15:00:08 +0200663 "net_if", "net_if_dev", "net_stack", "net_l2_data",
Andrew Boie945af952017-08-22 13:15:23 -0700664 "_k_queue_area", "_net_buf_pool_area", "app_datas",
Erwin Rolcb3d1272018-02-10 11:40:40 +0100665 "kobject_data", "mmu_tables", "app_pad", "priv_stacks",
Anas Nashif0b685602018-06-29 12:57:47 -0500666 "ccm_data", "usb_descriptor", "usb_data", "usb_bos_desc",
667 'log_backends_sections', 'log_dynamic_sections',
Johann Fischerfcffebf2018-09-13 00:53:15 +0200668 'log_const_sections',"app_smem", 'shell_root_cmds_sections',
Adithya Baglody1fa8cf92018-10-19 10:15:19 -0700669 'log_const_sections',"app_smem", "font_entry_sections",
Anas Nashif724d6f72018-12-07 14:43:16 -0500670 "priv_stacks_noinit", "_TEXT_SECTION_NAME_2"]
Andrew Boie73b4ee62015-10-07 11:33:22 -0700671 # These get copied into RAM only on non-XIP
Andrew Boie877f82e2017-10-17 11:20:22 -0700672 ro_sections = ["text", "ctors", "init_array", "reset", "object_access",
Andrew Boie506f15c2018-11-08 14:44:31 -0800673 "rodata", "devconfig", "net_l2", "vector", "sw_isr_table",
674 "_bt_settings_area"]
Andrew Boie73b4ee62015-10-07 11:33:22 -0700675
Andrew Boie52fef672016-11-29 12:21:59 -0800676 def __init__(self, filename, extra_sections):
Andrew Boie6acbe632015-07-17 12:03:52 -0700677 """Constructor
678
Andrew Boiebbd670c2015-08-17 13:16:11 -0700679 @param filename Path to the output binary
680 The <filename> is parsed by objdump to determine section sizes
Andrew Boie6acbe632015-07-17 12:03:52 -0700681 """
Andrew Boie6acbe632015-07-17 12:03:52 -0700682 # Make sure this is an ELF binary
Andrew Boiebbd670c2015-08-17 13:16:11 -0700683 with open(filename, "rb") as f:
Andrew Boie6acbe632015-07-17 12:03:52 -0700684 magic = f.read(4)
685
Anas Nashifb4bdd662018-08-15 17:12:28 -0500686 try:
687 if (magic != b'\x7fELF'):
688 raise SanityRuntimeError("%s is not an ELF binary" % filename)
689 except Exception as e:
690 print(str(e))
691 sys.exit(2)
Andrew Boie6acbe632015-07-17 12:03:52 -0700692
693 # Search for CONFIG_XIP in the ELF's list of symbols using NM and AWK.
Anas Nashif3ba1d432017-12-05 15:28:44 -0500694 # GREP can not be used as it returns an error if the symbol is not
695 # found.
696 is_xip_command = "nm " + filename + \
697 " | awk '/CONFIG_XIP/ { print $3 }'"
698 is_xip_output = subprocess.check_output(
699 is_xip_command, shell=True, stderr=subprocess.STDOUT).decode(
700 "utf-8").strip()
Anas Nashifb4bdd662018-08-15 17:12:28 -0500701 try:
702 if is_xip_output.endswith("no symbols"):
703 raise SanityRuntimeError("%s has no symbol information" % filename)
704 except Exception as e:
705 print(str(e))
706 sys.exit(2)
707
Andrew Boie6acbe632015-07-17 12:03:52 -0700708 self.is_xip = (len(is_xip_output) != 0)
709
Andrew Boiebbd670c2015-08-17 13:16:11 -0700710 self.filename = filename
Andrew Boie73b4ee62015-10-07 11:33:22 -0700711 self.sections = []
712 self.rom_size = 0
Andrew Boie6acbe632015-07-17 12:03:52 -0700713 self.ram_size = 0
Andrew Boie52fef672016-11-29 12:21:59 -0800714 self.extra_sections = extra_sections
Andrew Boie6acbe632015-07-17 12:03:52 -0700715
716 self._calculate_sizes()
717
718 def get_ram_size(self):
719 """Get the amount of RAM the application will use up on the device
720
721 @return amount of RAM, in bytes
722 """
Andrew Boie73b4ee62015-10-07 11:33:22 -0700723 return self.ram_size
Andrew Boie6acbe632015-07-17 12:03:52 -0700724
725 def get_rom_size(self):
726 """Get the size of the data that this application uses on device's flash
727
728 @return amount of ROM, in bytes
729 """
Andrew Boie73b4ee62015-10-07 11:33:22 -0700730 return self.rom_size
Andrew Boie6acbe632015-07-17 12:03:52 -0700731
732 def unrecognized_sections(self):
733 """Get a list of sections inside the binary that weren't recognized
734
David B. Kinder29963c32017-06-16 12:32:42 -0700735 @return list of unrecognized section names
Andrew Boie6acbe632015-07-17 12:03:52 -0700736 """
737 slist = []
Andrew Boie73b4ee62015-10-07 11:33:22 -0700738 for v in self.sections:
Andrew Boie6acbe632015-07-17 12:03:52 -0700739 if not v["recognized"]:
Andrew Boie73b4ee62015-10-07 11:33:22 -0700740 slist.append(v["name"])
Andrew Boie6acbe632015-07-17 12:03:52 -0700741 return slist
742
743 def _calculate_sizes(self):
744 """ Calculate RAM and ROM usage by section """
Andrew Boiebbd670c2015-08-17 13:16:11 -0700745 objdump_command = "objdump -h " + self.filename
Anas Nashif3ba1d432017-12-05 15:28:44 -0500746 objdump_output = subprocess.check_output(
747 objdump_command, shell=True).decode("utf-8").splitlines()
Andrew Boie6acbe632015-07-17 12:03:52 -0700748
749 for line in objdump_output:
750 words = line.split()
751
752 if (len(words) == 0): # Skip lines that are too short
753 continue
754
755 index = words[0]
756 if (not index[0].isdigit()): # Skip lines that do not start
757 continue # with a digit
758
759 name = words[1] # Skip lines with section names
760 if (name[0] == '.'): # starting with '.'
761 continue
762
Andrew Boie73b4ee62015-10-07 11:33:22 -0700763 # TODO this doesn't actually reflect the size in flash or RAM as
764 # it doesn't include linker-imposed padding between sections.
765 # It is close though.
Andrew Boie6acbe632015-07-17 12:03:52 -0700766 size = int(words[2], 16)
Andrew Boie9882dcd2015-10-07 14:25:51 -0700767 if size == 0:
768 continue
769
Andrew Boie73b4ee62015-10-07 11:33:22 -0700770 load_addr = int(words[4], 16)
771 virt_addr = int(words[3], 16)
Andrew Boie6acbe632015-07-17 12:03:52 -0700772
773 # Add section to memory use totals (for both non-XIP and XIP scenarios)
Andrew Boie6acbe632015-07-17 12:03:52 -0700774 # Unrecognized section names are not included in the calculations.
Andrew Boie6acbe632015-07-17 12:03:52 -0700775 recognized = True
Andrew Boie73b4ee62015-10-07 11:33:22 -0700776 if name in SizeCalculator.alloc_sections:
777 self.ram_size += size
778 stype = "alloc"
779 elif name in SizeCalculator.rw_sections:
780 self.ram_size += size
781 self.rom_size += size
782 stype = "rw"
783 elif name in SizeCalculator.ro_sections:
784 self.rom_size += size
785 if not self.is_xip:
786 self.ram_size += size
787 stype = "ro"
Andrew Boie6acbe632015-07-17 12:03:52 -0700788 else:
Andrew Boie73b4ee62015-10-07 11:33:22 -0700789 stype = "unknown"
Andrew Boie52fef672016-11-29 12:21:59 -0800790 if name not in self.extra_sections:
791 recognized = False
Andrew Boie6acbe632015-07-17 12:03:52 -0700792
Anas Nashif3ba1d432017-12-05 15:28:44 -0500793 self.sections.append({"name": name, "load_addr": load_addr,
794 "size": size, "virt_addr": virt_addr,
795 "type": stype, "recognized": recognized})
Andrew Boie6acbe632015-07-17 12:03:52 -0700796
797
798class MakeGoal:
799 """Metadata class representing one of the sub-makes called by MakeGenerator
800
David B. Kinder29963c32017-06-16 12:32:42 -0700801 MakeGenerator returns a dictionary of these which can then be associated
Andrew Boie6acbe632015-07-17 12:03:52 -0700802 with TestInstances to get a complete picture of what happened during a test.
803 MakeGenerator is used for tasks outside of building tests (such as
804 defconfigs) which is why MakeGoal is a separate class from TestInstance.
805 """
Anas Nashif3ba1d432017-12-05 15:28:44 -0500806
Anas Nashif4d25b502017-11-25 17:37:17 -0500807 def __init__(self, name, text, handler, make_log, build_log, run_log, handler_log):
Andrew Boie6acbe632015-07-17 12:03:52 -0700808 self.name = name
809 self.text = text
Anas Nashif4d25b502017-11-25 17:37:17 -0500810 self.handler = handler
Andrew Boie6acbe632015-07-17 12:03:52 -0700811 self.make_log = make_log
812 self.build_log = build_log
813 self.run_log = run_log
Anas Nashif4d25b502017-11-25 17:37:17 -0500814 self.handler_log = handler_log
Andrew Boie6acbe632015-07-17 12:03:52 -0700815 self.make_state = "waiting"
816 self.failed = False
817 self.finished = False
818 self.reason = None
819 self.metrics = {}
820
821 def get_error_log(self):
822 if self.make_state == "waiting":
823 # Shouldn't ever see this; breakage in the main Makefile itself.
824 return self.make_log
825 elif self.make_state == "building":
826 # Failure when calling the sub-make to build the code
827 return self.build_log
828 elif self.make_state == "running":
Anas Nashif3ac7b3a2017-08-03 10:03:02 -0400829 # Failure in sub-make for "make run", qemu probably failed to start
Andrew Boie6acbe632015-07-17 12:03:52 -0700830 return self.run_log
831 elif self.make_state == "finished":
Anas Nashifcc164222017-12-26 11:02:46 -0500832 # Execution handler finished, but timed out or otherwise wasn't successful
Anas Nashif4d25b502017-11-25 17:37:17 -0500833 return self.handler_log
Andrew Boie6acbe632015-07-17 12:03:52 -0700834
835 def fail(self, reason):
836 self.failed = True
837 self.finished = True
838 self.reason = reason
839
840 def success(self):
841 self.finished = True
842
843 def __str__(self):
844 if self.finished:
845 if self.failed:
846 return "[%s] failed (%s: see %s)" % (self.name, self.reason,
847 self.get_error_log())
848 else:
849 return "[%s] passed" % self.name
850 else:
851 return "[%s] in progress (%s)" % (self.name, self.make_state)
852
853
854class MakeGenerator:
855 """Generates a Makefile which just calls a bunch of sub-make sessions
856
857 In any given test suite we may need to build dozens if not hundreds of
858 test cases. The cleanest way to parallelize this is to just let Make
859 do the parallelization, sharing the jobserver among all the different
860 sub-make targets.
861 """
862
863 GOAL_HEADER_TMPL = """.PHONY: {goal}
864{goal}:
865"""
866
867 MAKE_RULE_TMPL = """\t@echo sanity_test_{phase} {goal} >&2
Anas Nashiffb91ad62017-10-31 08:33:17 -0400868\tcmake \\
Anas Nashifa8a13882017-12-30 13:01:06 -0500869\t\t-G"{generator}"\\
Anas Nashiffb91ad62017-10-31 08:33:17 -0400870\t\t-H{directory}\\
871\t\t-B{outdir}\\
872\t\t-DEXTRA_CFLAGS="-Werror {cflags}"\\
873\t\t-DEXTRA_AFLAGS=-Wa,--fatal-warnings\\
Anas Nashif262e4a42017-12-14 08:42:45 -0500874\t\t-DEXTRA_LDFLAGS="{ldflags}"\\
Anas Nashiffb91ad62017-10-31 08:33:17 -0400875\t\t{args}\\
876\t\t>{logfile} 2>&1
Anas Nashifa8a13882017-12-30 13:01:06 -0500877\t{generator_cmd} -C {outdir}\\
878\t\t{verb} {make_args}\\
Anas Nashiffb91ad62017-10-31 08:33:17 -0400879\t\t>>{logfile} 2>&1
Andrew Boie6acbe632015-07-17 12:03:52 -0700880"""
Anas Nashif20f553f2018-03-23 11:26:41 -0500881 MAKE_RULE_TMPL_RUN = """\t@echo sanity_test_{phase} {goal} >&2
882\t{generator_cmd} -C {outdir}\\
883\t\t{verb} {make_args}\\
884\t\t>>{logfile} 2>&1
885"""
Andrew Boie6acbe632015-07-17 12:03:52 -0700886
887 GOAL_FOOTER_TMPL = "\t@echo sanity_test_finished {goal} >&2\n\n"
888
Anas Nashif3ba1d432017-12-05 15:28:44 -0500889 re_make = re.compile(
890 "sanity_test_([A-Za-z0-9]+) (.+)|$|make[:] \*\*\* \[(.+:.+: )?(.+)\] Error.+$")
Andrew Boie6acbe632015-07-17 12:03:52 -0700891
Anas Nashif37f9dc52018-02-23 08:53:46 -0600892 def __init__(self, base_outdir):
Andrew Boie6acbe632015-07-17 12:03:52 -0700893 """MakeGenerator constructor
894
895 @param base_outdir Intended to be the base out directory. A make.log
896 file will be created here which contains the output of the
897 top-level Make session, as well as the dynamic control Makefile
898 @param verbose If true, pass V=1 to all the sub-makes which greatly
899 increases their verbosity
900 """
901 self.goals = {}
902 if not os.path.exists(base_outdir):
903 os.makedirs(base_outdir)
904 self.logfile = os.path.join(base_outdir, "make.log")
905 self.makefile = os.path.join(base_outdir, "Makefile")
Anas Nashif37f9dc52018-02-23 08:53:46 -0600906 self.deprecations = options.error_on_deprecations
Andrew Boie6acbe632015-07-17 12:03:52 -0700907
908 def _get_rule_header(self, name):
909 return MakeGenerator.GOAL_HEADER_TMPL.format(goal=name)
910
Anas Nashif3ba1d432017-12-05 15:28:44 -0500911 def _get_sub_make(self, name, phase, workdir, outdir,
912 logfile, args, make_args=""):
Anas Nashiffb91ad62017-10-31 08:33:17 -0400913 """
914 @param args Arguments given to CMake
915 @param make_args Arguments given to the Makefile generated by CMake
916 """
Anas Nashif3ba1d432017-12-05 15:28:44 -0500917 args = " ".join(["-D{}".format(a) for a in args])
Anas Nashif262e4a42017-12-14 08:42:45 -0500918 ldflags = ""
Andrew Boie29599f62018-05-24 13:33:09 -0700919 cflags = ""
Anas Nashife3febe92016-11-30 14:25:44 -0500920
921 if self.deprecations:
922 cflags = cflags + " -Wno-deprecated-declarations"
Andrew Boief7a6e282017-02-02 13:04:57 -0800923
Alberto Escolar Piedras770178b2018-05-02 13:49:51 +0200924 ldflags="-Wl,--fatal-warnings"
Anas Nashif262e4a42017-12-14 08:42:45 -0500925
Anas Nashif25f6ab62018-03-06 07:15:11 -0600926 if options.ninja:
Sebastian Bøe0e6689d2018-01-18 14:40:07 +0100927 generator = "Ninja"
Andy Rossdec163f2018-05-21 10:12:59 -0700928 generator_cmd = "ninja -j1"
Sebastian Bøe0e6689d2018-01-18 14:40:07 +0100929 verb = "-v" if VERBOSE else ""
Anas Nashif25f6ab62018-03-06 07:15:11 -0600930 else:
931 generator = "Unix Makefiles"
932 generator_cmd = "$(MAKE)"
Andrew Boie3efd2692018-06-26 10:41:35 -0700933 verb = "VERBOSE=1" if VERBOSE else ""
Anas Nashifa8a13882017-12-30 13:01:06 -0500934
Anas Nashif20f553f2018-03-23 11:26:41 -0500935 if phase == 'running':
936 return MakeGenerator.MAKE_RULE_TMPL_RUN.format(
937 generator_cmd=generator_cmd,
938 phase=phase,
939 goal=name,
940 outdir=outdir,
941 verb=verb,
942 logfile=logfile,
943 make_args=make_args
944 )
945 else:
946 return MakeGenerator.MAKE_RULE_TMPL.format(
947 generator=generator,
948 generator_cmd=generator_cmd,
949 phase=phase,
950 goal=name,
951 outdir=outdir,
952 cflags=cflags,
953 ldflags=ldflags,
954 directory=workdir,
955 verb=verb,
956 args=args,
957 logfile=logfile,
958 make_args=make_args
959 )
Andrew Boie6acbe632015-07-17 12:03:52 -0700960
961 def _get_rule_footer(self, name):
962 return MakeGenerator.GOAL_FOOTER_TMPL.format(goal=name)
963
Anas Nashif3ba1d432017-12-05 15:28:44 -0500964 def add_build_goal(self, name, directory, outdir,
965 args, buildlog, make_args=""):
Anas Nashif13773752018-07-06 18:20:23 -0500966 """Add a goal to invoke a build session
Andrew Boie6acbe632015-07-17 12:03:52 -0700967
968 @param name A unique string name for this build goal. The results
969 dictionary returned by execute() will be keyed by this name.
970 @param directory Absolute path to working directory, will be passed
971 to make -C
972 @param outdir Absolute path to output directory, will be passed to
Sebastian Bøe71d7de02017-11-09 12:06:04 +0100973 cmake via -B=<path>
974 @param args Extra command line arguments to pass to 'cmake', typically
Andrew Boie6acbe632015-07-17 12:03:52 -0700975 environment variables or specific Make goals
976 """
Andrew Boie6acbe632015-07-17 12:03:52 -0700977
Anas Nashif13773752018-07-06 18:20:23 -0500978 if not os.path.exists(outdir):
979 os.makedirs(outdir)
980
981 build_logfile = os.path.join(outdir, buildlog)
982 text = self._get_rule_header(name)
983 text += self._get_sub_make(name, "building", directory, outdir, build_logfile,
984 args, make_args=make_args)
985 text += self._get_rule_footer(name)
986
987 self.goals[name] = MakeGoal( name, text, None, self.logfile, build_logfile, None, None)
988
989 def add_goal(self, instance, type, args, make_args=""):
Anas Nashif4a9f3e62018-07-06 18:58:18 -0500990
Anas Nashif13773752018-07-06 18:20:23 -0500991 """Add a goal to build a Zephyr project and then run it using a handler
Andrew Boie6acbe632015-07-17 12:03:52 -0700992
993 The generated make goal invokes Make twice, the first time it will
994 build the default goal, and the second will invoke the 'qemu' goal.
Anas Nashif13773752018-07-06 18:20:23 -0500995 The output of the handler session will be monitored, and terminated
Andrew Boie6acbe632015-07-17 12:03:52 -0700996 either upon pass/fail result of the test program, or the timeout
997 is reached.
998
Sebastian Bøe71d7de02017-11-09 12:06:04 +0100999 @param args Extra cache entries to define in CMake.
Andrew Boie6acbe632015-07-17 12:03:52 -07001000 """
1001
Anas Nashif576be982017-12-23 20:20:27 -05001002 name = instance.name
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001003 directory = instance.test.test_path
Anas Nashif576be982017-12-23 20:20:27 -05001004 outdir = instance.outdir
1005
Andrew Boie6acbe632015-07-17 12:03:52 -07001006 build_logfile = os.path.join(outdir, "build.log")
1007 run_logfile = os.path.join(outdir, "run.log")
Andrew Boie6acbe632015-07-17 12:03:52 -07001008
Anas Nashif13773752018-07-06 18:20:23 -05001009 if not os.path.exists(outdir):
1010 os.makedirs(outdir)
Andrew Boie6acbe632015-07-17 12:03:52 -07001011
Anas Nashif13773752018-07-06 18:20:23 -05001012 handler = None
1013 if type == "qemu":
1014 handler = QEMUHandler(instance)
1015 elif type == "native":
Anas Nashifdf7ee612018-07-07 06:09:01 -05001016 handler = BinaryHandler(instance)
1017 handler.binary = os.path.join(outdir, "zephyr", "zephyr.exe")
Anas Nashif99f5a6c2018-07-07 08:45:53 -05001018 elif type == "nsim":
1019 handler = BinaryHandler(instance)
1020 handler.call_make_run = True
Anas Nashif13773752018-07-06 18:20:23 -05001021 elif type == "unit":
Anas Nashifdf7ee612018-07-07 06:09:01 -05001022 handler = BinaryHandler(instance)
1023 handler.binary = os.path.join(outdir, "testbinary")
Anas Nashif13773752018-07-06 18:20:23 -05001024 elif type == "device":
1025 handler = DeviceHandler(instance)
Anas Nashif576be982017-12-23 20:20:27 -05001026
Alberto Escolar Piedrasc026c2e2018-06-21 09:30:20 +02001027 if options.enable_coverage:
Anas Nashif8d72bb92018-11-07 23:05:42 -05001028 args += ["EXTRA_LDFLAGS=--coverage"]
Alberto Escolar Piedrasc026c2e2018-06-21 09:30:20 +02001029
Anas Nashif13773752018-07-06 18:20:23 -05001030 if type == 'qemu':
1031 args.append("QEMU_PIPE=%s" % handler.get_fifo())
1032
Anas Nashifcc164222017-12-26 11:02:46 -05001033 text = (self._get_rule_header(name) +
1034 self._get_sub_make(name, "building", directory,
Anas Nashif13773752018-07-06 18:20:23 -05001035 outdir, build_logfile, args, make_args=make_args))
1036 if handler and handler.run:
1037 text += self._get_sub_make(name, "running", directory,
1038 outdir, run_logfile,
1039 args, make_args="run")
Anas Nashif4d25b502017-11-25 17:37:17 -05001040
Anas Nashif13773752018-07-06 18:20:23 -05001041 text += self._get_rule_footer(name)
Anas Nashif73440ea2018-02-19 10:57:03 -06001042
Anas Nashif73440ea2018-02-19 10:57:03 -06001043 self.goals[name] = MakeGoal(name, text, handler, self.logfile, build_logfile,
Anas Nashif4a9f3e62018-07-06 18:58:18 -05001044 run_logfile, handler.log if handler else None)
Anas Nashif73440ea2018-02-19 10:57:03 -06001045
Anas Nashif13773752018-07-06 18:20:23 -05001046
Anas Nashif37f9dc52018-02-23 08:53:46 -06001047 def add_test_instance(self, ti, extra_args=[]):
Andrew Boie6acbe632015-07-17 12:03:52 -07001048 """Add a goal to build/test a TestInstance object
1049
1050 @param ti TestInstance object to build. The status dictionary returned
1051 by execute() will be keyed by its .name field.
1052 """
1053 args = ti.test.extra_args[:]
Anas Nashif3cbffef2018-11-07 23:50:54 -05001054 if len(ti.test.extra_configs) > 0 or options.coverage:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001055 args.append("OVERLAY_CONFIG=%s" %
1056 os.path.join(ti.outdir, "overlay.conf"))
Anas Nashiffa695d22017-10-04 16:14:27 -04001057
Anas Nashif3cbffef2018-11-07 23:50:54 -05001058 if ti.test.type == "unit" and options.enable_coverage:
1059 args.append("COVERAGE=1")
1060
Anas Nashiffb91ad62017-10-31 08:33:17 -04001061 args.append("BOARD={}".format(ti.platform.name))
Andrew Boieba612002016-09-01 10:41:03 -07001062 args.extend(extra_args)
Anas Nashif5df8cff2018-02-23 08:37:14 -06001063
Anas Nashif37f9dc52018-02-23 08:53:46 -06001064 do_run_slow = options.enable_slow or not ti.test.slow
1065 do_build_only = ti.build_only or options.build_only
Anas Nashif5df8cff2018-02-23 08:37:14 -06001066 do_run = (not do_build_only) and do_run_slow
1067
Anas Nashif13773752018-07-06 18:20:23 -05001068 # FIXME: Need refactoring and cleanup
1069 type = None
Anas Nashif5df8cff2018-02-23 08:37:14 -06001070 if ti.platform.qemu_support and do_run:
Anas Nashif13773752018-07-06 18:20:23 -05001071 type = "qemu"
Jaakko Hannikainenca505f82016-08-22 15:03:46 +03001072 elif ti.test.type == "unit":
Anas Nashif13773752018-07-06 18:20:23 -05001073 type = "unit"
Anas Nashif5df8cff2018-02-23 08:37:14 -06001074 elif ti.platform.type == "native" and do_run:
Anas Nashif13773752018-07-06 18:20:23 -05001075 type = "native"
Anas Nashif99f5a6c2018-07-07 08:45:53 -05001076 elif ti.platform.simulation == "nsim" and do_run:
Anas Nashife24350c2018-07-11 15:09:22 -05001077 if find_executable("nsimdrv"):
1078 type = "nsim"
Anas Nashif37f9dc52018-02-23 08:53:46 -06001079 elif options.device_testing and (not ti.build_only) and (not options.build_only):
Anas Nashif13773752018-07-06 18:20:23 -05001080 type = "device"
Anas Nashif37f9dc52018-02-23 08:53:46 -06001081
Anas Nashif13773752018-07-06 18:20:23 -05001082 self.add_goal(ti, type, args)
Andrew Boie6acbe632015-07-17 12:03:52 -07001083
1084 def execute(self, callback_fn=None, context=None):
1085 """Execute all the registered build goals
1086
1087 @param callback_fn If not None, a callback function will be called
1088 as individual goals transition between states. This function
1089 should accept two parameters: a string state and an arbitrary
1090 context object, supplied here
1091 @param context Context object to pass to the callback function.
1092 Type and semantics are specific to that callback function.
1093 @return A dictionary mapping goal names to final status.
1094 """
1095
Andrew Boie08ce5a52016-02-22 13:28:10 -08001096 with open(self.makefile, "wt") as tf, \
Andrew Boie6acbe632015-07-17 12:03:52 -07001097 open(os.devnull, "wb") as devnull, \
Andrew Boie08ce5a52016-02-22 13:28:10 -08001098 open(self.logfile, "wt") as make_log:
Andrew Boie6acbe632015-07-17 12:03:52 -07001099 # Create our dynamic Makefile and execute it.
1100 # Watch stderr output which is where we will keep
1101 # track of build state
Andrew Boie08ce5a52016-02-22 13:28:10 -08001102 for name, goal in self.goals.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07001103 tf.write(goal.text)
1104 tf.write("all: %s\n" % (" ".join(self.goals.keys())))
1105 tf.flush()
1106
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03001107 cmd = ["make", "-k", "-j", str(JOBS), "-f", tf.name, "all"]
Andy Rossdec163f2018-05-21 10:12:59 -07001108
Bobby Noelte9cf8b3c2018-06-13 06:10:20 +02001109 # assure language neutral environemnt
1110 make_env = os.environ.copy()
1111 make_env['LC_MESSAGES'] = 'C.UTF-8'
1112 p = subprocess.Popen(cmd, stderr=subprocess.PIPE,
1113 stdout=devnull, env=make_env)
Andrew Boie6acbe632015-07-17 12:03:52 -07001114
1115 for line in iter(p.stderr.readline, b''):
Andrew Boie08ce5a52016-02-22 13:28:10 -08001116 line = line.decode("utf-8")
Andrew Boie6acbe632015-07-17 12:03:52 -07001117 make_log.write(line)
1118 verbose("MAKE: " + repr(line.strip()))
1119 m = MakeGenerator.re_make.match(line)
1120 if not m:
1121 continue
1122
Jaakko Hannikainenca505f82016-08-22 15:03:46 +03001123 state, name, _, error = m.groups()
Andrew Boie6acbe632015-07-17 12:03:52 -07001124 if error:
1125 goal = self.goals[error]
Andrew Boie822b0872017-01-10 13:32:40 -08001126 # Sometimes QEMU will run an image and then crash out, which
Anas Nashif3ac7b3a2017-08-03 10:03:02 -04001127 # will cause the 'make run' invocation to exit with
Andrew Boie822b0872017-01-10 13:32:40 -08001128 # nonzero status.
1129 # Need to distinguish this case from a compilation failure.
Anas Nashif4d25b502017-11-25 17:37:17 -05001130 if goal.handler:
Anas Nashif9a839df2018-01-29 08:42:38 -05001131 goal.fail("handler_crash")
Andrew Boie822b0872017-01-10 13:32:40 -08001132 else:
1133 goal.fail("build_error")
Andrew Boie6acbe632015-07-17 12:03:52 -07001134 else:
Anas Nashifcc164222017-12-26 11:02:46 -05001135 goal = self.goals[name]
1136 goal.make_state = state
1137
Andrew Boie6acbe632015-07-17 12:03:52 -07001138 if state == "finished":
Anas Nashif4d25b502017-11-25 17:37:17 -05001139 if goal.handler:
Anas Nashif576be982017-12-23 20:20:27 -05001140 if hasattr(goal.handler, "handle"):
Anas Nashif4d25b502017-11-25 17:37:17 -05001141 goal.handler.handle()
Anas Nashif4a9f3e62018-07-06 18:58:18 -05001142 goal.handler_log = goal.handler.log
Anas Nashifcc164222017-12-26 11:02:46 -05001143
Anas Nashif4d25b502017-11-25 17:37:17 -05001144 thread_status, metrics = goal.handler.get_state()
Andrew Boie6acbe632015-07-17 12:03:52 -07001145 goal.metrics.update(metrics)
1146 if thread_status == "passed":
1147 goal.success()
1148 else:
1149 goal.fail(thread_status)
1150 else:
1151 goal.success()
1152
1153 if callback_fn:
1154 callback_fn(context, self.goals, goal)
1155
1156 p.wait()
1157 return self.goals
1158
1159
1160# "list" - List of strings
1161# "list:<type>" - List of <type>
1162# "set" - Set of unordered, unique strings
1163# "set:<type>" - Set of <type>
1164# "float" - Floating point
1165# "int" - Integer
1166# "bool" - Boolean
1167# "str" - String
1168
1169# XXX Be sure to update __doc__ if you change any of this!!
1170
Anas Nashif3ba1d432017-12-05 15:28:44 -05001171platform_valid_keys = {"qemu_support": {"type": "bool", "default": False},
Anas Nashif924a4e72018-10-18 12:25:55 -04001172 "supported_toolchains": {"type": "list", "default": []},
1173 "env": {"type": "list", "default": []}
1174 }
Andrew Boie6acbe632015-07-17 12:03:52 -07001175
Anas Nashif3ba1d432017-12-05 15:28:44 -05001176testcase_valid_keys = {"tags": {"type": "set", "required": False},
1177 "type": {"type": "str", "default": "integration"},
1178 "extra_args": {"type": "list"},
1179 "extra_configs": {"type": "list"},
1180 "build_only": {"type": "bool", "default": False},
1181 "build_on_all": {"type": "bool", "default": False},
1182 "skip": {"type": "bool", "default": False},
1183 "slow": {"type": "bool", "default": False},
1184 "timeout": {"type": "int", "default": 60},
1185 "min_ram": {"type": "int", "default": 8},
1186 "depends_on": {"type": "set"},
1187 "min_flash": {"type": "int", "default": 32},
1188 "arch_whitelist": {"type": "set"},
1189 "arch_exclude": {"type": "set"},
1190 "extra_sections": {"type": "list", "default": []},
1191 "platform_exclude": {"type": "set"},
1192 "platform_whitelist": {"type": "set"},
1193 "toolchain_exclude": {"type": "set"},
1194 "toolchain_whitelist": {"type": "set"},
Anas Nashifab940162017-12-08 10:17:57 -05001195 "filter": {"type": "str"},
Anas Nashif576be982017-12-23 20:20:27 -05001196 "harness": {"type": "str"},
Praful Swarnakarcf89e282018-09-13 02:58:28 +05301197 "harness_config": {"type": "map", "default": {}}
Anas Nashifab940162017-12-08 10:17:57 -05001198 }
Andrew Boie6acbe632015-07-17 12:03:52 -07001199
1200
1201class SanityConfigParser:
Anas Nashifa792a3d2017-04-04 18:47:49 -04001202 """Class to read test case files with semantic checking
Andrew Boie6acbe632015-07-17 12:03:52 -07001203 """
Anas Nashif3ba1d432017-12-05 15:28:44 -05001204
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001205 def __init__(self, filename, schema):
Andrew Boie6acbe632015-07-17 12:03:52 -07001206 """Instantiate a new SanityConfigParser object
1207
Anas Nashifa792a3d2017-04-04 18:47:49 -04001208 @param filename Source .yaml file to read
Andrew Boie6acbe632015-07-17 12:03:52 -07001209 """
Anas Nashif255625b2017-12-05 15:08:26 -05001210 self.data = scl.yaml_load_verify(filename, schema)
Andrew Boie6acbe632015-07-17 12:03:52 -07001211 self.filename = filename
Anas Nashif255625b2017-12-05 15:08:26 -05001212 self.tests = {}
1213 self.common = {}
1214 if 'tests' in self.data:
1215 self.tests = self.data['tests']
1216 if 'common' in self.data:
1217 self.common = self.data['common']
Andrew Boie6acbe632015-07-17 12:03:52 -07001218
1219 def _cast_value(self, value, typestr):
Anas Nashif3ba1d432017-12-05 15:28:44 -05001220 if isinstance(value, str):
Anas Nashifa792a3d2017-04-04 18:47:49 -04001221 v = value.strip()
Andrew Boie6acbe632015-07-17 12:03:52 -07001222 if typestr == "str":
1223 return v
1224
1225 elif typestr == "float":
Anas Nashifa792a3d2017-04-04 18:47:49 -04001226 return float(value)
Andrew Boie6acbe632015-07-17 12:03:52 -07001227
1228 elif typestr == "int":
Anas Nashifa792a3d2017-04-04 18:47:49 -04001229 return int(value)
Andrew Boie6acbe632015-07-17 12:03:52 -07001230
1231 elif typestr == "bool":
Anas Nashifa792a3d2017-04-04 18:47:49 -04001232 return value
Andrew Boie6acbe632015-07-17 12:03:52 -07001233
Anas Nashif3ba1d432017-12-05 15:28:44 -05001234 elif typestr.startswith("list") and isinstance(value, list):
Anas Nashiffa695d22017-10-04 16:14:27 -04001235 return value
Anas Nashif3ba1d432017-12-05 15:28:44 -05001236 elif typestr.startswith("list") and isinstance(value, str):
Andrew Boie6acbe632015-07-17 12:03:52 -07001237 vs = v.split()
1238 if len(typestr) > 4 and typestr[4] == ":":
1239 return [self._cast_value(vsi, typestr[5:]) for vsi in vs]
1240 else:
1241 return vs
1242
1243 elif typestr.startswith("set"):
1244 vs = v.split()
1245 if len(typestr) > 3 and typestr[3] == ":":
1246 return set([self._cast_value(vsi, typestr[4:]) for vsi in vs])
1247 else:
1248 return set(vs)
1249
Anas Nashif576be982017-12-23 20:20:27 -05001250 elif typestr.startswith("map"):
1251 return value
Andrew Boie6acbe632015-07-17 12:03:52 -07001252 else:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001253 raise ConfigurationError(
1254 self.filename, "unknown type '%s'" % value)
Andrew Boie6acbe632015-07-17 12:03:52 -07001255
Anas Nashifb4754ed2017-12-05 17:27:58 -05001256 def get_test(self, name, valid_keys):
1257 """Get a dictionary representing the keys/values within a test
Andrew Boie6acbe632015-07-17 12:03:52 -07001258
Anas Nashifb4754ed2017-12-05 17:27:58 -05001259 @param name The test in the .yaml file to retrieve data from
Andrew Boie6acbe632015-07-17 12:03:52 -07001260 @param valid_keys A dictionary representing the intended semantics
Anas Nashifb4754ed2017-12-05 17:27:58 -05001261 for this test. Each key in this dictionary is a key that could
Anas Nashifa792a3d2017-04-04 18:47:49 -04001262 be specified, if a key is given in the .yaml file which isn't in
Andrew Boie6acbe632015-07-17 12:03:52 -07001263 here, it will generate an error. Each value in this dictionary
1264 is another dictionary containing metadata:
1265
1266 "default" - Default value if not given
1267 "type" - Data type to convert the text value to. Simple types
1268 supported are "str", "float", "int", "bool" which will get
1269 converted to respective Python data types. "set" and "list"
1270 may also be specified which will split the value by
1271 whitespace (but keep the elements as strings). finally,
1272 "list:<type>" and "set:<type>" may be given which will
1273 perform a type conversion after splitting the value up.
1274 "required" - If true, raise an error if not defined. If false
1275 and "default" isn't specified, a type conversion will be
1276 done on an empty string
Anas Nashifb4754ed2017-12-05 17:27:58 -05001277 @return A dictionary containing the test key-value pairs with
Andrew Boie6acbe632015-07-17 12:03:52 -07001278 type conversion and default values filled in per valid_keys
1279 """
1280
1281 d = {}
Anas Nashif255625b2017-12-05 15:08:26 -05001282 for k, v in self.common.items():
Anas Nashiffa695d22017-10-04 16:14:27 -04001283 d[k] = v
Anas Nashif75547e22018-02-24 08:32:14 -06001284
Anas Nashifb4754ed2017-12-05 17:27:58 -05001285 for k, v in self.tests[name].items():
Andrew Boie6acbe632015-07-17 12:03:52 -07001286 if k not in valid_keys:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001287 raise ConfigurationError(
1288 self.filename,
1289 "Unknown config key '%s' in definition for '%s'" %
Anas Nashifb4754ed2017-12-05 17:27:58 -05001290 (k, name))
Andrew Boie6acbe632015-07-17 12:03:52 -07001291
Anas Nashiffa695d22017-10-04 16:14:27 -04001292 if k in d:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001293 if isinstance(d[k], str):
Anas Nashiffa695d22017-10-04 16:14:27 -04001294 d[k] += " " + v
1295 else:
1296 d[k] = v
Anas Nashif75547e22018-02-24 08:32:14 -06001297
Andrew Boie08ce5a52016-02-22 13:28:10 -08001298 for k, kinfo in valid_keys.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07001299 if k not in d:
1300 if "required" in kinfo:
1301 required = kinfo["required"]
1302 else:
1303 required = False
1304
1305 if required:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001306 raise ConfigurationError(
1307 self.filename,
Anas Nashifb4754ed2017-12-05 17:27:58 -05001308 "missing required value for '%s' in test '%s'" %
1309 (k, name))
Andrew Boie6acbe632015-07-17 12:03:52 -07001310 else:
1311 if "default" in kinfo:
1312 default = kinfo["default"]
1313 else:
1314 default = self._cast_value("", kinfo["type"])
1315 d[k] = default
1316 else:
1317 try:
1318 d[k] = self._cast_value(d[k], kinfo["type"])
Andrew Boie08ce5a52016-02-22 13:28:10 -08001319 except ValueError as ve:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001320 raise ConfigurationError(
Anas Nashifb4754ed2017-12-05 17:27:58 -05001321 self.filename, "bad %s value '%s' for key '%s' in name '%s'" %
1322 (kinfo["type"], d[k], k, name))
Andrew Boie6acbe632015-07-17 12:03:52 -07001323
1324 return d
1325
1326
1327class Platform:
1328 """Class representing metadata for a particular platform
1329
Anas Nashifc7406082015-12-13 15:00:31 -05001330 Maps directly to BOARD when building"""
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001331
1332 yaml_platform_schema = scl.yaml_load(
Anas Nashif3ba1d432017-12-05 15:28:44 -05001333 os.path.join(
Oleg Zhurakivskyy42822082018-08-17 14:54:41 +03001334 ZEPHYR_BASE,
Anas Nashif3ba1d432017-12-05 15:28:44 -05001335 "scripts",
1336 "sanity_chk",
1337 "sanitycheck-platform-schema.yaml"))
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001338
Anas Nashifa792a3d2017-04-04 18:47:49 -04001339 def __init__(self, cfile):
Andrew Boie6acbe632015-07-17 12:03:52 -07001340 """Constructor.
1341
Anas Nashif877d3ca2017-12-05 17:39:29 -05001342 @param cfile Path to platform configuration file, which gives
1343 info about the platform to be added.
Andrew Boie6acbe632015-07-17 12:03:52 -07001344 """
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001345 scp = SanityConfigParser(cfile, self.yaml_platform_schema)
Anas Nashif255625b2017-12-05 15:08:26 -05001346 data = scp.data
Anas Nashifa792a3d2017-04-04 18:47:49 -04001347
Anas Nashif255625b2017-12-05 15:08:26 -05001348 self.name = data['identifier']
Anas Nashiff3d48e12018-07-24 08:14:42 -05001349 self.sanitycheck = data.get("sanitycheck", True)
Anas Nashifa792a3d2017-04-04 18:47:49 -04001350 # if no RAM size is specified by the board, take a default of 128K
Anas Nashif255625b2017-12-05 15:08:26 -05001351 self.ram = data.get("ram", 128)
1352 testing = data.get("testing", {})
Anas Nashifa792a3d2017-04-04 18:47:49 -04001353 self.ignore_tags = testing.get("ignore_tags", [])
1354 self.default = testing.get("default", False)
1355 # if no flash size is specified by the board, take a default of 512K
Anas Nashif255625b2017-12-05 15:08:26 -05001356 self.flash = data.get("flash", 512)
Anas Nashif95276932017-07-31 08:47:41 -04001357 self.supported = set()
Anas Nashif255625b2017-12-05 15:08:26 -05001358 for supp_feature in data.get("supported", []):
Anas Nashif95276932017-07-31 08:47:41 -04001359 for item in supp_feature.split(":"):
1360 self.supported.add(item)
1361
Anas Nashif8acdbd72018-01-04 14:15:22 -05001362 self.qemu_support = True if data.get('simulation', "na") == 'qemu' else False
Anas Nashif255625b2017-12-05 15:08:26 -05001363 self.arch = data['arch']
Anas Nashifcc164222017-12-26 11:02:46 -05001364 self.type = data.get('type', "na")
Anas Nashif8acdbd72018-01-04 14:15:22 -05001365 self.simulation = data.get('simulation', "na")
Anas Nashif255625b2017-12-05 15:08:26 -05001366 self.supported_toolchains = data.get("toolchain", [])
Anas Nashif924a4e72018-10-18 12:25:55 -04001367 self.env = data.get("env", [])
1368 self.env_satisfied = True
1369 for env in self.env:
1370 if os.environ.get(env, None) == None:
1371 self.env_satisfied = False
Andrew Boie41878222016-11-03 11:58:53 -07001372 self.defconfig = None
Andrew Boie6acbe632015-07-17 12:03:52 -07001373 pass
1374
Andrew Boie6acbe632015-07-17 12:03:52 -07001375 def __repr__(self):
Anas Nashifa792a3d2017-04-04 18:47:49 -04001376 return "<%s on %s>" % (self.name, self.arch)
Andrew Boie6acbe632015-07-17 12:03:52 -07001377
1378
1379class Architecture:
1380 """Class representing metadata for a particular architecture
1381 """
Anas Nashif3ba1d432017-12-05 15:28:44 -05001382
Anas Nashifa792a3d2017-04-04 18:47:49 -04001383 def __init__(self, name, platforms):
Andrew Boie6acbe632015-07-17 12:03:52 -07001384 """Architecture constructor
1385
Anas Nashif877d3ca2017-12-05 17:39:29 -05001386 @param name String name for this architecture
1387 @param platforms list of platforms belonging to this architecture
Andrew Boie6acbe632015-07-17 12:03:52 -07001388 """
Anas Nashifa792a3d2017-04-04 18:47:49 -04001389 self.platforms = platforms
Andrew Boie6acbe632015-07-17 12:03:52 -07001390
Anas Nashifa792a3d2017-04-04 18:47:49 -04001391 self.name = name
Andrew Boie6acbe632015-07-17 12:03:52 -07001392
1393 def __repr__(self):
1394 return "<arch %s>" % self.name
1395
1396
1397class TestCase:
1398 """Class representing a test application
1399 """
Anas Nashif3ba1d432017-12-05 15:28:44 -05001400
Anas Nashif7fae29c2017-10-09 13:19:12 -04001401 def __init__(self, testcase_root, workdir, name, tc_dict, yamlfile):
Andrew Boie6acbe632015-07-17 12:03:52 -07001402 """TestCase constructor.
1403
Anas Nashif877d3ca2017-12-05 17:39:29 -05001404 This gets called by TestSuite as it finds and reads test yaml files.
Anas Nashifa792a3d2017-04-04 18:47:49 -04001405 Multiple TestCase instances may be generated from a single testcase.yaml,
Anas Nashif877d3ca2017-12-05 17:39:29 -05001406 each one corresponds to an entry within that file.
Andrew Boie6acbe632015-07-17 12:03:52 -07001407
Andrew Boie6acbe632015-07-17 12:03:52 -07001408 We need to have a unique name for every single test case. Since
Anas Nashifa792a3d2017-04-04 18:47:49 -04001409 a testcase.yaml can define multiple tests, the canonical name for
Andrew Boie6acbe632015-07-17 12:03:52 -07001410 the test case is <workdir>/<name>.
1411
1412 @param testcase_root Absolute path to the root directory where
1413 all the test cases live
1414 @param workdir Relative path to the project directory for this
1415 test application from the test_case root.
Anas Nashif877d3ca2017-12-05 17:39:29 -05001416 @param name Name of this test case, corresponding to the entry name
Andrew Boie6acbe632015-07-17 12:03:52 -07001417 in the test case configuration file. For many test cases that just
1418 define one test, can be anything and is usually "test". This is
1419 really only used to distinguish between different cases when
Anas Nashifa792a3d2017-04-04 18:47:49 -04001420 the testcase.yaml defines multiple tests
Anas Nashif877d3ca2017-12-05 17:39:29 -05001421 @param tc_dict Dictionary with test values for this test case
Anas Nashifa792a3d2017-04-04 18:47:49 -04001422 from the testcase.yaml file
Andrew Boie6acbe632015-07-17 12:03:52 -07001423 """
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001424 self.test_path = os.path.join(testcase_root, workdir)
1425
Anas Nashifaae71d72018-04-21 22:26:48 -05001426 self.id = name
1427 self.cases = []
Jaakko Hannikainenca505f82016-08-22 15:03:46 +03001428 self.type = tc_dict["type"]
Andrew Boie6acbe632015-07-17 12:03:52 -07001429 self.tags = tc_dict["tags"]
1430 self.extra_args = tc_dict["extra_args"]
Anas Nashiffa695d22017-10-04 16:14:27 -04001431 self.extra_configs = tc_dict["extra_configs"]
Andrew Boie6acbe632015-07-17 12:03:52 -07001432 self.arch_whitelist = tc_dict["arch_whitelist"]
Anas Nashif30d13872015-10-05 10:02:45 -04001433 self.arch_exclude = tc_dict["arch_exclude"]
Anas Nashif2bd99bc2015-10-12 13:10:57 -04001434 self.skip = tc_dict["skip"]
Anas Nashif30d13872015-10-05 10:02:45 -04001435 self.platform_exclude = tc_dict["platform_exclude"]
Andrew Boie6acbe632015-07-17 12:03:52 -07001436 self.platform_whitelist = tc_dict["platform_whitelist"]
Anas Nashifb17e1ca2017-06-27 18:05:30 -04001437 self.toolchain_exclude = tc_dict["toolchain_exclude"]
1438 self.toolchain_whitelist = tc_dict["toolchain_whitelist"]
Andrew Boie3ea78922016-03-24 14:46:00 -07001439 self.tc_filter = tc_dict["filter"]
Andrew Boie6acbe632015-07-17 12:03:52 -07001440 self.timeout = tc_dict["timeout"]
Anas Nashifb0f3ae02017-12-08 12:48:39 -05001441 self.harness = tc_dict["harness"]
Anas Nashif576be982017-12-23 20:20:27 -05001442 self.harness_config = tc_dict["harness_config"]
Andrew Boie6acbe632015-07-17 12:03:52 -07001443 self.build_only = tc_dict["build_only"]
Anas Nashifa792a3d2017-04-04 18:47:49 -04001444 self.build_on_all = tc_dict["build_on_all"]
Andrew Boie6bb087c2016-02-10 13:39:00 -08001445 self.slow = tc_dict["slow"]
Anas Nashifa792a3d2017-04-04 18:47:49 -04001446 self.min_ram = tc_dict["min_ram"]
1447 self.depends_on = tc_dict["depends_on"]
1448 self.min_flash = tc_dict["min_flash"]
Andrew Boie52fef672016-11-29 12:21:59 -08001449 self.extra_sections = tc_dict["extra_sections"]
Anas Nashifbd166f42017-09-02 12:32:08 -04001450
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001451 self.name = self.get_unique(testcase_root, workdir, name)
Alberto Escolar Piedras2151b862018-01-29 15:09:21 +01001452
Anas Nashif2cf0df02015-10-10 09:29:43 -04001453 self.defconfig = {}
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001454 self.dt_config = {}
Anas Nashif7fae29c2017-10-09 13:19:12 -04001455 self.yamlfile = yamlfile
Andrew Boie6acbe632015-07-17 12:03:52 -07001456
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001457
1458 def get_unique(self, testcase_root, workdir, name):
1459
1460 if ZEPHYR_BASE in testcase_root:
1461 # This is a Zephyr Test, so include path in name for uniqueness
1462 # FIXME: We should not depend on path of test for unique names.
1463
1464 zephyr_base = os.path.join(os.path.realpath(ZEPHYR_BASE))
1465 short_path = os.path.normpath(testcase_root.replace(zephyr_base + "/", ""))
1466 else:
1467 short_path = ""
1468
1469 unique = os.path.normpath(os.path.join(short_path, workdir, name))
1470 return unique
1471
Anas Nashifaae71d72018-04-21 22:26:48 -05001472 def scan_file(self, inf_name):
Anas Nashifaae71d72018-04-21 22:26:48 -05001473 suite_regex = re.compile(
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001474 # do not match until end-of-line, otherwise we won't allow
1475 # stc_regex below to catch the ones that are declared in the same
1476 # line--as we only search starting the end of this match
1477 br"^\s*ztest_test_suite\(\s*(?P<suite_name>[a-zA-Z0-9_]+)\s*,",
Anas Nashifaae71d72018-04-21 22:26:48 -05001478 re.MULTILINE)
1479 stc_regex = re.compile(
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001480 br"^\s*" # empy space at the beginning is ok
1481 # catch the case where it is declared in the same sentence, e.g:
1482 #
1483 # ztest_test_suite(mutex_complex, ztest_user_unit_test(TESTNAME));
1484 br"(?:ztest_test_suite\([a-zA-Z0-9_]+,\s*)?"
1485 # Catch ztest[_user]_unit_test-[_setup_teardown](TESTNAME)
1486 br"ztest_(?:user_)?unit_test(?:_setup_teardown)?"
1487 # Consume the argument that becomes the extra testcse
1488 br"\(\s*"
1489 br"(?P<stc_name>[a-zA-Z0-9_]+)"
1490 # _setup_teardown() variant has two extra arguments that we ignore
1491 br"(?:\s*,\s*[a-zA-Z0-9_]+\s*,\s*[a-zA-Z0-9_]+)?"
1492 br"\s*\)",
1493 # We don't check how it finishes; we don't care
Anas Nashifaae71d72018-04-21 22:26:48 -05001494 re.MULTILINE)
1495 suite_run_regex = re.compile(
1496 br"^\s*ztest_run_test_suite\((?P<suite_name>[a-zA-Z0-9_]+)\)",
1497 re.MULTILINE)
1498 achtung_regex = re.compile(
1499 br"(#ifdef|#endif)",
1500 re.MULTILINE)
1501 warnings = None
1502
1503 with open(inf_name) as inf:
1504 with contextlib.closing(mmap.mmap(inf.fileno(), 0, mmap.MAP_PRIVATE,
1505 mmap.PROT_READ, 0)) as main_c:
Anas Nashifaae71d72018-04-21 22:26:48 -05001506 suite_regex_match = suite_regex.search(main_c)
1507 if not suite_regex_match:
1508 # can't find ztest_test_suite, maybe a client, because
1509 # it includes ztest.h
1510 return None, None
1511
1512 suite_run_match = suite_run_regex.search(main_c)
1513 if not suite_run_match:
1514 raise ValueError("can't find ztest_run_test_suite")
1515
1516 achtung_matches = re.findall(
1517 achtung_regex,
1518 main_c[suite_regex_match.end():suite_run_match.start()])
1519 if achtung_matches:
1520 warnings = "found invalid %s in ztest_test_suite()" \
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001521 % ", ".join(set([
1522 match.decode() for match in achtung_matches
1523 ]))
1524 _matches = re.findall(
Anas Nashifaae71d72018-04-21 22:26:48 -05001525 stc_regex,
1526 main_c[suite_regex_match.end():suite_run_match.start()])
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001527 matches = [ match.decode().replace("test_", "") for match in _matches ]
Anas Nashifaae71d72018-04-21 22:26:48 -05001528 return matches, warnings
1529
1530 def scan_path(self, path):
1531 subcases = []
1532 for filename in glob.glob(os.path.join(path, "src", "*.c")):
1533 try:
1534 _subcases, warnings = self.scan_file(filename)
1535 if warnings:
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001536 error("%s: %s" % (filename, warnings))
Anas Nashifaae71d72018-04-21 22:26:48 -05001537 if _subcases:
1538 subcases += _subcases
1539 except ValueError as e:
1540 error("%s: can't find: %s", filename, e)
1541 return subcases
1542
1543
1544 def parse_subcases(self):
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001545 results = self.scan_path(self.test_path)
Anas Nashifaae71d72018-04-21 22:26:48 -05001546 for sub in results:
Inaky Perez-Gonzalez75e2d902018-04-26 00:17:01 -07001547 name = "{}.{}".format(self.id, sub)
Anas Nashifaae71d72018-04-21 22:26:48 -05001548 self.cases.append(name)
1549
1550
Anas Nashif75547e22018-02-24 08:32:14 -06001551 def __str__(self):
Andrew Boie6acbe632015-07-17 12:03:52 -07001552 return self.name
1553
1554
Andrew Boie6acbe632015-07-17 12:03:52 -07001555class TestInstance:
1556 """Class representing the execution of a particular TestCase on a platform
1557
1558 @param test The TestCase object we want to build/execute
1559 @param platform Platform object that we want to build and run against
1560 @param base_outdir Base directory for all test results. The actual
1561 out directory used is <outdir>/<platform>/<test case name>
1562 """
Anas Nashif3ba1d432017-12-05 15:28:44 -05001563
Anas Nashif37f9dc52018-02-23 08:53:46 -06001564 def __init__(self, test, platform, base_outdir):
Andrew Boie6acbe632015-07-17 12:03:52 -07001565 self.test = test
1566 self.platform = platform
Anas Nashifbd166f42017-09-02 12:32:08 -04001567 self.name = os.path.join(platform.name, test.name)
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001568 self.outdir = os.path.join(base_outdir, platform.name, test.name)
Anas Nashif7dd19ea2018-11-14 08:46:49 -05001569
1570 self.build_only = options.build_only or test.build_only \
1571 or self.check_dependency()
Anas Nashife0a6a0b2018-02-15 20:07:24 -06001572 self.results = {}
Andrew Boie6acbe632015-07-17 12:03:52 -07001573
Anas Nashif7dd19ea2018-11-14 08:46:49 -05001574 def check_dependency(self):
1575 build_only = False
1576 if self.test.harness == 'console':
1577 if "fixture" in self.test.harness_config:
1578 fixture = self.test.harness_config['fixture']
1579 if fixture not in options.fixture:
1580 build_only = True
1581 elif self.test.harness:
1582 build_only = True
1583
1584 return build_only
1585
Anas Nashiffa695d22017-10-04 16:14:27 -04001586 def create_overlay(self):
Anas Nashif3cbffef2018-11-07 23:50:54 -05001587 file = os.path.join(self.outdir, "overlay.conf")
1588 os.makedirs(self.outdir, exist_ok=True)
1589 f = open(file, "w")
1590 content = ""
1591
Anas Nashiffa695d22017-10-04 16:14:27 -04001592 if len(self.test.extra_configs) > 0:
Anas Nashif981f77f2017-10-18 07:53:58 -04001593 content = "\n".join(self.test.extra_configs)
Anas Nashif3cbffef2018-11-07 23:50:54 -05001594 if options.enable_coverage:
1595 content = content + "\nCONFIG_COVERAGE=y"
1596
1597 f.write(content)
1598 f.close()
Anas Nashiffa695d22017-10-04 16:14:27 -04001599
Andrew Boie6acbe632015-07-17 12:03:52 -07001600 def calculate_sizes(self):
1601 """Get the RAM/ROM sizes of a test case.
1602
1603 This can only be run after the instance has been executed by
1604 MakeGenerator, otherwise there won't be any binaries to measure.
1605
1606 @return A SizeCalculator object
1607 """
Anas Nashiffb91ad62017-10-31 08:33:17 -04001608 fns = glob.glob(os.path.join(self.outdir, "zephyr", "*.elf"))
Anas Nashif2f4e1702017-11-24 08:11:25 -05001609 fns.extend(glob.glob(os.path.join(self.outdir, "zephyr", "*.exe")))
Anas Nashiff8dcad42016-10-27 18:10:08 -04001610 fns = [x for x in fns if not x.endswith('_prebuilt.elf')]
Andrew Boie5d4eb782015-10-02 10:04:56 -07001611 if (len(fns) != 1):
1612 raise BuildError("Missing/multiple output ELF binary")
Andrew Boie52fef672016-11-29 12:21:59 -08001613 return SizeCalculator(fns[0], self.test.extra_sections)
Andrew Boie6acbe632015-07-17 12:03:52 -07001614
1615 def __repr__(self):
1616 return "<TestCase %s on %s>" % (self.test.name, self.platform.name)
1617
1618
Andrew Boie4ef16c52015-08-28 12:36:03 -07001619def defconfig_cb(context, goals, goal):
1620 if not goal.failed:
1621 return
1622
1623 info("%sCould not build defconfig for %s%s" %
Anas Nashif3ba1d432017-12-05 15:28:44 -05001624 (COLOR_RED, goal.name, COLOR_NORMAL))
Andrew Boie4ef16c52015-08-28 12:36:03 -07001625 if INLINE_LOGS:
1626 with open(goal.get_error_log()) as fp:
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -08001627 data = fp.read()
1628 sys.stdout.write(data)
1629 if log_file:
1630 log_file.write(data)
Andrew Boie4ef16c52015-08-28 12:36:03 -07001631 else:
Andrew Boie08ce5a52016-02-22 13:28:10 -08001632 info("\tsee: " + COLOR_YELLOW + goal.get_error_log() + COLOR_NORMAL)
Andrew Boie4ef16c52015-08-28 12:36:03 -07001633
Andrew Boie6acbe632015-07-17 12:03:52 -07001634
1635class TestSuite:
Andrew Boie60823c22017-02-10 09:43:07 -08001636 config_re = re.compile('(CONFIG_[A-Za-z0-9_]+)[=]\"?([^\"]*)\"?$')
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001637 dt_re = re.compile('([A-Za-z0-9_]+)[=]\"?([^\"]*)\"?$')
Andrew Boie6acbe632015-07-17 12:03:52 -07001638
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001639 yaml_tc_schema = scl.yaml_load(
Oleg Zhurakivskyy42822082018-08-17 14:54:41 +03001640 os.path.join(ZEPHYR_BASE,
Anas Nashifdb3d55f2017-09-02 06:31:25 -04001641 "scripts", "sanity_chk", "sanitycheck-tc-schema.yaml"))
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001642
Anas Nashif37f9dc52018-02-23 08:53:46 -06001643 def __init__(self, board_root_list, testcase_roots, outdir):
Andrew Boie6acbe632015-07-17 12:03:52 -07001644 # Keep track of which test cases we've filtered out and why
Andrew Boie6acbe632015-07-17 12:03:52 -07001645 self.arches = {}
1646 self.testcases = {}
1647 self.platforms = []
Andrew Boieb391e662015-08-31 15:25:45 -07001648 self.outdir = os.path.abspath(outdir)
Andrew Boie6acbe632015-07-17 12:03:52 -07001649 self.instances = {}
1650 self.goals = None
1651 self.discards = None
Kumar Galac84235e2018-04-10 13:32:51 -05001652 self.load_errors = 0
Andrew Boie6acbe632015-07-17 12:03:52 -07001653
Andrew Boie3d348712016-04-08 11:52:13 -07001654 for testcase_root in testcase_roots:
1655 testcase_root = os.path.abspath(testcase_root)
Andrew Boie6acbe632015-07-17 12:03:52 -07001656
Andrew Boie3d348712016-04-08 11:52:13 -07001657 debug("Reading test case configuration files under %s..." %
1658 testcase_root)
1659 for dirpath, dirnames, filenames in os.walk(testcase_root,
1660 topdown=True):
1661 verbose("scanning %s" % dirpath)
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001662 if 'sample.yaml' in filenames:
1663 filename = 'sample.yaml'
1664 elif 'testcase.yaml' in filenames:
1665 filename = 'testcase.yaml'
1666 else:
1667 continue
Anas Nashif61e21632018-04-08 13:30:16 -05001668
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001669 verbose("Found possible test case in " + dirpath)
1670 dirnames[:] = []
1671 yaml_path = os.path.join(dirpath, filename)
1672 try:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001673 parsed_data = SanityConfigParser(
1674 yaml_path, self.yaml_tc_schema)
Andrew Boie3d348712016-04-08 11:52:13 -07001675
Paul Sokolovsky100474d2018-01-03 17:19:43 +02001676 workdir = os.path.relpath(dirpath, testcase_root)
Inaky Perez-Gonzalez662dde62017-07-24 10:24:35 -07001677
Paul Sokolovsky100474d2018-01-03 17:19:43 +02001678 for name in parsed_data.tests.keys():
1679 tc_dict = parsed_data.get_test(name, testcase_valid_keys)
1680 tc = TestCase(testcase_root, workdir, name, tc_dict,
1681 yaml_path)
Anas Nashifaae71d72018-04-21 22:26:48 -05001682 tc.parse_subcases()
Paul Sokolovsky100474d2018-01-03 17:19:43 +02001683 self.testcases[tc.name] = tc
1684
1685 except Exception as e:
1686 error("E: %s: can't load (skipping): %s" % (yaml_path, e))
Kumar Galac84235e2018-04-10 13:32:51 -05001687 self.load_errors += 1
Paul Sokolovsky100474d2018-01-03 17:19:43 +02001688
Andrew Boie6acbe632015-07-17 12:03:52 -07001689
Anas Nashif86c8e232017-10-09 13:42:28 -04001690 for board_root in board_root_list:
1691 board_root = os.path.abspath(board_root)
1692
Anas Nashif3ba1d432017-12-05 15:28:44 -05001693 debug(
1694 "Reading platform configuration files under %s..." %
1695 board_root)
Anas Nashif8b11a1f2017-11-26 17:08:47 -05001696 for fn in glob.glob(os.path.join(board_root, "*", "*", "*.yaml")):
1697 verbose("Found plaform configuration " + fn)
1698 try:
1699 platform = Platform(fn)
Anas Nashiff3d48e12018-07-24 08:14:42 -05001700 if platform.sanitycheck:
1701 self.platforms.append(platform)
Anas Nashif8b11a1f2017-11-26 17:08:47 -05001702 except RuntimeError as e:
1703 error("E: %s: can't load: %s" % (fn, e))
Kumar Galac84235e2018-04-10 13:32:51 -05001704 self.load_errors += 1
Andrew Boie6acbe632015-07-17 12:03:52 -07001705
Anas Nashifa792a3d2017-04-04 18:47:49 -04001706 arches = []
1707 for p in self.platforms:
1708 arches.append(p.arch)
1709 for a in list(set(arches)):
Anas Nashif3ba1d432017-12-05 15:28:44 -05001710 aplatforms = [p for p in self.platforms if p.arch == a]
Anas Nashifa792a3d2017-04-04 18:47:49 -04001711 arch = Architecture(a, aplatforms)
1712 self.arches[a] = arch
1713
Andrew Boie6acbe632015-07-17 12:03:52 -07001714 self.instances = {}
1715
1716 def get_last_failed(self):
Anas Nashifb4bdd662018-08-15 17:12:28 -05001717
1718 try:
1719 if not os.path.exists(LAST_SANITY):
1720 raise SanityRuntimeError("Couldn't find last sanity run.")
1721 except Exception as e:
1722 print(str(e))
1723 sys.exit(2)
1724
Andrew Boie6acbe632015-07-17 12:03:52 -07001725 result = []
1726 with open(LAST_SANITY, "r") as fp:
1727 cr = csv.DictReader(fp)
1728 for row in cr:
1729 if row["passed"] == "True":
1730 continue
1731 test = row["test"]
1732 platform = row["platform"]
1733 result.append((test, platform))
1734 return result
1735
Anas Nashifbd166f42017-09-02 12:32:08 -04001736 def load_from_file(self, file):
Anas Nashifb4bdd662018-08-15 17:12:28 -05001737 try:
1738 if not os.path.exists(file):
1739 raise SanityRuntimeError(
1740 "Couldn't find input file with list of tests.")
1741 except Exception as e:
1742 print(str(e))
1743 sys.exit(2)
1744
Anas Nashifbd166f42017-09-02 12:32:08 -04001745 with open(file, "r") as fp:
1746 cr = csv.reader(fp)
1747 instance_list = []
1748 for row in cr:
1749 name = os.path.join(row[0], row[1])
1750 platforms = self.arches[row[3]].platforms
1751 myp = None
1752 for p in platforms:
1753 if p.name == row[2]:
1754 myp = p
1755 break
1756 instance = TestInstance(self.testcases[name], myp, self.outdir)
Anas Nashiffa695d22017-10-04 16:14:27 -04001757 instance.create_overlay()
Anas Nashifbd166f42017-09-02 12:32:08 -04001758 instance_list.append(instance)
1759 self.add_instances(instance_list)
1760
Anas Nashif4f028882017-12-30 11:48:43 -05001761 def apply_filters(self):
Anas Nashifbd166f42017-09-02 12:32:08 -04001762
Anas Nashif7fe35cf2018-02-15 07:20:18 -06001763 toolchain = os.environ.get("ZEPHYR_TOOLCHAIN_VARIANT", None) or \
1764 os.environ.get("ZEPHYR_GCC_VARIANT", None)
Anas Nashifb4bdd662018-08-15 17:12:28 -05001765
Sebastian Bøe5681f872018-10-12 16:03:49 +02001766 if toolchain == "gccarmemb":
1767 # Remove this translation when gccarmemb is no longer supported.
1768 toolchain = "gnuarmemb"
Anas Nashifb4bdd662018-08-15 17:12:28 -05001769
1770 try:
1771 if not toolchain:
1772 raise SanityRuntimeError("E: Variable ZEPHYR_TOOLCHAIN_VARIANT is not defined")
1773 except Exception as e:
1774 print(str(e))
1775 sys.exit(2)
Anas Nashif7fe35cf2018-02-15 07:20:18 -06001776
1777
Andrew Boie6acbe632015-07-17 12:03:52 -07001778 instances = []
1779 discards = {}
Anas Nashif4f028882017-12-30 11:48:43 -05001780 platform_filter = options.platform
1781 last_failed = options.only_failed
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05001782 testcase_filter = run_individual_tests
Anas Nashif4f028882017-12-30 11:48:43 -05001783 arch_filter = options.arch
1784 tag_filter = options.tag
1785 exclude_tag = options.exclude_tag
1786 config_filter = options.config
1787 extra_args = options.extra_args
1788 all_plats = options.all
Anas Nashiffa695d22017-10-04 16:14:27 -04001789
Andrew Boie6acbe632015-07-17 12:03:52 -07001790 verbose("platform filter: " + str(platform_filter))
1791 verbose(" arch_filter: " + str(arch_filter))
1792 verbose(" tag_filter: " + str(tag_filter))
Anas Nashifdfa86e22016-10-24 17:08:56 -04001793 verbose(" exclude_tag: " + str(exclude_tag))
Andrew Boie6acbe632015-07-17 12:03:52 -07001794 verbose(" config_filter: " + str(config_filter))
1795
1796 if last_failed:
1797 failed_tests = self.get_last_failed()
1798
Andrew Boie821d8322016-03-22 10:08:35 -07001799 default_platforms = False
1800
1801 if all_plats:
1802 info("Selecting all possible platforms per test case")
1803 # When --all used, any --platform arguments ignored
1804 platform_filter = []
1805 elif not platform_filter:
Andrew Boie6acbe632015-07-17 12:03:52 -07001806 info("Selecting default platforms per test case")
1807 default_platforms = True
Andrew Boie6acbe632015-07-17 12:03:52 -07001808
Sebastian Bøe781e3982017-11-09 11:43:33 +01001809 mg = MakeGenerator(self.outdir)
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001810 defconfig_list = {}
1811 dt_list = {}
Andrew Boie08ce5a52016-02-22 13:28:10 -08001812 for tc_name, tc in self.testcases.items():
1813 for arch_name, arch in self.arches.items():
Anas Nashif2cf0df02015-10-10 09:29:43 -04001814 for plat in arch.platforms:
1815 instance = TestInstance(tc, plat, self.outdir)
1816
Jaakko Hannikainenca505f82016-08-22 15:03:46 +03001817 if (arch_name == "unit") != (tc.type == "unit"):
1818 continue
1819
Anas Nashifbfab06b2017-06-22 09:22:24 -04001820 if tc.build_on_all and not platform_filter:
Anas Nashifa792a3d2017-04-04 18:47:49 -04001821 platform_filter = []
1822
Anas Nashif2bd99bc2015-10-12 13:10:57 -04001823 if tc.skip:
1824 continue
1825
Anas Nashif2cf0df02015-10-10 09:29:43 -04001826 if tag_filter and not tc.tags.intersection(tag_filter):
1827 continue
1828
Anas Nashifdfa86e22016-10-24 17:08:56 -04001829 if exclude_tag and tc.tags.intersection(exclude_tag):
1830 continue
1831
Anas Nashif2cf0df02015-10-10 09:29:43 -04001832 if testcase_filter and tc_name not in testcase_filter:
1833 continue
1834
Anas Nashif3ba1d432017-12-05 15:28:44 -05001835 if last_failed and (
1836 tc.name, plat.name) not in failed_tests:
Anas Nashif2cf0df02015-10-10 09:29:43 -04001837 continue
1838
1839 if arch_filter and arch_name not in arch_filter:
1840 continue
1841
1842 if tc.arch_whitelist and arch.name not in tc.arch_whitelist:
1843 continue
1844
1845 if tc.arch_exclude and arch.name in tc.arch_exclude:
1846 continue
1847
1848 if tc.platform_exclude and plat.name in tc.platform_exclude:
1849 continue
1850
Anas Nashifb17e1ca2017-06-27 18:05:30 -04001851 if tc.toolchain_exclude and toolchain in tc.toolchain_exclude:
1852 continue
1853
Anas Nashif2cf0df02015-10-10 09:29:43 -04001854 if platform_filter and plat.name not in platform_filter:
1855 continue
1856
Anas Nashif62224182017-08-09 23:55:53 -04001857 if plat.ram < tc.min_ram:
Anas Nashifa792a3d2017-04-04 18:47:49 -04001858 continue
1859
1860 if set(plat.ignore_tags) & tc.tags:
1861 continue
1862
Kumar Gala5141d522017-07-07 08:05:48 -05001863 if tc.depends_on:
Anas Nashif3ba1d432017-12-05 15:28:44 -05001864 dep_intersection = tc.depends_on.intersection(
1865 set(plat.supported))
Kumar Gala5141d522017-07-07 08:05:48 -05001866 if dep_intersection != set(tc.depends_on):
1867 continue
Anas Nashifa792a3d2017-04-04 18:47:49 -04001868
1869 if plat.flash < tc.min_flash:
1870 continue
1871
Anas Nashif2cf0df02015-10-10 09:29:43 -04001872 if tc.platform_whitelist and plat.name not in tc.platform_whitelist:
1873 continue
1874
Anas Nashifb17e1ca2017-06-27 18:05:30 -04001875 if tc.toolchain_whitelist and toolchain not in tc.toolchain_whitelist:
1876 continue
1877
Anas Nashif924a4e72018-10-18 12:25:55 -04001878 if (plat.env_satisfied and tc.tc_filter
1879 and (plat.default or all_plats or platform_filter)
Anas Nashif07d54c02018-07-21 19:29:08 -05001880 and (toolchain in plat.supported_toolchains or options.force_toolchain)):
Anas Nashif8ea9d022015-11-10 12:24:20 -05001881 args = tc.extra_args[:]
Anas Nashiffb91ad62017-10-31 08:33:17 -04001882 args.append("BOARD={}".format(plat.name))
Andrew Boieba612002016-09-01 10:41:03 -07001883 args.extend(extra_args)
Anas Nashif2cf0df02015-10-10 09:29:43 -04001884 # FIXME would be nice to use a common outdir for this so that
Andrew Boie41878222016-11-03 11:58:53 -07001885 # conf, gen_idt, etc aren't rebuilt for every combination,
David B. Kinder29963c32017-06-16 12:32:42 -07001886 # need a way to avoid different Make processes from clobbering
Anas Nashif3ba1d432017-12-05 15:28:44 -05001887 # each other since they all try to build them
1888 # simultaneously
Anas Nashif2cf0df02015-10-10 09:29:43 -04001889
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001890 o = os.path.join(self.outdir, plat.name, tc.name)
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001891 generated_dt_confg = "include/generated/generated_dts_board.conf"
1892 dt_config_path = os.path.join(o, "zephyr", generated_dt_confg)
1893 dt_list[tc, plat, tc.name.split("/")[-1]] = dt_config_path
1894 defconfig_list[tc, plat, tc.name.split("/")[-1]] = os.path.join(o, "zephyr", ".config")
Anas Nashif13773752018-07-06 18:20:23 -05001895 goal = "_".join([plat.name, "_".join(tc.name.split("/")), "config-sanitycheck"])
Anas Nashiff18ad9d2018-11-20 09:03:17 -05001896 mg.add_build_goal(goal, os.path.join(ZEPHYR_BASE, tc.test_path),
Anas Nashif13773752018-07-06 18:20:23 -05001897 o, args, "config-sanitycheck.log", make_args="config-sanitycheck")
Anas Nashif2cf0df02015-10-10 09:29:43 -04001898
1899 info("Building testcase defconfigs...")
1900 results = mg.execute(defconfig_cb)
1901
Andrew Boie08ce5a52016-02-22 13:28:10 -08001902 for name, goal in results.items():
Anas Nashifb4bdd662018-08-15 17:12:28 -05001903 try:
1904 if goal.failed:
1905 raise SanityRuntimeError("Couldn't build some defconfigs")
1906 except Exception as e:
1907 error(str(e))
1908 sys.exit(2)
1909
Anas Nashif2cf0df02015-10-10 09:29:43 -04001910
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001911 for k, out_config in defconfig_list.items():
Andrew Boie41878222016-11-03 11:58:53 -07001912 test, plat, name = k
Anas Nashif2cf0df02015-10-10 09:29:43 -04001913 defconfig = {}
1914 with open(out_config, "r") as fp:
1915 for line in fp.readlines():
1916 m = TestSuite.config_re.match(line)
1917 if not m:
Andrew Boie3ea78922016-03-24 14:46:00 -07001918 if line.strip() and not line.startswith("#"):
1919 sys.stderr.write("Unrecognized line %s\n" % line)
Anas Nashif2cf0df02015-10-10 09:29:43 -04001920 continue
1921 defconfig[m.group(1)] = m.group(2).strip()
Andrew Boie41878222016-11-03 11:58:53 -07001922 test.defconfig[plat] = defconfig
Anas Nashif2cf0df02015-10-10 09:29:43 -04001923
Anas Nashif1c65b6b2018-12-02 19:12:21 -05001924 for k, out_config in dt_list.items():
1925 if not os.path.exists(out_config):
1926 continue
1927
1928 test, plat, name = k
1929 dt_conf = {}
1930 with open(out_config, "r") as fp:
1931 for line in fp.readlines():
1932 m = TestSuite.dt_re.match(line)
1933 if not m:
1934 if line.strip() and not line.startswith("#"):
1935 sys.stderr.write("Unrecognized line %s\n" % line)
1936 continue
1937 dt_conf[m.group(1)] = m.group(2).strip()
1938 test.dt_config[plat] = dt_conf
1939
Andrew Boie08ce5a52016-02-22 13:28:10 -08001940 for tc_name, tc in self.testcases.items():
1941 for arch_name, arch in self.arches.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07001942 instance_list = []
1943 for plat in arch.platforms:
1944 instance = TestInstance(tc, plat, self.outdir)
1945
Jaakko Hannikainenca505f82016-08-22 15:03:46 +03001946 if (arch_name == "unit") != (tc.type == "unit"):
1947 # Discard silently
1948 continue
1949
Anas Nashif2bd99bc2015-10-12 13:10:57 -04001950 if tc.skip:
1951 discards[instance] = "Skip filter"
1952 continue
1953
Anas Nashifbfab06b2017-06-22 09:22:24 -04001954 if tc.build_on_all and not platform_filter:
Anas Nashifa792a3d2017-04-04 18:47:49 -04001955 platform_filter = []
1956
Andrew Boie6acbe632015-07-17 12:03:52 -07001957 if tag_filter and not tc.tags.intersection(tag_filter):
1958 discards[instance] = "Command line testcase tag filter"
1959 continue
1960
Anas Nashifdfa86e22016-10-24 17:08:56 -04001961 if exclude_tag and tc.tags.intersection(exclude_tag):
1962 discards[instance] = "Command line testcase exclude filter"
1963 continue
1964
Andrew Boie6acbe632015-07-17 12:03:52 -07001965 if testcase_filter and tc_name not in testcase_filter:
1966 discards[instance] = "Testcase name filter"
1967 continue
1968
Anas Nashif3ba1d432017-12-05 15:28:44 -05001969 if last_failed and (
1970 tc.name, plat.name) not in failed_tests:
Andrew Boie6acbe632015-07-17 12:03:52 -07001971 discards[instance] = "Passed or skipped during last run"
1972 continue
1973
1974 if arch_filter and arch_name not in arch_filter:
1975 discards[instance] = "Command line testcase arch filter"
1976 continue
1977
1978 if tc.arch_whitelist and arch.name not in tc.arch_whitelist:
1979 discards[instance] = "Not in test case arch whitelist"
1980 continue
1981
Anas Nashif30d13872015-10-05 10:02:45 -04001982 if tc.arch_exclude and arch.name in tc.arch_exclude:
1983 discards[instance] = "In test case arch exclude"
1984 continue
1985
1986 if tc.platform_exclude and plat.name in tc.platform_exclude:
1987 discards[instance] = "In test case platform exclude"
1988 continue
1989
Anas Nashifb17e1ca2017-06-27 18:05:30 -04001990 if tc.toolchain_exclude and toolchain in tc.toolchain_exclude:
1991 discards[instance] = "In test case toolchain exclude"
1992 continue
1993
Andrew Boie6acbe632015-07-17 12:03:52 -07001994 if platform_filter and plat.name not in platform_filter:
1995 discards[instance] = "Command line platform filter"
1996 continue
1997
1998 if tc.platform_whitelist and plat.name not in tc.platform_whitelist:
1999 discards[instance] = "Not in testcase platform whitelist"
2000 continue
2001
Anas Nashifb17e1ca2017-06-27 18:05:30 -04002002 if tc.toolchain_whitelist and toolchain not in tc.toolchain_whitelist:
2003 discards[instance] = "Not in testcase toolchain whitelist"
2004 continue
2005
Anas Nashif924a4e72018-10-18 12:25:55 -04002006 if not plat.env_satisfied:
2007 discards[instance] = "Environment ({}) not satisfied".format(", ".join(plat.env))
2008 continue
2009
2010 if not options.force_toolchain \
2011 and toolchain and (toolchain not in plat.supported_toolchains) \
2012 and tc.type != 'unit':
Javier B Perez4b554ba2016-08-15 13:25:33 -05002013 discards[instance] = "Not supported by the toolchain"
2014 continue
2015
Anas Nashif62224182017-08-09 23:55:53 -04002016 if plat.ram < tc.min_ram:
Anas Nashifa792a3d2017-04-04 18:47:49 -04002017 discards[instance] = "Not enough RAM"
2018 continue
2019
Kumar Gala5141d522017-07-07 08:05:48 -05002020 if tc.depends_on:
Anas Nashif07d54c02018-07-21 19:29:08 -05002021 dep_intersection = tc.depends_on.intersection(set(plat.supported))
Kumar Gala5141d522017-07-07 08:05:48 -05002022 if dep_intersection != set(tc.depends_on):
2023 discards[instance] = "No hardware support"
2024 continue
Anas Nashifa792a3d2017-04-04 18:47:49 -04002025
Anas Nashif62224182017-08-09 23:55:53 -04002026 if plat.flash < tc.min_flash:
Anas Nashifa792a3d2017-04-04 18:47:49 -04002027 discards[instance] = "Not enough FLASH"
2028 continue
2029
2030 if set(plat.ignore_tags) & tc.tags:
2031 discards[instance] = "Excluded tags per platform"
2032 continue
2033
Anas Nashif674bb282018-01-09 09:12:15 -05002034 defconfig = {
Anas Nashif674bb282018-01-09 09:12:15 -05002035 "ARCH": arch.name,
2036 "PLATFORM": plat.name
2037 }
Javier B Perez79414542016-08-08 12:24:59 -05002038 defconfig.update(os.environ)
Andrew Boie41878222016-11-03 11:58:53 -07002039 for p, tdefconfig in tc.defconfig.items():
2040 if p == plat:
Andrew Boie3ea78922016-03-24 14:46:00 -07002041 defconfig.update(tdefconfig)
Anas Nashif2cf0df02015-10-10 09:29:43 -04002042 break
2043
Anas Nashif1c65b6b2018-12-02 19:12:21 -05002044 for p, tdefconfig in tc.dt_config.items():
2045 if p == plat:
2046 defconfig.update(tdefconfig)
2047 break
2048
Andrew Boie3ea78922016-03-24 14:46:00 -07002049 if tc.tc_filter:
2050 try:
2051 res = expr_parser.parse(tc.tc_filter, defconfig)
Andrew Boiec09b4b82017-04-18 11:46:07 -07002052 except (ValueError, SyntaxError) as se:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002053 sys.stderr.write(
2054 "Failed processing %s\n" % tc.yamlfile)
Andrew Boie3ea78922016-03-24 14:46:00 -07002055 raise se
2056 if not res:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002057 discards[instance] = (
2058 "defconfig doesn't satisfy expression '%s'" %
2059 tc.tc_filter)
Andrew Boie3ea78922016-03-24 14:46:00 -07002060 continue
Anas Nashif2cf0df02015-10-10 09:29:43 -04002061
Andrew Boie6acbe632015-07-17 12:03:52 -07002062 instance_list.append(instance)
2063
2064 if not instance_list:
2065 # Every platform in this arch was rejected already
2066 continue
2067
Anas Nashifa792a3d2017-04-04 18:47:49 -04002068 if default_platforms and not tc.build_on_all:
2069 if not tc.platform_whitelist:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002070 instances = list(
2071 filter(
2072 lambda tc: tc.platform.default,
2073 instance_list))
Anas Nashifa792a3d2017-04-04 18:47:49 -04002074 self.add_instances(instances)
2075 else:
Anas Nashifab747062017-12-05 17:59:01 -05002076 self.add_instances(instance_list[:1])
Anas Nashifa792a3d2017-04-04 18:47:49 -04002077
Anas Nashif3ba1d432017-12-05 15:28:44 -05002078 for instance in list(
2079 filter(lambda tc: not tc.platform.default, instance_list)):
Anas Nashifa792a3d2017-04-04 18:47:49 -04002080 discards[instance] = "Not a default test platform"
Andrew Boie6acbe632015-07-17 12:03:52 -07002081 else:
Andrew Boie821d8322016-03-22 10:08:35 -07002082 self.add_instances(instance_list)
Anas Nashifab351f42018-04-08 08:57:48 -05002083
2084 for name, case in self.instances.items():
2085 case.create_overlay()
2086
Andrew Boie6acbe632015-07-17 12:03:52 -07002087 self.discards = discards
2088 return discards
2089
Andrew Boie821d8322016-03-22 10:08:35 -07002090 def add_instances(self, ti_list):
2091 for ti in ti_list:
2092 self.instances[ti.name] = ti
Andrew Boie6acbe632015-07-17 12:03:52 -07002093
Anas Nashif37f9dc52018-02-23 08:53:46 -06002094 def execute(self, cb, cb_context):
Daniel Leung6b170072016-04-07 12:10:25 -07002095
2096 def calc_one_elf_size(name, goal):
2097 if not goal.failed:
Alberto Escolar Piedrasb1045fe2018-07-14 13:11:02 +02002098 if self.instances[name].platform.type != "native":
2099 i = self.instances[name]
2100 sc = i.calculate_sizes()
2101 goal.metrics["ram_size"] = sc.get_ram_size()
2102 goal.metrics["rom_size"] = sc.get_rom_size()
2103 goal.metrics["unrecognized"] = sc.unrecognized_sections()
2104 else:
2105 goal.metrics["ram_size"] = 0
2106 goal.metrics["rom_size"] = 0
2107 goal.metrics["unrecognized"] = []
Daniel Leung6b170072016-04-07 12:10:25 -07002108
Anas Nashif37f9dc52018-02-23 08:53:46 -06002109 mg = MakeGenerator(self.outdir)
Andrew Boie6acbe632015-07-17 12:03:52 -07002110 for i in self.instances.values():
Anas Nashif37f9dc52018-02-23 08:53:46 -06002111 mg.add_test_instance(i, options.extra_args)
Andrew Boie6acbe632015-07-17 12:03:52 -07002112 self.goals = mg.execute(cb, cb_context)
Daniel Leung6b170072016-04-07 12:10:25 -07002113
Andy Ross9c9162d2019-01-03 10:50:53 -08002114 if not options.disable_size_report:
2115 # Parallelize size calculation
2116 executor = concurrent.futures.ThreadPoolExecutor(JOBS)
2117 futures = [executor.submit(calc_one_elf_size, name, goal)
2118 for name, goal in self.goals.items()]
2119 concurrent.futures.wait(futures)
2120 else:
2121 for goal in self.goals.values():
2122 goal.metrics["ram_size"] = 0
2123 goal.metrics["rom_size"] = 0
2124 goal.metrics["unrecognized"] = []
Daniel Leung6b170072016-04-07 12:10:25 -07002125
Andrew Boie6acbe632015-07-17 12:03:52 -07002126 return self.goals
2127
Anas Nashifbd166f42017-09-02 12:32:08 -04002128 def run_report(self, filename):
2129 with open(filename, "at") as csvfile:
2130 fieldnames = ['path', 'test', 'platform', 'arch']
2131 cw = csv.DictWriter(csvfile, fieldnames, lineterminator=os.linesep)
2132 for instance in self.instances.values():
2133 rowdict = {
Anas Nashif3ba1d432017-12-05 15:28:44 -05002134 "path": os.path.dirname(instance.test.name),
2135 "test": os.path.basename(instance.test.name),
2136 "platform": instance.platform.name,
2137 "arch": instance.platform.arch
2138 }
Anas Nashifbd166f42017-09-02 12:32:08 -04002139 cw.writerow(rowdict)
2140
Andrew Boie6acbe632015-07-17 12:03:52 -07002141 def discard_report(self, filename):
Anas Nashifb4bdd662018-08-15 17:12:28 -05002142
2143 try:
2144 if self.discards is None:
2145 raise SanityRuntimeError("apply_filters() hasn't been run!")
2146 except Exception as e:
2147 error(str(e))
2148 sys.exit(2)
Andrew Boie6acbe632015-07-17 12:03:52 -07002149
Anas Nashifbd166f42017-09-02 12:32:08 -04002150 with open(filename, "wt") as csvfile:
Andrew Boie6acbe632015-07-17 12:03:52 -07002151 fieldnames = ["test", "arch", "platform", "reason"]
2152 cw = csv.DictWriter(csvfile, fieldnames, lineterminator=os.linesep)
2153 cw.writeheader()
Andrew Boie08ce5a52016-02-22 13:28:10 -08002154 for instance, reason in self.discards.items():
Anas Nashif3ba1d432017-12-05 15:28:44 -05002155 rowdict = {"test": instance.test.name,
2156 "arch": instance.platform.arch,
2157 "platform": instance.platform.name,
2158 "reason": reason}
Andrew Boie6acbe632015-07-17 12:03:52 -07002159 cw.writerow(rowdict)
2160
2161 def compare_metrics(self, filename):
2162 # name, datatype, lower results better
2163 interesting_metrics = [("ram_size", int, True),
2164 ("rom_size", int, True)]
2165
Anas Nashifb4bdd662018-08-15 17:12:28 -05002166 try:
2167 if self.goals is None:
2168 raise SanityRuntimeError("execute() hasn't been run!")
2169 except Exception as e:
2170 print(str(e))
2171 sys.exit(2)
Andrew Boie6acbe632015-07-17 12:03:52 -07002172
2173 if not os.path.exists(filename):
2174 info("Cannot compare metrics, %s not found" % filename)
2175 return []
2176
2177 results = []
2178 saved_metrics = {}
2179 with open(filename) as fp:
2180 cr = csv.DictReader(fp)
2181 for row in cr:
2182 d = {}
2183 for m, _, _ in interesting_metrics:
2184 d[m] = row[m]
2185 saved_metrics[(row["test"], row["platform"])] = d
2186
Andrew Boie08ce5a52016-02-22 13:28:10 -08002187 for name, goal in self.goals.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07002188 i = self.instances[name]
2189 mkey = (i.test.name, i.platform.name)
2190 if mkey not in saved_metrics:
2191 continue
2192 sm = saved_metrics[mkey]
2193 for metric, mtype, lower_better in interesting_metrics:
2194 if metric not in goal.metrics:
2195 continue
2196 if sm[metric] == "":
2197 continue
2198 delta = goal.metrics[metric] - mtype(sm[metric])
Andrew Boieea7928f2015-08-14 14:27:38 -07002199 if delta == 0:
2200 continue
2201 results.append((i, metric, goal.metrics[metric], delta,
2202 lower_better))
Andrew Boie6acbe632015-07-17 12:03:52 -07002203 return results
2204
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002205
2206
2207 def encode_for_xml(self, unicode_data, encoding='ascii'):
2208 unicode_data = unicode_data.replace('\x00', '')
2209 return unicode_data
2210
2211 def testcase_target_report(self, report_file):
2212
2213 run = "Sanitycheck"
2214 eleTestsuite = None
2215 append = options.only_failed
2216
2217 errors = 0
2218 passes = 0
2219 fails = 0
2220 duration = 0
2221 skips = 0
2222
2223 for identifier, ti in self.instances.items():
2224 for k in ti.results.keys():
2225 if ti.results[k] == 'PASS':
2226 passes += 1
2227 elif ti.results[k] == 'BLOCK':
2228 errors += 1
Anas Nashif61e21632018-04-08 13:30:16 -05002229 elif ti.results[k] == 'SKIP':
2230 skips += 1
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002231 else:
2232 fails += 1
2233
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002234 eleTestsuites = ET.Element('testsuites')
2235 eleTestsuite = ET.SubElement(eleTestsuites, 'testsuite',
2236 name=run, time="%d" % duration,
2237 tests="%d" % (errors + passes + fails),
2238 failures="%d" % fails,
Anas Nashif61e21632018-04-08 13:30:16 -05002239 errors="%d" % errors, skipped="%d" %skips)
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002240
2241 handler_time = "0"
Anas Nashif61e21632018-04-08 13:30:16 -05002242
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002243 # print out test results
2244 for identifier, ti in self.instances.items():
2245 for k in ti.results.keys():
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002246
2247 eleTestcase = ET.SubElement(
2248 eleTestsuite, 'testcase', classname="%s:%s" %(ti.platform.name, os.path.basename(ti.test.name)),
Anas Nashif61e21632018-04-08 13:30:16 -05002249 name="%s" % (k), time=handler_time)
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002250 if ti.results[k] in ['FAIL', 'BLOCK']:
2251 el = None
2252
2253 if ti.results[k] == 'FAIL':
2254 el = ET.SubElement(
2255 eleTestcase,
2256 'failure',
2257 type="failure",
2258 message="failed")
2259 elif ti.results[k] == 'BLOCK':
2260 el = ET.SubElement(
2261 eleTestcase,
2262 'error',
2263 type="failure",
2264 message="failed")
2265 p = os.path.join(options.outdir, ti.platform.name, ti.test.name)
2266 bl = os.path.join(p, "handler.log")
2267
2268 if os.path.exists(bl):
2269 with open(bl, "rb") as f:
2270 log = f.read().decode("utf-8")
2271 el.text = self.encode_for_xml(log)
2272
Anas Nashif61e21632018-04-08 13:30:16 -05002273 elif ti.results[k] == 'SKIP':
2274 el = ET.SubElement(
2275 eleTestcase,
Anas Nashif2c7636b2018-09-02 13:11:19 -04002276 'skipped',
2277 type="skipped",
2278 message="Skipped")
Anas Nashif61e21632018-04-08 13:30:16 -05002279
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002280 result = ET.tostring(eleTestsuites)
2281 f = open(report_file, 'wb')
2282 f.write(result)
2283 f.close()
2284
2285
Anas Nashif4f028882017-12-30 11:48:43 -05002286 def testcase_xunit_report(self, filename, duration):
Anas Nashifb4bdd662018-08-15 17:12:28 -05002287 try:
2288 if self.goals is None:
2289 raise SanityRuntimeError("execute() hasn't been run!")
2290 except Exception as e:
2291 print(str(e))
2292 sys.exit(2)
Anas Nashifb3311ed2017-04-13 14:44:48 -04002293
2294 fails = 0
2295 passes = 0
2296 errors = 0
2297
2298 for name, goal in self.goals.items():
2299 if goal.failed:
Anas Nashif9a839df2018-01-29 08:42:38 -05002300 if goal.reason in ['build_error', 'handler_crash']:
Anas Nashifb3311ed2017-04-13 14:44:48 -04002301 errors += 1
2302 else:
2303 fails += 1
2304 else:
2305 passes += 1
2306
2307 run = "Sanitycheck"
2308 eleTestsuite = None
Anas Nashif4f028882017-12-30 11:48:43 -05002309 append = options.only_failed
Anas Nashifb3311ed2017-04-13 14:44:48 -04002310
Anas Nashif0605fa32017-05-07 08:51:02 -04002311 if os.path.exists(filename) and append:
Anas Nashifb3311ed2017-04-13 14:44:48 -04002312 tree = ET.parse(filename)
2313 eleTestsuites = tree.getroot()
Anas Nashif3ba1d432017-12-05 15:28:44 -05002314 eleTestsuite = tree.findall('testsuite')[0]
Anas Nashifb3311ed2017-04-13 14:44:48 -04002315 else:
2316 eleTestsuites = ET.Element('testsuites')
Anas Nashif3ba1d432017-12-05 15:28:44 -05002317 eleTestsuite = ET.SubElement(eleTestsuites, 'testsuite',
2318 name=run, time="%d" % duration,
2319 tests="%d" % (errors + passes + fails),
2320 failures="%d" % fails,
2321 errors="%d" % errors, skip="0")
Anas Nashifb3311ed2017-04-13 14:44:48 -04002322
Anas Nashifc8390f12017-11-25 17:14:12 -05002323 handler_time = "0"
Anas Nashifb3311ed2017-04-13 14:44:48 -04002324 for name, goal in self.goals.items():
2325
2326 i = self.instances[name]
2327 if append:
2328 for tc in eleTestsuite.findall('testcase'):
Anas Nashif3ba1d432017-12-05 15:28:44 -05002329 if tc.get('classname') == "%s:%s" % (
2330 i.platform.name, i.test.name):
Anas Nashifb3311ed2017-04-13 14:44:48 -04002331 eleTestsuite.remove(tc)
2332
Anas Nashif4d25b502017-11-25 17:37:17 -05002333 if not goal.failed and goal.handler:
2334 handler_time = "%s" %(goal.metrics["handler_time"])
Anas Nashifb3311ed2017-04-13 14:44:48 -04002335
Anas Nashif3ba1d432017-12-05 15:28:44 -05002336 eleTestcase = ET.SubElement(
2337 eleTestsuite, 'testcase', classname="%s:%s" %
2338 (i.platform.name, i.test.name), name="%s" %
2339 (name), time=handler_time)
Anas Nashifb3311ed2017-04-13 14:44:48 -04002340 if goal.failed:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002341 failure = ET.SubElement(
2342 eleTestcase,
2343 'failure',
2344 type="failure",
2345 message=goal.reason)
Anas Nashif4f028882017-12-30 11:48:43 -05002346 p = ("%s/%s/%s" % (options.outdir, i.platform.name, i.test.name))
Anas Nashifb3311ed2017-04-13 14:44:48 -04002347 bl = os.path.join(p, "build.log")
Anas Nashifaccc8eb2017-05-01 16:33:43 -04002348 if goal.reason != 'build_error':
Anas Nashifa49048b2018-01-29 08:41:19 -05002349 bl = os.path.join(p, "handler.log")
Anas Nashifaccc8eb2017-05-01 16:33:43 -04002350
Anas Nashifb3311ed2017-04-13 14:44:48 -04002351 if os.path.exists(bl):
Anas Nashif712d3452017-12-29 22:09:03 -05002352 with open(bl, "rb") as f:
2353 log = f.read().decode("utf-8")
Anas Nashifa4c368e2018-10-15 09:45:59 -04002354 filtered_string = ''.join(filter(lambda x: x in string.printable, log))
2355 failure.text = filtered_string
Anas Nashifba4643b2018-09-23 09:41:59 -05002356 f.close()
Anas Nashifb3311ed2017-04-13 14:44:48 -04002357
2358 result = ET.tostring(eleTestsuites)
2359 f = open(filename, 'wb')
2360 f.write(result)
2361 f.close()
2362
Andrew Boie6acbe632015-07-17 12:03:52 -07002363 def testcase_report(self, filename):
Anas Nashifb4bdd662018-08-15 17:12:28 -05002364 try:
2365 if self.goals is None:
2366 raise SanityRuntimeError("execute() hasn't been run!")
2367 except Exception as e:
2368 print(str(e))
2369 sys.exit(2)
Andrew Boie6acbe632015-07-17 12:03:52 -07002370
Andrew Boie08ce5a52016-02-22 13:28:10 -08002371 with open(filename, "wt") as csvfile:
Andrew Boie6acbe632015-07-17 12:03:52 -07002372 fieldnames = ["test", "arch", "platform", "passed", "status",
Anas Nashifc8390f12017-11-25 17:14:12 -05002373 "extra_args", "qemu", "handler_time", "ram_size",
Andrew Boie6acbe632015-07-17 12:03:52 -07002374 "rom_size"]
2375 cw = csv.DictWriter(csvfile, fieldnames, lineterminator=os.linesep)
2376 cw.writeheader()
Andrew Boie08ce5a52016-02-22 13:28:10 -08002377 for name, goal in self.goals.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07002378 i = self.instances[name]
Anas Nashif3ba1d432017-12-05 15:28:44 -05002379 rowdict = {"test": i.test.name,
2380 "arch": i.platform.arch,
2381 "platform": i.platform.name,
2382 "extra_args": " ".join(i.test.extra_args),
2383 "qemu": i.platform.qemu_support}
Andrew Boie6acbe632015-07-17 12:03:52 -07002384 if goal.failed:
2385 rowdict["passed"] = False
2386 rowdict["status"] = goal.reason
2387 else:
2388 rowdict["passed"] = True
Anas Nashif4d25b502017-11-25 17:37:17 -05002389 if goal.handler:
Anas Nashifc8390f12017-11-25 17:14:12 -05002390 rowdict["handler_time"] = goal.metrics["handler_time"]
Andrew Boie6acbe632015-07-17 12:03:52 -07002391 rowdict["ram_size"] = goal.metrics["ram_size"]
2392 rowdict["rom_size"] = goal.metrics["rom_size"]
2393 cw.writerow(rowdict)
2394
2395
2396def parse_arguments():
2397
Anas Nashif3ba1d432017-12-05 15:28:44 -05002398 parser = argparse.ArgumentParser(
2399 description=__doc__,
2400 formatter_class=argparse.RawDescriptionHelpFormatter)
Genaro Saucedo Tejada28bba922016-10-24 18:00:58 -05002401 parser.fromfile_prefix_chars = "+"
Andrew Boie6acbe632015-07-17 12:03:52 -07002402
Anas Nashif07d54c02018-07-21 19:29:08 -05002403 parser.add_argument("--force-toolchain", action="store_true",
2404 help="Do not filter based on toolchain, use the set "
2405 " toolchain unconditionally")
Anas Nashif3ba1d432017-12-05 15:28:44 -05002406 parser.add_argument(
2407 "-p", "--platform", action="append",
2408 help="Platform filter for testing. This option may be used multiple "
2409 "times. Testcases will only be built/run on the platforms "
2410 "specified. If this option is not used, then platforms marked "
2411 "as default in the platform metadata file will be chosen "
2412 "to build and test. ")
2413 parser.add_argument(
Anas Nashif3ba1d432017-12-05 15:28:44 -05002414 "-a", "--arch", action="append",
2415 help="Arch filter for testing. Takes precedence over --platform. "
2416 "If unspecified, test all arches. Multiple invocations "
2417 "are treated as a logical 'or' relationship")
2418 parser.add_argument(
2419 "-t", "--tag", action="append",
2420 help="Specify tags to restrict which tests to run by tag value. "
2421 "Default is to not do any tag filtering. Multiple invocations "
2422 "are treated as a logical 'or' relationship")
Anas Nashifdfa86e22016-10-24 17:08:56 -04002423 parser.add_argument("-e", "--exclude-tag", action="append",
Anas Nashif3ba1d432017-12-05 15:28:44 -05002424 help="Specify tags of tests that should not run. "
2425 "Default is to run all tests with all tags.")
2426 parser.add_argument(
2427 "-f",
2428 "--only-failed",
2429 action="store_true",
2430 help="Run only those tests that failed the previous sanity check "
2431 "invocation.")
2432 parser.add_argument(
2433 "-c", "--config", action="append",
2434 help="Specify platform configuration values filtering. This can be "
2435 "specified two ways: <config>=<value> or just <config>. The "
2436 "defconfig for all platforms will be "
2437 "checked. For the <config>=<value> case, only match defconfig "
2438 "that have that value defined. For the <config> case, match "
2439 "defconfig that have that value assigned to any value. "
2440 "Prepend a '!' to invert the match.")
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002441
Anas Nashif3ba1d432017-12-05 15:28:44 -05002442 parser.add_argument(
2443 "-s", "--test", action="append",
2444 help="Run only the specified test cases. These are named by "
2445 "<path to test project relative to "
2446 "--testcase-root>/<testcase.yaml section name>")
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002447
2448 parser.add_argument(
2449 "--sub-test", action="append",
2450 help="Run only the specified sub-test cases and its parent. These are named by "
2451 "test case name appended by test function, i.e. kernel.mutex.mutex_lock_unlock."
2452 )
2453
Anas Nashif3ba1d432017-12-05 15:28:44 -05002454 parser.add_argument(
2455 "-l", "--all", action="store_true",
2456 help="Build/test on all platforms. Any --platform arguments "
2457 "ignored.")
Andrew Boie6acbe632015-07-17 12:03:52 -07002458
Anas Nashif3ba1d432017-12-05 15:28:44 -05002459 parser.add_argument(
2460 "-o", "--testcase-report",
2461 help="Output a CSV spreadsheet containing results of the test run")
2462 parser.add_argument(
2463 "-d", "--discard-report",
2464 help="Output a CSV spreadsheet showing tests that were skipped "
2465 "and why")
Daniel Leung7f850102016-04-08 11:07:32 -07002466 parser.add_argument("--compare-report",
Anas Nashif3ba1d432017-12-05 15:28:44 -05002467 help="Use this report file for size comparison")
Daniel Leung7f850102016-04-08 11:07:32 -07002468
Anas Nashif3ba1d432017-12-05 15:28:44 -05002469 parser.add_argument(
2470 "-B", "--subset",
2471 help="Only run a subset of the tests, 1/4 for running the first 25%%, "
2472 "3/5 means run the 3rd fifth of the total. "
2473 "This option is useful when running a large number of tests on "
2474 "different hosts to speed up execution time.")
Anas Nashifa8a13882017-12-30 13:01:06 -05002475
2476 parser.add_argument(
2477 "-N", "--ninja", action="store_true",
Anas Nashif25f6ab62018-03-06 07:15:11 -06002478 help="Use the Ninja generator with CMake")
Anas Nashifa8a13882017-12-30 13:01:06 -05002479
Anas Nashif3ba1d432017-12-05 15:28:44 -05002480 parser.add_argument(
2481 "-y", "--dry-run", action="store_true",
2482 help="Create the filtered list of test cases, but don't actually "
2483 "run them. Useful if you're just interested in "
2484 "--discard-report")
Andrew Boie6acbe632015-07-17 12:03:52 -07002485
Anas Nashif75547e22018-02-24 08:32:14 -06002486 parser.add_argument("--list-tags", action="store_true",
2487 help="list all tags in selected tests")
2488
Anas Nashifc0149cc2018-04-14 23:12:58 -05002489 parser.add_argument("--list-tests", action="store_true",
2490 help="list all tests.")
2491
Anas Nashif5d6e7eb2018-06-01 09:51:08 -05002492 parser.add_argument("--export-tests", action="store",
2493 metavar="FILENAME",
2494 help="Export tests case meta-data to a file in CSV format.")
2495
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002496 parser.add_argument("--detailed-report",
2497 action="store",
2498 metavar="FILENAME",
2499 help="Generate a junit report with detailed testcase results.")
2500
Anas Nashif3ba1d432017-12-05 15:28:44 -05002501 parser.add_argument(
2502 "-r", "--release", action="store_true",
2503 help="Update the benchmark database with the results of this test "
2504 "run. Intended to be run by CI when tagging an official "
2505 "release. This database is used as a basis for comparison "
2506 "when looking for deltas in metrics such as footprint")
Andrew Boie6acbe632015-07-17 12:03:52 -07002507 parser.add_argument("-w", "--warnings-as-errors", action="store_true",
Anas Nashif3ba1d432017-12-05 15:28:44 -05002508 help="Treat warning conditions as errors")
2509 parser.add_argument(
2510 "-v",
2511 "--verbose",
2512 action="count",
2513 default=0,
2514 help="Emit debugging information, call multiple times to increase "
2515 "verbosity")
2516 parser.add_argument(
2517 "-i", "--inline-logs", action="store_true",
2518 help="Upon test failure, print relevant log data to stdout "
2519 "instead of just a path to it")
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -08002520 parser.add_argument("--log-file", metavar="FILENAME", action="store",
Anas Nashif3ba1d432017-12-05 15:28:44 -05002521 help="log also to file")
2522 parser.add_argument(
2523 "-m", "--last-metrics", action="store_true",
2524 help="Instead of comparing metrics from the last --release, "
2525 "compare with the results of the previous sanity check "
2526 "invocation")
2527 parser.add_argument(
2528 "-u",
2529 "--no-update",
2530 action="store_true",
2531 help="do not update the results of the last run of the sanity "
2532 "checks")
2533 parser.add_argument(
2534 "-F",
2535 "--load-tests",
2536 metavar="FILENAME",
2537 action="store",
2538 help="Load list of tests to be run from file.")
Anas Nashifbd166f42017-09-02 12:32:08 -04002539
Anas Nashif3ba1d432017-12-05 15:28:44 -05002540 parser.add_argument(
2541 "-E",
2542 "--save-tests",
2543 metavar="FILENAME",
2544 action="store",
2545 help="Save list of tests to be run to file.")
Anas Nashifbd166f42017-09-02 12:32:08 -04002546
Anas Nashif3ba1d432017-12-05 15:28:44 -05002547 parser.add_argument(
2548 "-b", "--build-only", action="store_true",
2549 help="Only build the code, do not execute any of it in QEMU")
2550 parser.add_argument(
2551 "-j", "--jobs", type=int,
Oleg Zhurakivskyyf3bc9672018-08-17 18:31:38 +03002552 help="Number of jobs for building, defaults to number of CPU threads "
2553 "overcommited by factor 2")
Anas Nashif73440ea2018-02-19 10:57:03 -06002554
2555 parser.add_argument(
2556 "--device-testing", action="store_true",
Anas Nashif333a3152018-05-24 14:35:33 -05002557 help="Test on device directly. Specify the serial device to "
2558 "use with the --device-serial option.")
Anas Nashif7dd19ea2018-11-14 08:46:49 -05002559
2560 parser.add_argument(
2561 "-X", "--fixture", action="append", default=[],
2562 help="Specify a fixture that a board might support")
Anas Nashif73440ea2018-02-19 10:57:03 -06002563 parser.add_argument(
2564 "--device-serial",
Anas Nashif333a3152018-05-24 14:35:33 -05002565 help="Serial device for accessing the board (e.g., /dev/ttyACM0)")
Anas Nashif3ba1d432017-12-05 15:28:44 -05002566 parser.add_argument(
Anas Nashif424a3db2018-02-20 08:37:24 -06002567 "--show-footprint", action="store_true",
2568 help="Show footprint statistics and deltas since last release."
2569 )
2570 parser.add_argument(
Anas Nashif3ba1d432017-12-05 15:28:44 -05002571 "-H", "--footprint-threshold", type=float, default=5,
2572 help="When checking test case footprint sizes, warn the user if "
2573 "the new app size is greater then the specified percentage "
2574 "from the last release. Default is 5. 0 to warn on any "
2575 "increase on app size")
2576 parser.add_argument(
2577 "-D", "--all-deltas", action="store_true",
2578 help="Show all footprint deltas, positive or negative. Implies "
2579 "--footprint-threshold=0")
Håkon Øye Amundsende223cc2018-04-25 06:24:25 +00002580 parser.add_argument(
2581 "-O", "--outdir",
Anas Nashiff114a132018-11-20 11:51:34 -05002582 default="%s/sanity-out" % os.getcwd(),
Håkon Øye Amundsende223cc2018-04-25 06:24:25 +00002583 help="Output directory for logs and binaries. "
Anas Nashiff114a132018-11-20 11:51:34 -05002584 "Default is 'sanity-out' in the current directory. "
Håkon Øye Amundsende223cc2018-04-25 06:24:25 +00002585 "This directory will be deleted unless '--no-clean' is set.")
Anas Nashif3ba1d432017-12-05 15:28:44 -05002586 parser.add_argument(
2587 "-n", "--no-clean", action="store_true",
2588 help="Do not delete the outdir before building. Will result in "
2589 "faster compilation since builds will be incremental")
2590 parser.add_argument(
2591 "-T", "--testcase-root", action="append", default=[],
2592 help="Base directory to recursively search for test cases. All "
2593 "testcase.yaml files under here will be processed. May be "
2594 "called multiple times. Defaults to the 'samples' and "
2595 "'tests' directories in the Zephyr tree.")
Anas Nashif7dd19ea2018-11-14 08:46:49 -05002596
Anas Nashif3ba1d432017-12-05 15:28:44 -05002597 board_root_list = ["%s/boards" % ZEPHYR_BASE,
2598 "%s/scripts/sanity_chk/boards" % ZEPHYR_BASE]
Anas Nashif7dd19ea2018-11-14 08:46:49 -05002599
Anas Nashif3ba1d432017-12-05 15:28:44 -05002600 parser.add_argument(
2601 "-A", "--board-root", action="append", default=board_root_list,
2602 help="Directory to search for board configuration files. All .yaml "
2603 "files in the directory will be processed.")
2604 parser.add_argument(
2605 "-z", "--size", action="append",
2606 help="Don't run sanity checks. Instead, produce a report to "
2607 "stdout detailing RAM/ROM sizes on the specified filenames. "
2608 "All other command line arguments ignored.")
2609 parser.add_argument(
2610 "-S", "--enable-slow", action="store_true",
2611 help="Execute time-consuming test cases that have been marked "
2612 "as 'slow' in testcase.yaml. Normally these are only built.")
Sebastian Bøe03ed0952018-11-13 13:36:19 +01002613 parser.add_argument(
2614 "--disable-unrecognized-section-test", action="store_true",
2615 default=False,
2616 help="Skip the 'unrecognized section' test.")
Andrew Boie55121052016-07-20 11:52:04 -07002617 parser.add_argument("-R", "--enable-asserts", action="store_true",
Kumar Gala0d48cbe2018-01-26 10:22:20 -06002618 default=True,
Andrew Boie29599f62018-05-24 13:33:09 -07002619 help="deprecated, left for compatibility")
Kumar Gala0d48cbe2018-01-26 10:22:20 -06002620 parser.add_argument("--disable-asserts", action="store_false",
2621 dest="enable_asserts",
Andrew Boie29599f62018-05-24 13:33:09 -07002622 help="deprecated, left for compatibility")
Anas Nashife3febe92016-11-30 14:25:44 -05002623 parser.add_argument("-Q", "--error-on-deprecations", action="store_false",
Anas Nashif3ba1d432017-12-05 15:28:44 -05002624 help="Error on deprecation warnings.")
Andy Ross9c9162d2019-01-03 10:50:53 -08002625 parser.add_argument("--disable-size-report", action="store_true",
2626 help="Skip expensive computation of ram/rom segment sizes.")
Sebastian Bøec2182612017-11-09 12:25:02 +01002627
2628 parser.add_argument(
2629 "-x", "--extra-args", action="append", default=[],
Alberto Escolar Piedras25e06362018-02-10 17:40:33 +01002630 help="""Extra CMake cache entries to define when building test cases.
2631 May be called multiple times. The key-value entries will be
Sebastian Bøec2182612017-11-09 12:25:02 +01002632 prefixed with -D before being passed to CMake.
2633
2634 E.g
2635 "sanitycheck -x=USE_CCACHE=0"
2636 will translate to
2637 "cmake -DUSE_CCACHE=0"
2638
2639 which will ultimately disable ccache.
2640 """
2641 )
2642
Alberto Escolar Piedrasc026c2e2018-06-21 09:30:20 +02002643 parser.add_argument("--enable-coverage", action="store_true",
Anas Nashif8d72bb92018-11-07 23:05:42 -05002644 help="Enable code coverage using gcov.")
Alberto Escolar Piedrasc026c2e2018-06-21 09:30:20 +02002645
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03002646 parser.add_argument("-C", "--coverage", action="store_true",
Anas Nashif8d72bb92018-11-07 23:05:42 -05002647 help="Generate coverage reports. Implies --enable_coverage")
Andrew Boie6acbe632015-07-17 12:03:52 -07002648
2649 return parser.parse_args()
2650
Anas Nashif3ba1d432017-12-05 15:28:44 -05002651
Andrew Boie6acbe632015-07-17 12:03:52 -07002652def log_info(filename):
Mazen NEIFER8f1dd3a2017-02-04 14:32:04 +01002653 filename = os.path.relpath(os.path.realpath(filename))
Andrew Boie6acbe632015-07-17 12:03:52 -07002654 if INLINE_LOGS:
Andrew Boie08ce5a52016-02-22 13:28:10 -08002655 info("{:-^100}".format(filename))
Andrew Boied01535c2017-01-10 13:15:02 -08002656
2657 try:
2658 with open(filename) as fp:
2659 data = fp.read()
2660 except Exception as e:
2661 data = "Unable to read log data (%s)\n" % (str(e))
2662
2663 sys.stdout.write(data)
2664 if log_file:
2665 log_file.write(data)
Andrew Boie08ce5a52016-02-22 13:28:10 -08002666 info("{:-^100}".format(filename))
Andrew Boie6acbe632015-07-17 12:03:52 -07002667 else:
Andrew Boie08ce5a52016-02-22 13:28:10 -08002668 info("\tsee: " + COLOR_YELLOW + filename + COLOR_NORMAL)
Andrew Boie6acbe632015-07-17 12:03:52 -07002669
Anas Nashif3ba1d432017-12-05 15:28:44 -05002670
Andrew Boie6acbe632015-07-17 12:03:52 -07002671def terse_test_cb(instances, goals, goal):
2672 total_tests = len(goals)
2673 total_done = 0
2674 total_failed = 0
2675
Andrew Boie08ce5a52016-02-22 13:28:10 -08002676 for k, g in goals.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07002677 if g.finished:
2678 total_done += 1
2679 if g.failed:
2680 total_failed += 1
2681
2682 if goal.failed:
2683 i = instances[goal.name]
Anas Nashif3ba1d432017-12-05 15:28:44 -05002684 info(
2685 "\n\n{:<25} {:<50} {}FAILED{}: {}".format(
2686 i.platform.name,
2687 i.test.name,
2688 COLOR_RED,
2689 COLOR_NORMAL,
2690 goal.reason))
Andrew Boie6acbe632015-07-17 12:03:52 -07002691 log_info(goal.get_error_log())
2692 info("")
2693
Anas Nashif3ba1d432017-12-05 15:28:44 -05002694 sys.stdout.write(
2695 "\rtotal complete: %s%4d/%4d%s %2d%% failed: %s%4d%s" %
2696 (COLOR_GREEN, total_done, total_tests, COLOR_NORMAL,
2697 int((float(total_done) / total_tests) * 100),
2698 COLOR_RED if total_failed > 0 else COLOR_NORMAL, total_failed,
2699 COLOR_NORMAL))
Andrew Boie6acbe632015-07-17 12:03:52 -07002700 sys.stdout.flush()
2701
Anas Nashif3ba1d432017-12-05 15:28:44 -05002702
Andrew Boie6acbe632015-07-17 12:03:52 -07002703def chatty_test_cb(instances, goals, goal):
2704 i = instances[goal.name]
2705
2706 if VERBOSE < 2 and not goal.finished:
2707 return
2708
Ruslan Mstoi33fa63e2018-05-29 14:35:34 +03002709 total_tests = len(goals)
2710 total_tests_width = len(str(total_tests))
2711 total_done = 0
2712
2713 for k, g in goals.items():
2714 if g.finished:
2715 total_done += 1
2716
Andrew Boie6acbe632015-07-17 12:03:52 -07002717 if goal.failed:
2718 status = COLOR_RED + "FAILED" + COLOR_NORMAL + ": " + goal.reason
2719 elif goal.finished:
2720 status = COLOR_GREEN + "PASSED" + COLOR_NORMAL
2721 else:
2722 status = goal.make_state
2723
Ruslan Mstoi33fa63e2018-05-29 14:35:34 +03002724 info("{:>{}}/{} {:<25} {:<50} {}".format(
2725 total_done, total_tests_width, total_tests, i.platform.name,
2726 i.test.name, status))
Andrew Boie6acbe632015-07-17 12:03:52 -07002727 if goal.failed:
2728 log_info(goal.get_error_log())
2729
Andrew Boiebbd670c2015-08-17 13:16:11 -07002730
2731def size_report(sc):
2732 info(sc.filename)
Andrew Boie73b4ee62015-10-07 11:33:22 -07002733 info("SECTION NAME VMA LMA SIZE HEX SZ TYPE")
Andrew Boie9882dcd2015-10-07 14:25:51 -07002734 for i in range(len(sc.sections)):
2735 v = sc.sections[i]
2736
Andrew Boie73b4ee62015-10-07 11:33:22 -07002737 info("%-17s 0x%08x 0x%08x %8d 0x%05x %-7s" %
2738 (v["name"], v["virt_addr"], v["load_addr"], v["size"], v["size"],
2739 v["type"]))
Andrew Boie9882dcd2015-10-07 14:25:51 -07002740
Andrew Boie73b4ee62015-10-07 11:33:22 -07002741 info("Totals: %d bytes (ROM), %d bytes (RAM)" %
Anas Nashif3ba1d432017-12-05 15:28:44 -05002742 (sc.rom_size, sc.ram_size))
Andrew Boiebbd670c2015-08-17 13:16:11 -07002743 info("")
2744
Anas Nashif3ba1d432017-12-05 15:28:44 -05002745
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03002746def generate_coverage(outdir, ignores):
2747 with open(os.path.join(outdir, "coverage.log"), "a") as coveragelog:
2748 coveragefile = os.path.join(outdir, "coverage.info")
2749 ztestfile = os.path.join(outdir, "ztest.info")
2750 subprocess.call(["lcov", "--capture", "--directory", outdir,
Alberto Escolar Piedrasbc101c52018-02-11 09:33:55 +01002751 "--rc", "lcov_branch_coverage=1",
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03002752 "--output-file", coveragefile], stdout=coveragelog)
2753 # We want to remove tests/* and tests/ztest/test/* but save tests/ztest
2754 subprocess.call(["lcov", "--extract", coveragefile,
Anas Nashif3ba1d432017-12-05 15:28:44 -05002755 os.path.join(ZEPHYR_BASE, "tests", "ztest", "*"),
Alberto Escolar Piedrasbc101c52018-02-11 09:33:55 +01002756 "--output-file", ztestfile,
2757 "--rc", "lcov_branch_coverage=1"], stdout=coveragelog)
2758
Anas Nashif3cbffef2018-11-07 23:50:54 -05002759 if os.path.exists(ztestfile) and os.path.getsize(ztestfile) > 0:
Alberto Escolar Piedrasbc101c52018-02-11 09:33:55 +01002760 subprocess.call(["lcov", "--remove", ztestfile,
2761 os.path.join(ZEPHYR_BASE, "tests/ztest/test/*"),
2762 "--output-file", ztestfile,
2763 "--rc", "lcov_branch_coverage=1"],
2764 stdout=coveragelog)
2765 files = [coveragefile, ztestfile];
2766 else:
2767 files = [coveragefile];
2768
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03002769 for i in ignores:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002770 subprocess.call(
2771 ["lcov", "--remove", coveragefile, i, "--output-file",
Alberto Escolar Piedrasbc101c52018-02-11 09:33:55 +01002772 coveragefile, "--rc", "lcov_branch_coverage=1"],
Anas Nashif3ba1d432017-12-05 15:28:44 -05002773 stdout=coveragelog)
Alberto Escolar Piedrasbc101c52018-02-11 09:33:55 +01002774
2775 ret = subprocess.call(["genhtml", "--legend", "--branch-coverage",
2776 "-output-directory",
2777 os.path.join(outdir, "coverage")] + files,
2778 stdout=coveragelog)
2779 if ret==0:
2780 info("HTML report generated: %s"%
2781 os.path.join(outdir, "coverage","index.html"));
Anas Nashif3ba1d432017-12-05 15:28:44 -05002782
Andrew Boiebbd670c2015-08-17 13:16:11 -07002783
Andrew Boie6acbe632015-07-17 12:03:52 -07002784def main():
Andrew Boie4b182472015-07-31 12:25:22 -07002785 start_time = time.time()
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03002786 global VERBOSE, INLINE_LOGS, JOBS, log_file
Anas Nashife10b6512017-12-30 13:01:45 -05002787 global options
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002788 global run_individual_tests
Anas Nashife10b6512017-12-30 13:01:45 -05002789 options = parse_arguments()
Andrew Boiebbd670c2015-08-17 13:16:11 -07002790
Alberto Escolar Piedrasc026c2e2018-06-21 09:30:20 +02002791 if options.coverage:
2792 options.enable_coverage = True
2793
Anas Nashife10b6512017-12-30 13:01:45 -05002794 if options.size:
2795 for fn in options.size:
Andrew Boie52fef672016-11-29 12:21:59 -08002796 size_report(SizeCalculator(fn, []))
Andrew Boiebbd670c2015-08-17 13:16:11 -07002797 sys.exit(0)
2798
Anas Nashif73440ea2018-02-19 10:57:03 -06002799
2800 if options.device_testing:
2801 if options.device_serial is None or len(options.platform) != 1:
2802 sys.exit(1)
2803
Anas Nashife10b6512017-12-30 13:01:45 -05002804 VERBOSE += options.verbose
2805 INLINE_LOGS = options.inline_logs
2806 if options.log_file:
2807 log_file = open(options.log_file, "w")
Oleg Zhurakivskyyc97054c2018-08-17 14:56:05 +03002808
Anas Nashife10b6512017-12-30 13:01:45 -05002809 if options.jobs:
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03002810 JOBS = options.jobs
Andrew Boie6acbe632015-07-17 12:03:52 -07002811
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03002812 # Decrease JOBS for Ninja, if jobs weren't explicitly set
Oleg Zhurakivskyyc97054c2018-08-17 14:56:05 +03002813 if options.ninja and not options.jobs:
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03002814 JOBS = int(JOBS * 0.75)
Oleg Zhurakivskyyc97054c2018-08-17 14:56:05 +03002815
Oleg Zhurakivskyy99aacd92018-08-17 15:02:28 +03002816 info("JOBS: %d" % JOBS);
Oleg Zhurakivskyyc97054c2018-08-17 14:56:05 +03002817
Anas Nashife10b6512017-12-30 13:01:45 -05002818 if options.subset:
2819 subset, sets = options.subset.split("/")
Anas Nashif035799f2017-05-13 21:31:53 -04002820 if int(subset) > 0 and int(sets) >= int(subset):
Anas Nashif3ba1d432017-12-05 15:28:44 -05002821 info("Running only a subset: %s/%s" % (subset, sets))
Anas Nashif035799f2017-05-13 21:31:53 -04002822 else:
Anas Nashife10b6512017-12-30 13:01:45 -05002823 error("You have provided a wrong subset value: %s." % options.subset)
Anas Nashif035799f2017-05-13 21:31:53 -04002824 return
2825
Anas Nashife10b6512017-12-30 13:01:45 -05002826 if os.path.exists(options.outdir) and not options.no_clean:
2827 info("Cleaning output directory " + options.outdir)
2828 shutil.rmtree(options.outdir)
Andrew Boie6acbe632015-07-17 12:03:52 -07002829
Anas Nashife10b6512017-12-30 13:01:45 -05002830 if not options.testcase_root:
2831 options.testcase_root = [os.path.join(ZEPHYR_BASE, "tests"),
Andrew Boie3d348712016-04-08 11:52:13 -07002832 os.path.join(ZEPHYR_BASE, "samples")]
2833
Anas Nashif37f9dc52018-02-23 08:53:46 -06002834 ts = TestSuite(options.board_root, options.testcase_root, options.outdir)
Anas Nashifbd166f42017-09-02 12:32:08 -04002835
Kumar Galac84235e2018-04-10 13:32:51 -05002836 if ts.load_errors:
2837 sys.exit(1)
2838
Anas Nashif75547e22018-02-24 08:32:14 -06002839 if options.list_tags:
2840 tags = set()
2841 for n,tc in ts.testcases.items():
2842 tags = tags.union(tc.tags)
2843
2844 for t in tags:
2845 print("- {}".format(t))
2846
2847 return
2848
Anas Nashif5d6e7eb2018-06-01 09:51:08 -05002849
2850 def export_tests(filename, tests):
2851 with open(filename, "wt") as csvfile:
2852 fieldnames = ['section', 'subsection', 'title', 'reference']
2853 cw = csv.DictWriter(csvfile, fieldnames, lineterminator=os.linesep)
2854 for test in tests:
2855 data = test.split(".")
2856 subsec = " ".join(data[1].split("_")).title()
2857 rowdict = {
2858 "section": data[0].capitalize(),
2859 "subsection": subsec,
2860 "title": test,
2861 "reference": test
2862 }
2863 cw.writerow(rowdict)
2864
2865 if options.export_tests:
2866 cnt = 0
2867 unq = []
2868 for n,tc in ts.testcases.items():
2869 for c in tc.cases:
2870 unq.append(c)
2871
2872 tests = sorted(set(unq))
2873 export_tests(options.export_tests, tests)
2874 return
2875
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002876 run_individual_tests = []
Anas Nashif5d6e7eb2018-06-01 09:51:08 -05002877
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002878 if options.test:
2879 run_individual_tests = options.test
2880
2881 if options.list_tests or options.sub_test:
Anas Nashifc0149cc2018-04-14 23:12:58 -05002882 cnt = 0
Anas Nashifa3abe962018-05-05 19:10:22 -05002883 unq = []
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002884 run_individual_tests = []
Anas Nashifc0149cc2018-04-14 23:12:58 -05002885 for n,tc in ts.testcases.items():
2886 for c in tc.cases:
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002887 if options.sub_test and c in options.sub_test:
2888 if tc.name not in run_individual_tests:
2889 run_individual_tests.append(tc.name)
Anas Nashifa3abe962018-05-05 19:10:22 -05002890 unq.append(c)
Anas Nashifc0149cc2018-04-14 23:12:58 -05002891
Anas Nashif1ea5d7b2018-07-12 09:25:22 -05002892 if options.sub_test:
2893 if run_individual_tests:
2894 info("Running the following tests:")
2895 for t in run_individual_tests:
2896 print(" - {}".format(t))
2897 else:
2898 info("Tests not found")
2899 return
2900
2901 elif options.list_tests:
2902 for u in sorted(set(unq)):
2903 cnt = cnt + 1
2904 print(" - {}".format(u))
2905 print("{} total.".format(cnt))
2906 return
Anas Nashifc0149cc2018-04-14 23:12:58 -05002907
Anas Nashifbd166f42017-09-02 12:32:08 -04002908 discards = []
Anas Nashife10b6512017-12-30 13:01:45 -05002909 if options.load_tests:
2910 ts.load_from_file(options.load_tests)
Anas Nashifbd166f42017-09-02 12:32:08 -04002911 else:
Anas Nashif4f028882017-12-30 11:48:43 -05002912 discards = ts.apply_filters()
Andrew Boie6acbe632015-07-17 12:03:52 -07002913
Anas Nashife10b6512017-12-30 13:01:45 -05002914 if options.discard_report:
2915 ts.discard_report(options.discard_report)
Andrew Boie6acbe632015-07-17 12:03:52 -07002916
Anas Nashif30551f42018-01-12 21:56:59 -05002917 if VERBOSE > 1 and discards:
Anas Nashiff18e2ab2018-01-13 07:57:42 -05002918 # if we are using command line platform filter, no need to list every
2919 # other platform as excluded, we know that already.
2920 # Show only the discards that apply to the selected platforms on the
2921 # command line
2922
Andrew Boie08ce5a52016-02-22 13:28:10 -08002923 for i, reason in discards.items():
Anas Nashiff18e2ab2018-01-13 07:57:42 -05002924 if options.platform and i.platform.name not in options.platform:
2925 continue
Anas Nashif3ba1d432017-12-05 15:28:44 -05002926 debug(
2927 "{:<25} {:<50} {}SKIPPED{}: {}".format(
2928 i.platform.name,
2929 i.test.name,
2930 COLOR_YELLOW,
2931 COLOR_NORMAL,
2932 reason))
Andrew Boie6acbe632015-07-17 12:03:52 -07002933
Anas Nashif1a5bba72018-01-05 08:07:45 -05002934
Alberto Escolar Piedras3777b442018-10-08 08:57:13 +02002935 def native_and_unit_first(a, b):
2936 if a[0].startswith('unit_testing'):
Anas Nashif1a5bba72018-01-05 08:07:45 -05002937 return -1
Alberto Escolar Piedras3777b442018-10-08 08:57:13 +02002938 if b[0].startswith('unit_testing'):
Anas Nashif1a5bba72018-01-05 08:07:45 -05002939 return 1
Alberto Escolar Piedras3777b442018-10-08 08:57:13 +02002940 if a[0].startswith('native_posix'):
2941 return -1
2942 if b[0].startswith('native_posix'):
2943 return 1
2944 if a[0].split("/",1)[0].endswith("_bsim"):
2945 return -1
2946 if b[0].split("/",1)[0].endswith("_bsim"):
2947 return 1
2948
Anas Nashif1a5bba72018-01-05 08:07:45 -05002949 return (a > b) - (a < b)
2950
2951 ts.instances = OrderedDict(sorted(ts.instances.items(),
Alberto Escolar Piedras3777b442018-10-08 08:57:13 +02002952 key=cmp_to_key(native_and_unit_first)))
Anas Nashifbd166f42017-09-02 12:32:08 -04002953
Anas Nashife10b6512017-12-30 13:01:45 -05002954 if options.save_tests:
2955 ts.run_report(options.save_tests)
Anas Nashifbd166f42017-09-02 12:32:08 -04002956 return
2957
Anas Nashife10b6512017-12-30 13:01:45 -05002958 if options.subset:
Anas Nashif1a5bba72018-01-05 08:07:45 -05002959
Anas Nashife10b6512017-12-30 13:01:45 -05002960 subset, sets = options.subset.split("/")
Anas Nashifbd166f42017-09-02 12:32:08 -04002961 total = len(ts.instances)
Anas Nashif035799f2017-05-13 21:31:53 -04002962 per_set = round(total / int(sets))
Anas Nashif3ba1d432017-12-05 15:28:44 -05002963 start = (int(subset) - 1) * per_set
Anas Nashif035799f2017-05-13 21:31:53 -04002964 if subset == sets:
2965 end = total
2966 else:
2967 end = start + per_set
2968
Anas Nashif3ba1d432017-12-05 15:28:44 -05002969 sliced_instances = islice(ts.instances.items(), start, end)
Anas Nashif035799f2017-05-13 21:31:53 -04002970 ts.instances = OrderedDict(sliced_instances)
2971
Andrew Boie6acbe632015-07-17 12:03:52 -07002972 info("%d tests selected, %d tests discarded due to filters" %
2973 (len(ts.instances), len(discards)))
2974
Anas Nashife10b6512017-12-30 13:01:45 -05002975 if options.dry_run:
Andrew Boie6acbe632015-07-17 12:03:52 -07002976 return
2977
2978 if VERBOSE or not TERMINAL:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002979 goals = ts.execute(
2980 chatty_test_cb,
Anas Nashif37f9dc52018-02-23 08:53:46 -06002981 ts.instances)
Andrew Boie6acbe632015-07-17 12:03:52 -07002982 else:
Anas Nashif3ba1d432017-12-05 15:28:44 -05002983 goals = ts.execute(
2984 terse_test_cb,
Anas Nashif37f9dc52018-02-23 08:53:46 -06002985 ts.instances)
Andrew Boie08ce5a52016-02-22 13:28:10 -08002986 info("")
Andrew Boie6acbe632015-07-17 12:03:52 -07002987
Anas Nashife0a6a0b2018-02-15 20:07:24 -06002988 if options.detailed_report:
2989 ts.testcase_target_report(options.detailed_report)
2990
Daniel Leung7f850102016-04-08 11:07:32 -07002991 # figure out which report to use for size comparison
Anas Nashife10b6512017-12-30 13:01:45 -05002992 if options.compare_report:
2993 report_to_use = options.compare_report
2994 elif options.last_metrics:
Daniel Leung7f850102016-04-08 11:07:32 -07002995 report_to_use = LAST_SANITY
2996 else:
2997 report_to_use = RELEASE_DATA
2998
2999 deltas = ts.compare_metrics(report_to_use)
Andrew Boie6acbe632015-07-17 12:03:52 -07003000 warnings = 0
Anas Nashif424a3db2018-02-20 08:37:24 -06003001 if deltas and options.show_footprint:
Andrew Boieea7928f2015-08-14 14:27:38 -07003002 for i, metric, value, delta, lower_better in deltas:
Anas Nashife10b6512017-12-30 13:01:45 -05003003 if not options.all_deltas and ((delta < 0 and lower_better) or
Andrew Boieea7928f2015-08-14 14:27:38 -07003004 (delta > 0 and not lower_better)):
Andrew Boie6acbe632015-07-17 12:03:52 -07003005 continue
3006
Andrew Boieea7928f2015-08-14 14:27:38 -07003007 percentage = (float(delta) / float(value - delta))
Anas Nashife10b6512017-12-30 13:01:45 -05003008 if not options.all_deltas and (percentage <
3009 (options.footprint_threshold / 100.0)):
Andrew Boieea7928f2015-08-14 14:27:38 -07003010 continue
3011
Daniel Leung00525c22016-04-11 10:27:56 -07003012 info("{:<25} {:<60} {}{}{}: {} {:<+4}, is now {:6} {:+.2%}".format(
Andrew Boieea7928f2015-08-14 14:27:38 -07003013 i.platform.name, i.test.name, COLOR_YELLOW,
Anas Nashife10b6512017-12-30 13:01:45 -05003014 "INFO" if options.all_deltas else "WARNING", COLOR_NORMAL,
Andrew Boie829c0562015-10-13 09:44:19 -07003015 metric, delta, value, percentage))
Andrew Boie6acbe632015-07-17 12:03:52 -07003016 warnings += 1
3017
3018 if warnings:
3019 info("Deltas based on metrics from last %s" %
Anas Nashife10b6512017-12-30 13:01:45 -05003020 ("release" if not options.last_metrics else "run"))
Andrew Boie6acbe632015-07-17 12:03:52 -07003021
3022 failed = 0
Andrew Boie08ce5a52016-02-22 13:28:10 -08003023 for name, goal in goals.items():
Andrew Boie6acbe632015-07-17 12:03:52 -07003024 if goal.failed:
3025 failed += 1
Sebastian Bøe03ed0952018-11-13 13:36:19 +01003026 elif goal.metrics.get("unrecognized") and not options.disable_unrecognized_section_test:
Andrew Boie73b4ee62015-10-07 11:33:22 -07003027 info("%sFAILED%s: %s has unrecognized binary sections: %s" %
3028 (COLOR_RED, COLOR_NORMAL, goal.name,
3029 str(goal.metrics["unrecognized"])))
3030 failed += 1
Andrew Boie6acbe632015-07-17 12:03:52 -07003031
Anas Nashife10b6512017-12-30 13:01:45 -05003032 if options.coverage:
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03003033 info("Generating coverage files...")
Anas Nashif4df6c562018-11-07 19:29:04 -05003034 generate_coverage(options.outdir, ["*generated*", "tests/*", "samples/*"])
Jaakko Hannikainen54c90bc2016-08-31 14:17:03 +03003035
Anas Nashif0605fa32017-05-07 08:51:02 -04003036 duration = time.time() - start_time
Andrew Boie4b182472015-07-31 12:25:22 -07003037 info("%s%d of %d%s tests passed with %s%d%s warnings in %d seconds" %
Anas Nashif3ba1d432017-12-05 15:28:44 -05003038 (COLOR_RED if failed else COLOR_GREEN, len(goals) - failed,
3039 len(goals), COLOR_NORMAL, COLOR_YELLOW if warnings else COLOR_NORMAL,
3040 warnings, COLOR_NORMAL, duration))
Andrew Boie6acbe632015-07-17 12:03:52 -07003041
Anas Nashife10b6512017-12-30 13:01:45 -05003042 if options.testcase_report:
3043 ts.testcase_report(options.testcase_report)
3044 if not options.no_update:
Anas Nashif4f028882017-12-30 11:48:43 -05003045 ts.testcase_xunit_report(LAST_SANITY_XUNIT, duration)
Andrew Boie6acbe632015-07-17 12:03:52 -07003046 ts.testcase_report(LAST_SANITY)
Anas Nashife10b6512017-12-30 13:01:45 -05003047 if options.release:
Andrew Boie6acbe632015-07-17 12:03:52 -07003048 ts.testcase_report(RELEASE_DATA)
Inaky Perez-Gonzalez9a36cb62016-11-29 10:43:40 -08003049 if log_file:
3050 log_file.close()
Anas Nashife10b6512017-12-30 13:01:45 -05003051 if failed or (warnings and options.warnings_as_errors):
Andrew Boie6acbe632015-07-17 12:03:52 -07003052 sys.exit(1)
3053
Anas Nashif3ba1d432017-12-05 15:28:44 -05003054
Andrew Boie6acbe632015-07-17 12:03:52 -07003055if __name__ == "__main__":
3056 main()