A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
test-ns3.py
Go to the documentation of this file.
1#! /usr/bin/env python3
2#
3# Copyright (c) 2021-2023 Universidade de Brasília
4#
5# SPDX-License-Identifier: GPL-2.0-only
6#
7# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
8
9"""!
10Test suite for the ns3 wrapper script
11"""
12
13import glob
14import os
15import platform
16import re
17import shutil
18import subprocess
19import sys
20import unittest
21from functools import partial
22
23# Get path containing ns3
24ns3_path = os.path.dirname(os.path.abspath(os.sep.join([__file__, "../../"])))
25ns3_lock_filename = os.path.join(ns3_path, ".lock-ns3_%s_build" % sys.platform)
26ns3_script = os.sep.join([ns3_path, "ns3"])
27ns3rc_script = os.sep.join([ns3_path, ".ns3rc"])
28usual_outdir = os.sep.join([ns3_path, "build"])
29usual_lib_outdir = os.sep.join([usual_outdir, "lib"])
30
31# Move the current working directory to the ns-3-dev folder
32os.chdir(ns3_path)
33
34# Cmake commands
35num_threads = max(1, os.cpu_count() - 1)
36cmake_build_project_command = "cmake --build {cmake_cache} -j".format(
37 ns3_path=ns3_path, cmake_cache=os.path.abspath(os.path.join(ns3_path, "cmake-cache"))
38)
39cmake_build_target_command = partial(
40 "cmake --build {cmake_cache} -j {jobs} --target {target}".format,
41 jobs=num_threads,
42 cmake_cache=os.path.abspath(os.path.join(ns3_path, "cmake-cache")),
43)
44win32 = sys.platform == "win32"
45macos = sys.platform == "darwin"
46platform_makefiles = "MinGW Makefiles" if win32 else "Unix Makefiles"
47ext = ".exe" if win32 else ""
48arch = platform.machine()
49
50
51def run_ns3(args, env=None, generator=platform_makefiles):
52 """!
53 Runs the ns3 wrapper script with arguments
54 @param args: string containing arguments that will get split before calling ns3
55 @param env: environment variables dictionary
56 @param generator: CMake generator
57 @return tuple containing (error code, stdout and stderr)
58 """
59 if "clean" in args:
60 possible_leftovers = ["contrib/borked", "contrib/calibre"]
61 for leftover in possible_leftovers:
62 if os.path.exists(leftover):
63 shutil.rmtree(leftover, ignore_errors=True)
64 if " -G " in args:
65 args = args.format(generator=generator)
66 if env is None:
67 env = {}
68 # Disable colored output by default during tests
69 env["CLICOLOR"] = "0"
70 return run_program(ns3_script, args, python=True, env=env)
71
72
73# Adapted from https://github.com/metabrainz/picard/blob/master/picard/util/__init__.py
74def run_program(program, args, python=False, cwd=ns3_path, env=None):
75 """!
76 Runs a program with the given arguments and returns a tuple containing (error code, stdout and stderr)
77 @param program: program to execute (or python script)
78 @param args: string containing arguments that will get split before calling the program
79 @param python: flag indicating whether the program is a python script
80 @param cwd: the working directory used that will be the root folder for the execution
81 @param env: environment variables dictionary
82 @return tuple containing (error code, stdout and stderr)
83 """
84 if type(args) != str:
85 raise Exception("args should be a string")
86
87 # Include python interpreter if running a python script
88 if python:
89 arguments = [sys.executable, program]
90 else:
91 arguments = [program]
92
93 if args != "":
94 arguments.extend(re.findall(r'(?:".*?"|\S)+', args)) # noqa
95
96 for i in range(len(arguments)):
97 arguments[i] = arguments[i].replace('"', "")
98
99 # Forward environment variables used by the ns3 script
100 current_env = os.environ.copy()
101
102 # Add different environment variables
103 if env:
104 current_env.update(env)
105
106 # Call program with arguments
107 ret = subprocess.run(
108 arguments,
109 stdin=subprocess.DEVNULL,
110 stdout=subprocess.PIPE,
111 stderr=subprocess.PIPE,
112 cwd=cwd, # run process from the ns-3-dev path
113 env=current_env,
114 )
115 # Return (error code, stdout and stderr)
116 return (
117 ret.returncode,
118 ret.stdout.decode(sys.stdout.encoding),
119 ret.stderr.decode(sys.stderr.encoding),
120 )
121
122
124 """!
125 Extracts the programs list from .lock-ns3
126 @return list of programs.
127 """
128 values = {}
129 with open(ns3_lock_filename, encoding="utf-8") as f:
130 exec(f.read(), globals(), values)
131
132 programs_list = values["ns3_runnable_programs"]
133
134 # Add .exe suffix to programs if on Windows
135 if win32:
136 programs_list = list(map(lambda x: x + ext, programs_list))
137 return programs_list
138
139
140def get_libraries_list(lib_outdir=usual_lib_outdir):
141 """!
142 Gets a list of built libraries
143 @param lib_outdir: path containing libraries
144 @return list of built libraries.
145 """
146 libraries = glob.glob(lib_outdir + "/*", recursive=True)
147 return list(filter(lambda x: "scratch-nested-subdir-lib" not in x, libraries))
148
149
150def get_headers_list(outdir=usual_outdir):
151 """!
152 Gets a list of header files
153 @param outdir: path containing headers
154 @return list of headers.
155 """
156 return glob.glob(outdir + "/**/*.h", recursive=True)
157
158
160 """!
161 Read interesting entries from the .lock-ns3 file
162 @param entry: entry to read from .lock-ns3
163 @return value of the requested entry.
164 """
165 values = {}
166 with open(ns3_lock_filename, encoding="utf-8") as f:
167 exec(f.read(), globals(), values)
168 return values.get(entry, None)
169
170
172 """!
173 Check if tests are enabled in the .lock-ns3
174 @return bool.
175 """
176 return read_lock_entry("ENABLE_TESTS")
177
178
180 """
181 Check if tests are enabled in the .lock-ns3
182 @return list of enabled modules (prefixed with 'ns3-').
183 """
184 return read_lock_entry("NS3_ENABLED_MODULES")
185
186
188 """!
189 Python-on-whales wrapper for Docker-based ns-3 tests
190 """
191
192 def __init__(self, currentTestCase: unittest.TestCase, containerName: str = "ubuntu:latest"):
193 """!
194 Create and start container with containerName in the current ns-3 directory
195 @param self: the current DockerContainerManager instance
196 @param currentTestCase: the test case instance creating the DockerContainerManager
197 @param containerName: name of the container image to be used
198 """
199 global DockerException
200 try:
201 from python_on_whales import docker
202 from python_on_whales.exceptions import DockerException
203 except ModuleNotFoundError:
204 docker = None # noqa
205 DockerException = None # noqa
206 currentTestCase.skipTest("python-on-whales was not found")
207
208 # Import rootless docker settings from .bashrc
209 with open(os.path.expanduser("~/.bashrc"), "r", encoding="utf-8") as f:
210 docker_settings = re.findall("(DOCKER_.*=.*)", f.read())
211 if docker_settings:
212 for setting in docker_settings:
213 key, value = setting.split("=")
214 os.environ[key] = value
215 del setting, key, value
216
217 # Create Docker client instance and start it
218 ## The Python-on-whales container instance
219 self.container = docker.run(
220 containerName,
221 interactive=True,
222 detach=True,
223 tty=False,
224 volumes=[(ns3_path, "/ns-3-dev")],
225 )
226
227 # Redefine the execute command of the container
228 def split_exec(docker_container, cmd):
229 return docker_container._execute(cmd.split(), workdir="/ns-3-dev")
230
231 self.container._execute = self.container.execute
232 self.container.execute = partial(split_exec, self.container)
233
234 def __enter__(self):
235 """!
236 Return the managed container when entiring the block "with DockerContainerManager() as container"
237 @param self: the current DockerContainerManager instance
238 @return container managed by DockerContainerManager.
239 """
240 return self.container
241
242 def __exit__(self, exc_type, exc_val, exc_tb):
243 """!
244 Clean up the managed container at the end of the block "with DockerContainerManager() as container"
245 @param self: the current DockerContainerManager instance
246 @param exc_type: unused parameter
247 @param exc_val: unused parameter
248 @param exc_tb: unused parameter
249 @return None
250 """
251 self.container.stop()
252 self.container.remove()
253
254
255class NS3UnusedSourcesTestCase(unittest.TestCase):
256 """!
257 ns-3 tests related to checking if source files were left behind, not being used by CMake
258 """
259
260 ## dictionary containing directories with .cc source files # noqa
261 directory_and_files = {}
262
263 def setUp(self):
264 """!
265 Scan all C++ source files and add them to a list based on their path
266 @return None
267 """
268 for root, dirs, files in os.walk(ns3_path):
269 if "gitlab-ci-local" in root:
270 continue
271 for name in files:
272 if name.endswith(".cc"):
273 path = os.path.join(root, name)
274 directory = os.path.dirname(path)
275 if directory not in self.directory_and_files:
276 self.directory_and_files[directory] = []
277 self.directory_and_files[directory].append(path)
278
280 """!
281 Test if all example source files are being used in their respective CMakeLists.txt
282 @return None
283 """
284 unused_sources = set()
285 for example_directory in self.directory_and_files.keys():
286 # Skip non-example directories
287 if os.sep + "examples" not in example_directory:
288 continue
289
290 # Skip directories without a CMakeLists.txt
291 if not os.path.exists(os.path.join(example_directory, "CMakeLists.txt")):
292 continue
293
294 # Open the examples CMakeLists.txt and read it
295 with open(
296 os.path.join(example_directory, "CMakeLists.txt"), "r", encoding="utf-8"
297 ) as f:
298 cmake_contents = f.read()
299
300 # For each file, check if it is in the CMake contents
301 for file in self.directory_and_files[example_directory]:
302 # We remove the .cc because some examples sources can be written as ${example_name}.cc
303 if os.path.basename(file).replace(".cc", "") not in cmake_contents:
304 unused_sources.add(file)
305
306 self.assertListEqual([], list(unused_sources))
307
309 """!
310 Test if all module source files are being used in their respective CMakeLists.txt
311 @return None
312 """
313 unused_sources = set()
314 for directory in self.directory_and_files.keys():
315 # Skip examples and bindings directories
316 is_not_module = not ("src" in directory or "contrib" in directory)
317 is_example = os.sep + "examples" in directory
318 is_bindings = os.sep + "bindings" in directory
319
320 if is_not_module or is_bindings or is_example:
321 continue
322
323 # We can be in one of the module subdirectories (helper, model, test, bindings, etc)
324 # Navigate upwards until we hit a CMakeLists.txt
325 cmake_path = os.path.join(directory, "CMakeLists.txt")
326 while not os.path.exists(cmake_path):
327 parent_directory = os.path.dirname(os.path.dirname(cmake_path))
328 cmake_path = os.path.join(parent_directory, os.path.basename(cmake_path))
329
330 # Open the module CMakeLists.txt and read it
331 with open(cmake_path, "r", encoding="utf-8") as f:
332 cmake_contents = f.read()
333
334 # For each file, check if it is in the CMake contents
335 for file in self.directory_and_files[directory]:
336 if os.path.basename(file) not in cmake_contents:
337 unused_sources.add(file)
338
339 # Remove temporary exceptions
340 exceptions = [
341 "win32-system-wall-clock-ms.cc", # Should be removed with MR784
342 ]
343 for exception in exceptions:
344 for unused_source in unused_sources:
345 if os.path.basename(unused_source) == exception:
346 unused_sources.remove(unused_source)
347 break
348
349 self.assertListEqual([], list(unused_sources))
350
352 """!
353 Test if all utils source files are being used in their respective CMakeLists.txt
354 @return None
355 """
356 unused_sources = set()
357 for directory in self.directory_and_files.keys():
358 # Skip directories that are not utils
359 is_module = "src" in directory or "contrib" in directory
360 if os.sep + "utils" not in directory or is_module:
361 continue
362
363 # We can be in one of the module subdirectories (helper, model, test, bindings, etc)
364 # Navigate upwards until we hit a CMakeLists.txt
365 cmake_path = os.path.join(directory, "CMakeLists.txt")
366 while not os.path.exists(cmake_path):
367 parent_directory = os.path.dirname(os.path.dirname(cmake_path))
368 cmake_path = os.path.join(parent_directory, os.path.basename(cmake_path))
369
370 # Open the module CMakeLists.txt and read it
371 with open(cmake_path, "r", encoding="utf-8") as f:
372 cmake_contents = f.read()
373
374 # For each file, check if it is in the CMake contents
375 for file in self.directory_and_files[directory]:
376 if os.path.basename(file) not in cmake_contents:
377 unused_sources.add(file)
378
379 self.assertListEqual([], list(unused_sources))
380
381
382class NS3DependenciesTestCase(unittest.TestCase):
383 """!
384 ns-3 tests related to dependencies
385 """
386
388 """!
389 Checks if headers from different modules (src/A, contrib/B) that are included by
390 the current module (src/C) source files correspond to the list of linked modules
391 LIBNAME C
392 LIBRARIES_TO_LINK A (missing B)
393 @return None
394 """
395 modules = {}
396 headers_to_modules = {}
397 module_paths = glob.glob(ns3_path + "/src/*/") + glob.glob(ns3_path + "/contrib/*/")
398
399 for path in module_paths:
400 # Open the module CMakeLists.txt and read it
401 cmake_path = os.path.join(path, "CMakeLists.txt")
402 with open(cmake_path, "r", encoding="utf-8") as f:
403 cmake_contents = f.readlines()
404
405 module_name = os.path.relpath(path, ns3_path)
406 module_name_nodir = module_name.replace("src/", "").replace("contrib/", "")
407 modules[module_name_nodir] = {
408 "sources": set(),
409 "headers": set(),
410 "libraries": set(),
411 "included_headers": set(),
412 "included_libraries": set(),
413 }
414
415 # Separate list of source files and header files
416 for line in cmake_contents:
417 source_file_path = re.findall(r"\b(?:[^\s]+\.[ch]{1,2})\b", line.strip())
418 if not source_file_path:
419 continue
420 source_file_path = source_file_path[0]
421 base_name = os.path.basename(source_file_path)
422 if not os.path.exists(os.path.join(path, source_file_path)):
423 continue
424
425 if ".h" in source_file_path:
426 # Register all module headers as module headers and sources
427 modules[module_name_nodir]["headers"].add(base_name)
428 modules[module_name_nodir]["sources"].add(base_name)
429
430 # Register the header as part of the current module
431 headers_to_modules[base_name] = module_name_nodir
432
433 if ".cc" in source_file_path:
434 # Register the source file as part of the current module
435 modules[module_name_nodir]["sources"].add(base_name)
436
437 if ".cc" in source_file_path or ".h" in source_file_path:
438 # Extract includes from headers and source files and then add to a list of included headers
439 source_file = os.path.join(ns3_path, module_name, source_file_path)
440 with open(source_file, "r", encoding="utf-8") as f:
441 source_contents = f.read()
442 modules[module_name_nodir]["included_headers"].update(
443 map(
444 lambda x: x.replace("ns3/", ""),
445 re.findall('#include.*["|<](.*)["|>]', source_contents),
446 )
447 )
448 continue
449
450 # Extract libraries linked to the module
451 modules[module_name_nodir]["libraries"].update(
452 re.findall(r"\${lib(.*?)}", "".join(cmake_contents))
453 )
454 modules[module_name_nodir]["libraries"] = list(
455 filter(
456 lambda x: x
457 not in ["raries_to_link", module_name_nodir, module_name_nodir + "-obj"],
458 modules[module_name_nodir]["libraries"],
459 )
460 )
461
462 # Now that we have all the information we need, check if we have all the included libraries linked
463 all_project_headers = set(headers_to_modules.keys())
464
465 sys.stderr.flush()
466 print(file=sys.stderr)
467 for module in sorted(modules):
468 external_headers = modules[module]["included_headers"].difference(all_project_headers)
469 project_headers_included = modules[module]["included_headers"].difference(
470 external_headers
471 )
472 modules[module]["included_libraries"] = set(
473 [headers_to_modules[x] for x in project_headers_included]
474 ).difference({module})
475
476 diff = modules[module]["included_libraries"].difference(modules[module]["libraries"])
477
478 # Find graph with least amount of edges based on included_libraries
479 def recursive_check_dependencies(checked_module):
480 # Remove direct explicit dependencies
481 for module_to_link in modules[checked_module]["included_libraries"]:
482 modules[checked_module]["included_libraries"] = set(
483 modules[checked_module]["included_libraries"]
484 ) - set(modules[module_to_link]["included_libraries"])
485
486 for module_to_link in modules[checked_module]["included_libraries"]:
487 recursive_check_dependencies(module_to_link)
488
489 # Remove unnecessary implicit dependencies
490 def is_implicitly_linked(searched_module, current_module):
491 if len(modules[current_module]["included_libraries"]) == 0:
492 return False
493 if searched_module in modules[current_module]["included_libraries"]:
494 return True
495 for module in modules[current_module]["included_libraries"]:
496 if is_implicitly_linked(searched_module, module):
497 return True
498 return False
499
500 from itertools import combinations
501
502 implicitly_linked = set()
503 for dep1, dep2 in combinations(modules[checked_module]["included_libraries"], 2):
504 if is_implicitly_linked(dep1, dep2):
505 implicitly_linked.add(dep1)
506 if is_implicitly_linked(dep2, dep1):
507 implicitly_linked.add(dep2)
508
509 modules[checked_module]["included_libraries"] = (
510 set(modules[checked_module]["included_libraries"]) - implicitly_linked
511 )
512
513 for module in modules:
514 recursive_check_dependencies(module)
515
516 # Print findings
517 for module in sorted(modules):
518 if module == "test":
519 continue
520 minimal_linking_set = ", ".join(modules[module]["included_libraries"])
521 unnecessarily_linked = ", ".join(
522 set(modules[module]["libraries"]) - set(modules[module]["included_libraries"])
523 )
524 missing_linked = ", ".join(
525 set(modules[module]["included_libraries"]) - set(modules[module]["libraries"])
526 )
527 if unnecessarily_linked:
528 print(f"Module '{module}' unnecessarily linked: {unnecessarily_linked}.")
529 if missing_linked:
530 print(f"Module '{module}' missing linked: {missing_linked}.")
531 if unnecessarily_linked or missing_linked:
532 print(f"Module '{module}' minimal linking set: {minimal_linking_set}.")
533 self.assertTrue(True)
534
535
536class NS3StyleTestCase(unittest.TestCase):
537 """!
538 ns-3 tests to check if the source code, whitespaces and CMake formatting
539 are according to the coding style
540 """
541
542 ## Holds the original diff, which must be maintained by each and every case tested # noqa
543 starting_diff = None
544 ## Holds the GitRepo's repository object # noqa
545 repo = None
546
547 def setUp(self) -> None:
548 """!
549 Import GitRepo and load the original diff state of the repository before the tests
550 @return None
551 """
552 if not NS3StyleTestCase.starting_diff:
553 if shutil.which("git") is None:
554 self.skipTest("Git is not available")
555
556 try:
557 import git.exc # noqa
558 from git import Repo # noqa
559 except ImportError:
560 self.skipTest("GitPython is not available")
561
562 try:
563 repo = Repo(ns3_path) # noqa
564 except git.exc.InvalidGitRepositoryError: # noqa
565 self.skipTest("ns-3 directory does not contain a .git directory")
566
567 hcommit = repo.head.commit # noqa
568 NS3StyleTestCase.starting_diff = hcommit.diff(None)
569 NS3StyleTestCase.repo = repo
570
571 if NS3StyleTestCase.starting_diff is None:
572 self.skipTest("Unmet dependencies")
573
575 """!
576 Check if there is any difference between tracked file after
577 applying cmake-format
578 @return None
579 """
580
581 for required_program in ["cmake", "cmake-format"]:
582 if shutil.which(required_program) is None:
583 self.skipTest("%s was not found" % required_program)
584
585 # Configure ns-3 to get the cmake-format target
586 return_code, stdout, stderr = run_ns3("configure")
587 self.assertEqual(return_code, 0)
588
589 # Build/run cmake-format
590 return_code, stdout, stderr = run_ns3("build cmake-format")
591 self.assertEqual(return_code, 0)
592
593 # Clean the ns-3 configuration
594 return_code, stdout, stderr = run_ns3("clean")
595 self.assertEqual(return_code, 0)
596
597 # Check if the diff still is the same
598 new_diff = NS3StyleTestCase.repo.head.commit.diff(None)
599 self.assertEqual(NS3StyleTestCase.starting_diff, new_diff)
600
601
602class NS3CommonSettingsTestCase(unittest.TestCase):
603 """!
604 ns3 tests related to generic options
605 """
606
607 def setUp(self):
608 """!
609 Clean configuration/build artifacts before common commands
610 @return None
611 """
612 super().setUp()
613 # No special setup for common test cases other than making sure we are working on a clean directory.
614 run_ns3("clean")
615
617 """!
618 Test not passing any arguments to
619 @return None
620 """
621 return_code, stdout, stderr = run_ns3("")
622 self.assertEqual(return_code, 1)
623 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
624
626 """!
627 Test only passing --quiet argument to ns3
628 @return None
629 """
630 return_code, stdout, stderr = run_ns3("--quiet")
631 self.assertEqual(return_code, 1)
632 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
633
635 """!
636 Test only passing 'show config' argument to ns3
637 @return None
638 """
639 return_code, stdout, stderr = run_ns3("show config")
640 self.assertEqual(return_code, 1)
641 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
642
644 """!
645 Test only passing 'show profile' argument to ns3
646 @return None
647 """
648 return_code, stdout, stderr = run_ns3("show profile")
649 self.assertEqual(return_code, 1)
650 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
651
653 """!
654 Test only passing 'show version' argument to ns3
655 @return None
656 """
657 return_code, stdout, stderr = run_ns3("show version")
658 self.assertEqual(return_code, 1)
659 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
660
661
662class NS3ConfigureBuildProfileTestCase(unittest.TestCase):
663 """!
664 ns3 tests related to build profiles
665 """
666
667 def setUp(self):
668 """!
669 Clean configuration/build artifacts before testing configuration settings
670 @return None
671 """
672 super().setUp()
673 # No special setup for common test cases other than making sure we are working on a clean directory.
674 run_ns3("clean")
675
676 def test_01_Debug(self):
677 """!
678 Test the debug build
679 @return None
680 """
681 return_code, stdout, stderr = run_ns3(
682 'configure -G "{generator}" -d debug --enable-verbose'
683 )
684 self.assertEqual(return_code, 0)
685 self.assertIn("Build profile : debug", stdout)
686 self.assertIn("Build files have been written to", stdout)
687
688 # Build core to check if profile suffixes match the expected.
689 return_code, stdout, stderr = run_ns3("build core")
690 self.assertEqual(return_code, 0)
691 self.assertIn("Built target core", stdout)
692
693 libraries = get_libraries_list()
694 self.assertGreater(len(libraries), 0)
695 self.assertIn("core-debug", libraries[0])
696
698 """!
699 Test the release build
700 @return None
701 """
702 return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d release')
703 self.assertEqual(return_code, 0)
704 self.assertIn("Build profile : release", stdout)
705 self.assertIn("Build files have been written to", stdout)
706
708 """!
709 Test the optimized build
710 @return None
711 """
712 return_code, stdout, stderr = run_ns3(
713 'configure -G "{generator}" -d optimized --enable-verbose'
714 )
715 self.assertEqual(return_code, 0)
716 self.assertIn("Build profile : optimized", stdout)
717 self.assertIn("Build files have been written to", stdout)
718
719 # Build core to check if profile suffixes match the expected
720 return_code, stdout, stderr = run_ns3("build core")
721 self.assertEqual(return_code, 0)
722 self.assertIn("Built target core", stdout)
723
724 libraries = get_libraries_list()
725 self.assertGreater(len(libraries), 0)
726 self.assertIn("core-optimized", libraries[0])
727
728 def test_04_Typo(self):
729 """!
730 Test a build type with a typo
731 @return None
732 """
733 return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d Optimized')
734 self.assertEqual(return_code, 2)
735 self.assertIn("invalid choice: 'Optimized'", stderr)
736
737 def test_05_TYPO(self):
738 """!
739 Test a build type with another typo
740 @return None
741 """
742 return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d OPTIMIZED')
743 self.assertEqual(return_code, 2)
744 self.assertIn("invalid choice: 'OPTIMIZED'", stderr)
745
747 """!
748 Replace settings set by default (e.g. ASSERT/LOGs enabled in debug builds and disabled in default ones)
749 @return None
750 """
751 return_code, _, _ = run_ns3("clean")
752 self.assertEqual(return_code, 0)
753
754 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --dry-run -d debug')
755 self.assertEqual(return_code, 0)
756 self.assertIn(
757 "-DCMAKE_BUILD_TYPE=debug -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF",
758 stdout,
759 )
760
761 return_code, stdout, stderr = run_ns3(
762 'configure -G "{generator}" --dry-run -d debug --disable-asserts --disable-logs --disable-werror'
763 )
764 self.assertEqual(return_code, 0)
765 self.assertIn(
766 "-DCMAKE_BUILD_TYPE=debug -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF",
767 stdout,
768 )
769
770 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --dry-run')
771 self.assertEqual(return_code, 0)
772 self.assertIn(
773 "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=OFF -DNS3_NATIVE_OPTIMIZATIONS=OFF",
774 stdout,
775 )
776
777 return_code, stdout, stderr = run_ns3(
778 'configure -G "{generator}" --dry-run --enable-asserts --enable-logs --enable-werror'
779 )
780 self.assertEqual(return_code, 0)
781 self.assertIn(
782 "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON",
783 stdout,
784 )
785
786
787class NS3BaseTestCase(unittest.TestCase):
788 """!
789 Generic test case with basic function inherited by more complex tests.
790 """
791
792 def config_ok(self, return_code, stdout, stderr):
793 """!
794 Check if configuration for release mode worked normally
795 @param return_code: return code from CMake
796 @param stdout: output from CMake.
797 @param stderr: error from CMake.
798 @return None
799 """
800 self.assertEqual(return_code, 0)
801 self.assertIn("Build profile : release", stdout)
802 self.assertIn("Build files have been written to", stdout)
803 self.assertNotIn("uninitialized variable", stderr)
804
805 def setUp(self):
806 """!
807 Clean configuration/build artifacts before testing configuration and build settings
808 After configuring the build as release,
809 check if configuration worked and check expected output files.
810 @return None
811 """
812 super().setUp()
813
814 if os.path.exists(ns3rc_script):
815 os.remove(ns3rc_script)
816
817 # Reconfigure from scratch before each test
818 run_ns3("clean")
819 return_code, stdout, stderr = run_ns3(
820 'configure -G "{generator}" -d release --enable-verbose'
821 )
822 self.config_ok(return_code, stdout, stderr)
823
824 # Check if .lock-ns3 exists, then read to get list of executables.
825 self.assertTrue(os.path.exists(ns3_lock_filename))
826 ## ns3_executables holds a list of executables in .lock-ns3 # noqa
828
829 # Check if .lock-ns3 exists than read to get the list of enabled modules.
830 self.assertTrue(os.path.exists(ns3_lock_filename))
831 ## ns3_modules holds a list to the modules enabled stored in .lock-ns3 # noqa
833
834
836 """!
837 Test ns3 configuration options
838 """
839
840 def setUp(self):
841 """!
842 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
843 @return None
844 """
845 super().setUp()
846
848 """!
849 Test enabling and disabling examples
850 @return None
851 """
852 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
853
854 # This just tests if we didn't break anything, not that we actually have enabled anything.
855 self.config_ok(return_code, stdout, stderr)
856
857 # If nothing went wrong, we should have more executables in the list after enabling the examples.
858 ## ns3_executables
859 self.assertGreater(len(get_programs_list()), len(self.ns3_executablesns3_executables))
860
861 # Now we disabled them back.
862 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-examples')
863
864 # This just tests if we didn't break anything, not that we actually have enabled anything.
865 self.config_ok(return_code, stdout, stderr)
866
867 # Then check if they went back to the original list.
868 self.assertEqual(len(get_programs_list()), len(self.ns3_executablesns3_executables))
869
870 def test_02_Tests(self):
871 """!
872 Test enabling and disabling tests
873 @return None
874 """
875 # Try enabling tests
876 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-tests')
877 self.config_ok(return_code, stdout, stderr)
878
879 # Then try building the libcore test
880 return_code, stdout, stderr = run_ns3("build core-test")
881
882 # If nothing went wrong, this should have worked
883 self.assertEqual(return_code, 0)
884 self.assertIn("Built target core-test", stdout)
885
886 # Now we disabled the tests
887 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-tests')
888 self.config_ok(return_code, stdout, stderr)
889
890 # Now building the library test should fail
891 return_code, stdout, stderr = run_ns3("build core-test")
892
893 # Then check if they went back to the original list
894 self.assertEqual(return_code, 1)
895 self.assertIn("Target to build does not exist: core-test", stdout)
896
898 """!
899 Test enabling specific modules
900 @return None
901 """
902 # Try filtering enabled modules to network+Wi-Fi and their dependencies
903 return_code, stdout, stderr = run_ns3(
904 "configure -G \"{generator}\" --enable-modules='network;wifi'"
905 )
906 self.config_ok(return_code, stdout, stderr)
907
908 # At this point we should have fewer modules
909 enabled_modules = get_enabled_modules()
910 ## ns3_modules
911 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
912 self.assertIn("ns3-network", enabled_modules)
913 self.assertIn("ns3-wifi", enabled_modules)
914
915 # Try enabling only core
916 return_code, stdout, stderr = run_ns3(
917 "configure -G \"{generator}\" --enable-modules='core'"
918 )
919 self.config_ok(return_code, stdout, stderr)
920 self.assertIn("ns3-core", get_enabled_modules())
921
922 # Try cleaning the list of enabled modules to reset to the normal configuration.
923 return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules=''")
924 self.config_ok(return_code, stdout, stderr)
925
926 # At this point we should have the same amount of modules that we had when we started.
927 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
928
930 """!
931 Test disabling specific modules
932 @return None
933 """
934 # Try filtering disabled modules to disable lte and modules that depend on it.
935 return_code, stdout, stderr = run_ns3(
936 "configure -G \"{generator}\" --disable-modules='lte;wimax'"
937 )
938 self.config_ok(return_code, stdout, stderr)
939
940 # At this point we should have fewer modules.
941 enabled_modules = get_enabled_modules()
942 self.assertLess(len(enabled_modules), len(self.ns3_modulesns3_modules))
943 self.assertNotIn("ns3-lte", enabled_modules)
944 self.assertNotIn("ns3-wimax", enabled_modules)
945
946 # Try cleaning the list of enabled modules to reset to the normal configuration.
947 return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules=''")
948 self.config_ok(return_code, stdout, stderr)
949
950 # At this point we should have the same amount of modules that we had when we started.
951 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
952
954 """!
955 Test enabling comma-separated (waf-style) examples
956 @return None
957 """
958 # Try filtering enabled modules to network+Wi-Fi and their dependencies.
959 return_code, stdout, stderr = run_ns3(
960 "configure -G \"{generator}\" --enable-modules='network,wifi'"
961 )
962 self.config_ok(return_code, stdout, stderr)
963
964 # At this point we should have fewer modules.
965 enabled_modules = get_enabled_modules()
966 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
967 self.assertIn("ns3-network", enabled_modules)
968 self.assertIn("ns3-wifi", enabled_modules)
969
970 # Try cleaning the list of enabled modules to reset to the normal configuration.
971 return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules=''")
972 self.config_ok(return_code, stdout, stderr)
973
974 # At this point we should have the same amount of modules that we had when we started.
975 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
976
978 """!
979 Test disabling comma-separated (waf-style) examples
980 @return None
981 """
982 # Try filtering disabled modules to disable lte and modules that depend on it.
983 return_code, stdout, stderr = run_ns3(
984 "configure -G \"{generator}\" --disable-modules='lte,mpi'"
985 )
986 self.config_ok(return_code, stdout, stderr)
987
988 # At this point we should have fewer modules.
989 enabled_modules = get_enabled_modules()
990 self.assertLess(len(enabled_modules), len(self.ns3_modulesns3_modules))
991 self.assertNotIn("ns3-lte", enabled_modules)
992 self.assertNotIn("ns3-mpi", enabled_modules)
993
994 # Try cleaning the list of enabled modules to reset to the normal configuration.
995 return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules=''")
996 self.config_ok(return_code, stdout, stderr)
997
998 # At this point we should have the same amount of modules that we had when we started.
999 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1000
1001 def test_07_Ns3rc(self):
1002 """!
1003 Test loading settings from the ns3rc config file
1004 @return None
1005 """
1006
1007 class ns3rc_str: # noqa
1008 ## python-based ns3rc template # noqa
1009 ns3rc_python_template = "# ! /usr/bin/env python\
1010 \
1011 # A list of the modules that will be enabled when ns-3 is run.\
1012 # Modules that depend on the listed modules will be enabled also.\
1013 #\
1014 # All modules can be enabled by choosing 'all_modules'.\
1015 modules_enabled = [{modules}]\
1016 \
1017 # Set this equal to true if you want examples to be run.\
1018 examples_enabled = {examples}\
1019 \
1020 # Set this equal to true if you want tests to be run.\
1021 tests_enabled = {tests}\
1022 "
1023
1024 ## cmake-based ns3rc template # noqa
1025 ns3rc_cmake_template = "set(ns3rc_tests_enabled {tests})\
1026 \nset(ns3rc_examples_enabled {examples})\
1027 \nset(ns3rc_enabled_modules {modules})\
1028 "
1029
1030 ## map ns3rc templates to types # noqa
1031 ns3rc_templates = {"python": ns3rc_python_template, "cmake": ns3rc_cmake_template}
1032
1033 def __init__(self, type_ns3rc):
1034 ## type contains the ns3rc variant type (deprecated python-based or current cmake-based)
1035 self.type = type_ns3rc
1036
1037 def format(self, **args):
1038 # Convert arguments from python-based ns3rc format to CMake
1039 if self.type == "cmake":
1040 args["modules"] = (
1041 args["modules"].replace("'", "").replace('"', "").replace(",", " ")
1042 )
1043 args["examples"] = "ON" if args["examples"] == "True" else "OFF"
1044 args["tests"] = "ON" if args["tests"] == "True" else "OFF"
1045
1046 formatted_string = ns3rc_str.ns3rc_templates[self.type].format(**args)
1047
1048 # Return formatted string
1049 return formatted_string
1050
1051 @staticmethod
1052 def types():
1053 return ns3rc_str.ns3rc_templates.keys()
1054
1055 for ns3rc_type in ns3rc_str.types():
1056 # Replace default format method from string with a custom one
1057 ns3rc_template = ns3rc_str(ns3rc_type)
1058
1059 # Now we repeat the command line tests but with the ns3rc file.
1060 with open(ns3rc_script, "w", encoding="utf-8") as f:
1061 f.write(ns3rc_template.format(modules="'lte'", examples="False", tests="True"))
1062
1063 # Reconfigure.
1064 run_ns3("clean")
1065 return_code, stdout, stderr = run_ns3(
1066 'configure -G "{generator}" -d release --enable-verbose'
1067 )
1068 self.config_ok(return_code, stdout, stderr)
1069
1070 # Check.
1071 enabled_modules = get_enabled_modules()
1072 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1073 self.assertIn("ns3-lte", enabled_modules)
1074 self.assertTrue(get_test_enabled())
1075 self.assertLessEqual(
1076 len(get_programs_list()), len(self.ns3_executablesns3_executables) + (win32 or macos)
1077 )
1078
1079 # Replace the ns3rc file with the wifi module, enabling examples and disabling tests
1080 with open(ns3rc_script, "w", encoding="utf-8") as f:
1081 f.write(ns3rc_template.format(modules="'wifi'", examples="True", tests="False"))
1082
1083 # Reconfigure
1084 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1085 self.config_ok(return_code, stdout, stderr)
1086
1087 # Check
1088 enabled_modules = get_enabled_modules()
1089 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1090 self.assertIn("ns3-wifi", enabled_modules)
1091 self.assertFalse(get_test_enabled())
1092 self.assertGreater(len(get_programs_list()), len(self.ns3_executablesns3_executables))
1093
1094 # Replace the ns3rc file with multiple modules
1095 with open(ns3rc_script, "w", encoding="utf-8") as f:
1096 f.write(
1097 ns3rc_template.format(
1098 modules="'core','network'", examples="True", tests="False"
1099 )
1100 )
1101
1102 # Reconfigure
1103 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1104 self.config_ok(return_code, stdout, stderr)
1105
1106 # Check
1107 enabled_modules = get_enabled_modules()
1108 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1109 self.assertIn("ns3-core", enabled_modules)
1110 self.assertIn("ns3-network", enabled_modules)
1111 self.assertFalse(get_test_enabled())
1112 self.assertGreater(len(get_programs_list()), len(self.ns3_executablesns3_executables))
1113
1114 # Replace the ns3rc file with multiple modules,
1115 # in various different ways and with comments
1116 with open(ns3rc_script, "w", encoding="utf-8") as f:
1117 if ns3rc_type == "python":
1118 f.write(
1119 ns3rc_template.format(
1120 modules="""'core', #comment
1121 'lte',
1122 #comment2,
1123 #comment3
1124 'network', 'internet','wimax'""",
1125 examples="True",
1126 tests="True",
1127 )
1128 )
1129 else:
1130 f.write(
1131 ns3rc_template.format(
1132 modules="'core', 'lte', 'network', 'internet', 'wimax'",
1133 examples="True",
1134 tests="True",
1135 )
1136 )
1137 # Reconfigure
1138 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1139 self.config_ok(return_code, stdout, stderr)
1140
1141 # Check
1142 enabled_modules = get_enabled_modules()
1143 self.assertLess(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1144 self.assertIn("ns3-core", enabled_modules)
1145 self.assertIn("ns3-internet", enabled_modules)
1146 self.assertIn("ns3-lte", enabled_modules)
1147 self.assertIn("ns3-wimax", enabled_modules)
1148 self.assertTrue(get_test_enabled())
1149 self.assertGreater(len(get_programs_list()), len(self.ns3_executablesns3_executables))
1150
1151 # Then we roll back by removing the ns3rc config file
1152 os.remove(ns3rc_script)
1153
1154 # Reconfigure
1155 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1156 self.config_ok(return_code, stdout, stderr)
1157
1158 # Check
1159 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1160 self.assertFalse(get_test_enabled())
1161 self.assertEqual(len(get_programs_list()), len(self.ns3_executablesns3_executables))
1162
1164 """!
1165 Test dry-run (printing commands to be executed instead of running them)
1166 @return None
1167 """
1168 run_ns3("clean")
1169
1170 # Try dry-run before and after the positional commands (outputs should match)
1171 for positional_command in ["configure", "build", "clean"]:
1172 return_code, stdout, stderr = run_ns3("--dry-run %s" % positional_command)
1173 return_code1, stdout1, stderr1 = run_ns3("%s --dry-run" % positional_command)
1174
1175 self.assertEqual(return_code, return_code1)
1176 self.assertEqual(stdout, stdout1)
1177 self.assertEqual(stderr, stderr1)
1178
1179 run_ns3("clean")
1180
1181 # Build target before using below
1182 run_ns3('configure -G "{generator}" -d release --enable-verbose')
1183 run_ns3("build scratch-simulator")
1184
1185 # Run all cases and then check outputs
1186 return_code0, stdout0, stderr0 = run_ns3("--dry-run run scratch-simulator")
1187 return_code1, stdout1, stderr1 = run_ns3("run scratch-simulator")
1188 return_code2, stdout2, stderr2 = run_ns3("--dry-run run scratch-simulator --no-build")
1189 return_code3, stdout3, stderr3 = run_ns3("run scratch-simulator --no-build")
1190
1191 # Return code and stderr should be the same for all of them.
1192 self.assertEqual(sum([return_code0, return_code1, return_code2, return_code3]), 0)
1193 self.assertEqual([stderr0, stderr1, stderr2, stderr3], [""] * 4)
1194
1195 scratch_path = None
1196 for program in get_programs_list():
1197 if "scratch-simulator" in program and "subdir" not in program:
1198 scratch_path = program
1199 break
1200
1201 # Scratches currently have a 'scratch_' prefix in their CMake targets
1202 # Case 0: dry-run + run (should print commands to build target and then run)
1203 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout0)
1204 self.assertIn(scratch_path, stdout0)
1205
1206 # Case 1: run (should print only make build message)
1207 self.assertNotIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout1)
1208 self.assertIn("Built target", stdout1)
1209 self.assertNotIn(scratch_path, stdout1)
1210
1211 # Case 2: dry-run + run-no-build (should print commands to run the target)
1212 self.assertIn("The following commands would be executed:", stdout2)
1213 self.assertIn(scratch_path, stdout2)
1214
1215 # Case 3: run-no-build (should print the target output only)
1216 self.assertNotIn("Finished executing the following commands:", stdout3)
1217 self.assertNotIn(scratch_path, stdout3)
1218
1220 """!
1221 Test if ns3 is propagating back the return code from the executables called with the run command
1222 @return None
1223 """
1224 # From this point forward we are reconfiguring in debug mode
1225 return_code, _, _ = run_ns3("clean")
1226 self.assertEqual(return_code, 0)
1227
1228 return_code, _, _ = run_ns3('configure -G "{generator}" --enable-examples --enable-tests')
1229 self.assertEqual(return_code, 0)
1230
1231 # Build necessary executables
1232 return_code, stdout, stderr = run_ns3("build command-line-example test-runner")
1233 self.assertEqual(return_code, 0)
1234
1235 # Now some tests will succeed normally
1236 return_code, stdout, stderr = run_ns3(
1237 'run "test-runner --test-name=command-line" --no-build'
1238 )
1239 self.assertEqual(return_code, 0)
1240
1241 # Now some tests will fail during NS_COMMANDLINE_INTROSPECTION
1242 return_code, stdout, stderr = run_ns3(
1243 'run "test-runner --test-name=command-line" --no-build',
1244 env={"NS_COMMANDLINE_INTROSPECTION": ".."},
1245 )
1246 self.assertNotEqual(return_code, 0)
1247
1248 # Cause a sigsegv
1249 sigsegv_example = os.path.join(ns3_path, "scratch", "sigsegv.cc")
1250 with open(sigsegv_example, "w", encoding="utf-8") as f:
1251 f.write(
1252 """
1253 int main (int argc, char *argv[])
1254 {
1255 char *s = "hello world"; *s = 'H';
1256 return 0;
1257 }
1258 """
1259 )
1260 return_code, stdout, stderr = run_ns3("run sigsegv")
1261 if win32:
1262 self.assertEqual(return_code, 4294967295) # unsigned -1
1263 self.assertIn("sigsegv-default.exe' returned non-zero exit status", stdout)
1264 else:
1265 self.assertEqual(return_code, 245)
1266 self.assertIn("sigsegv-default' died with <Signals.SIGSEGV: 11>", stdout)
1267
1268 # Cause an abort
1269 abort_example = os.path.join(ns3_path, "scratch", "abort.cc")
1270 with open(abort_example, "w", encoding="utf-8") as f:
1271 f.write(
1272 """
1273 #include "ns3/core-module.h"
1274
1275 using namespace ns3;
1276 int main (int argc, char *argv[])
1277 {
1278 NS_ABORT_IF(true);
1279 return 0;
1280 }
1281 """
1282 )
1283 return_code, stdout, stderr = run_ns3("run abort")
1284 if win32:
1285 self.assertNotEqual(return_code, 0)
1286 self.assertIn("abort-default.exe' returned non-zero exit status", stdout)
1287 else:
1288 self.assertEqual(return_code, 250)
1289 self.assertIn("abort-default' died with <Signals.SIGABRT: 6>", stdout)
1290
1291 os.remove(sigsegv_example)
1292 os.remove(abort_example)
1293
1295 """!
1296 Test passing 'show config' argument to ns3 to get the configuration table
1297 @return None
1298 """
1299 return_code, stdout, stderr = run_ns3("show config")
1300 self.assertEqual(return_code, 0)
1301 self.assertIn("Summary of ns-3 settings", stdout)
1302
1304 """!
1305 Test passing 'show profile' argument to ns3 to get the build profile
1306 @return None
1307 """
1308 return_code, stdout, stderr = run_ns3("show profile")
1309 self.assertEqual(return_code, 0)
1310 self.assertIn("Build profile: release", stdout)
1311
1313 """!
1314 Test passing 'show version' argument to ns3 to get the build version
1315 @return None
1316 """
1317 if shutil.which("git") is None:
1318 self.skipTest("git is not available")
1319
1320 return_code, _, _ = run_ns3('configure -G "{generator}" --enable-build-version')
1321 self.assertEqual(return_code, 0)
1322
1323 return_code, stdout, stderr = run_ns3("show version")
1324 self.assertEqual(return_code, 0)
1325 self.assertIn("ns-3 version:", stdout)
1326
1328 """!
1329 Test if CMake target names for scratches and ns3 shortcuts
1330 are working correctly
1331 @return None
1332 """
1333
1334 test_files = [
1335 "scratch/main.cc",
1336 "scratch/empty.cc",
1337 "scratch/subdir1/main.cc",
1338 "scratch/subdir2/main.cc",
1339 "scratch/main.test.dots.in.name.cc",
1340 ]
1341 backup_files = ["scratch/.main.cc"] # hidden files should be ignored
1342
1343 # Create test scratch files
1344 for path in test_files + backup_files:
1345 filepath = os.path.join(ns3_path, path)
1346 os.makedirs(os.path.dirname(filepath), exist_ok=True)
1347 with open(filepath, "w", encoding="utf-8") as f:
1348 if "main" in path:
1349 f.write("int main (int argc, char *argv[]){}")
1350 else:
1351 # no main function will prevent this target from
1352 # being created, we should skip it and continue
1353 # processing without crashing
1354 f.write("")
1355
1356 # Reload the cmake cache to pick them up
1357 # It will fail because the empty scratch has no main function
1358 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1359 self.assertEqual(return_code, 1)
1360
1361 # Remove the empty.cc file and try again
1362 empty = "scratch/empty.cc"
1363 os.remove(empty)
1364 test_files.remove(empty)
1365 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1366 self.assertEqual(return_code, 0)
1367
1368 # Try to build them with ns3 and cmake
1369 for path in test_files + backup_files:
1370 path = path.replace(".cc", "")
1371 return_code1, stdout1, stderr1 = run_program(
1372 "cmake",
1373 "--build . --target %s -j %d" % (path.replace("/", "_"), num_threads),
1374 cwd=os.path.join(ns3_path, "cmake-cache"),
1375 )
1376 return_code2, stdout2, stderr2 = run_ns3("build %s" % path)
1377 if "main" in path and ".main" not in path:
1378 self.assertEqual(return_code1, 0)
1379 self.assertEqual(return_code2, 0)
1380 else:
1381 self.assertEqual(return_code1, 2)
1382 self.assertEqual(return_code2, 1)
1383
1384 # Try to run them
1385 for path in test_files:
1386 path = path.replace(".cc", "")
1387 return_code, stdout, stderr = run_ns3("run %s --no-build" % path)
1388 if "main" in path:
1389 self.assertEqual(return_code, 0)
1390 else:
1391 self.assertEqual(return_code, 1)
1392
1393 run_ns3("clean")
1394 with DockerContainerManager(self, "ubuntu:22.04") as container:
1395 container.execute("apt-get update")
1396 container.execute("apt-get install -y python3 cmake g++ ninja-build")
1397 try:
1398 container.execute(
1399 "./ns3 configure --enable-modules=core,network,internet -- -DCMAKE_CXX_COMPILER=/usr/bin/g++"
1400 )
1401 except DockerException as e:
1402 self.fail()
1403 for path in test_files:
1404 path = path.replace(".cc", "")
1405 try:
1406 container.execute(f"./ns3 run {path}")
1407 except DockerException as e:
1408 if "main" in path:
1409 self.fail()
1410 run_ns3("clean")
1411
1412 # Delete the test files and reconfigure to clean them up
1413 for path in test_files + backup_files:
1414 source_absolute_path = os.path.join(ns3_path, path)
1415 os.remove(source_absolute_path)
1416 if "empty" in path or ".main" in path:
1417 continue
1418 filename = os.path.basename(path).replace(".cc", "")
1419 executable_absolute_path = os.path.dirname(os.path.join(ns3_path, "build", path))
1420 if os.path.exists(executable_absolute_path):
1421 executable_name = list(
1422 filter(lambda x: filename in x, os.listdir(executable_absolute_path))
1423 )[0]
1424
1425 os.remove(os.path.join(executable_absolute_path, executable_name))
1426 if not os.listdir(os.path.dirname(path)):
1427 os.rmdir(os.path.dirname(source_absolute_path))
1428
1429 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1430 self.assertEqual(return_code, 0)
1431
1433 """!
1434 Test if ns3 is inserting additional arguments by MPICH and OpenMPI to run on the CI
1435 @return None
1436 """
1437 # Skip test if mpi is not installed
1438 if shutil.which("mpiexec") is None or win32:
1439 self.skipTest("Mpi is not available")
1440
1441 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
1442 self.assertEqual(return_code, 0)
1443 executables = get_programs_list()
1444
1445 # Ensure sample simulator was built
1446 return_code, stdout, stderr = run_ns3("build sample-simulator")
1447 self.assertEqual(return_code, 0)
1448
1449 # Get executable path
1450 sample_simulator_path = list(filter(lambda x: "sample-simulator" in x, executables))[0]
1451
1452 mpi_command = '--dry-run run sample-simulator --command-template="mpiexec -np 2 %s"'
1453 non_mpi_command = '--dry-run run sample-simulator --command-template="echo %s"'
1454
1455 # Get the commands to run sample-simulator in two processes with mpi
1456 return_code, stdout, stderr = run_ns3(mpi_command)
1457 self.assertEqual(return_code, 0)
1458 self.assertIn("mpiexec -np 2 %s" % sample_simulator_path, stdout)
1459
1460 # Get the commands to run sample-simulator in two processes with mpi, now with the environment variable
1461 return_code, stdout, stderr = run_ns3(mpi_command)
1462 self.assertEqual(return_code, 0)
1463 if os.getenv("USER", "") == "root":
1464 if shutil.which("ompi_info"):
1465 self.assertIn(
1466 "mpiexec --allow-run-as-root --oversubscribe -np 2 %s" % sample_simulator_path,
1467 stdout,
1468 )
1469 else:
1470 self.assertIn(
1471 "mpiexec --allow-run-as-root -np 2 %s" % sample_simulator_path, stdout
1472 )
1473 else:
1474 self.assertIn("mpiexec -np 2 %s" % sample_simulator_path, stdout)
1475
1476 # Now we repeat for the non-mpi command
1477 return_code, stdout, stderr = run_ns3(non_mpi_command)
1478 self.assertEqual(return_code, 0)
1479 self.assertIn("echo %s" % sample_simulator_path, stdout)
1480
1481 # Again the non-mpi command, with the MPI_CI environment variable set
1482 return_code, stdout, stderr = run_ns3(non_mpi_command)
1483 self.assertEqual(return_code, 0)
1484 self.assertIn("echo %s" % sample_simulator_path, stdout)
1485
1486 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-examples')
1487 self.assertEqual(return_code, 0)
1488
1490 """!
1491 Test if CMake and ns3 fail in the expected ways when:
1492 - examples from modules or general examples fail if they depend on a
1493 library with a name shorter than 4 characters or are disabled when
1494 a library is nonexistent
1495 - a module library passes the configuration but fails to build due to
1496 a missing library
1497 @return None
1498 """
1499 os.makedirs("contrib/borked", exist_ok=True)
1500 os.makedirs("contrib/borked/examples", exist_ok=True)
1501
1502 # Test if configuration succeeds and building the module library fails
1503 with open("contrib/borked/examples/CMakeLists.txt", "w", encoding="utf-8") as f:
1504 f.write("")
1505 for invalid_or_nonexistent_library in ["", "gsd", "lib", "libfi", "calibre"]:
1506 with open("contrib/borked/CMakeLists.txt", "w", encoding="utf-8") as f:
1507 f.write(
1508 """
1509 build_lib(
1510 LIBNAME borked
1511 SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
1512 LIBRARIES_TO_LINK ${libcore} %s
1513 )
1514 """
1515 % invalid_or_nonexistent_library
1516 )
1517
1518 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
1519 if invalid_or_nonexistent_library in ["", "gsd", "libfi", "calibre"]:
1520 self.assertEqual(return_code, 0)
1521 elif invalid_or_nonexistent_library in ["lib"]:
1522 self.assertEqual(return_code, 1)
1523 self.assertIn("Invalid library name: %s" % invalid_or_nonexistent_library, stderr)
1524 else:
1525 pass
1526
1527 return_code, stdout, stderr = run_ns3("build borked")
1528 if invalid_or_nonexistent_library in [""]:
1529 self.assertEqual(return_code, 0)
1530 elif invalid_or_nonexistent_library in ["lib"]:
1531 self.assertEqual(return_code, 2) # should fail due to invalid library name
1532 self.assertIn("Invalid library name: %s" % invalid_or_nonexistent_library, stderr)
1533 elif invalid_or_nonexistent_library in ["gsd", "libfi", "calibre"]:
1534 self.assertEqual(return_code, 2) # should fail due to missing library
1535 if "lld" in stdout + stderr:
1536 self.assertIn(
1537 "unable to find library -l%s" % invalid_or_nonexistent_library, stderr
1538 )
1539 elif "mold" in stdout + stderr:
1540 self.assertIn("library not found: %s" % invalid_or_nonexistent_library, stderr)
1541 elif macos:
1542 self.assertIn(
1543 "library not found for -l%s" % invalid_or_nonexistent_library, stderr
1544 )
1545 else:
1546 self.assertIn("cannot find -l%s" % invalid_or_nonexistent_library, stderr)
1547 else:
1548 pass
1549
1550 # Now test if the example can be built with:
1551 # - no additional library (should work)
1552 # - invalid library names (should fail to configure)
1553 # - valid library names but nonexistent libraries (should not create a target)
1554 with open("contrib/borked/CMakeLists.txt", "w", encoding="utf-8") as f:
1555 f.write(
1556 """
1557 build_lib(
1558 LIBNAME borked
1559 SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
1560 LIBRARIES_TO_LINK ${libcore}
1561 )
1562 """
1563 )
1564 for invalid_or_nonexistent_library in ["", "gsd", "lib", "libfi", "calibre"]:
1565 with open("contrib/borked/examples/CMakeLists.txt", "w", encoding="utf-8") as f:
1566 f.write(
1567 """
1568 build_lib_example(
1569 NAME borked-example
1570 SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty-main.cc
1571 LIBRARIES_TO_LINK ${libborked} %s
1572 )
1573 """
1574 % invalid_or_nonexistent_library
1575 )
1576
1577 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1578 if invalid_or_nonexistent_library in ["", "gsd", "libfi", "calibre"]:
1579 self.assertEqual(return_code, 0) # should be able to configure
1580 elif invalid_or_nonexistent_library in ["lib"]:
1581 self.assertEqual(return_code, 1) # should fail to even configure
1582 self.assertIn("Invalid library name: %s" % invalid_or_nonexistent_library, stderr)
1583 else:
1584 pass
1585
1586 return_code, stdout, stderr = run_ns3("build borked-example")
1587 if invalid_or_nonexistent_library in [""]:
1588 self.assertEqual(return_code, 0) # should be able to build
1589 elif invalid_or_nonexistent_library in ["libf"]:
1590 self.assertEqual(return_code, 2) # should fail due to missing configuration
1591 self.assertIn("Invalid library name: %s" % invalid_or_nonexistent_library, stderr)
1592 elif invalid_or_nonexistent_library in ["gsd", "libfi", "calibre"]:
1593 self.assertEqual(return_code, 1) # should fail to find target
1594 self.assertIn("Target to build does not exist: borked-example", stdout)
1595 else:
1596 pass
1597
1598 shutil.rmtree("contrib/borked", ignore_errors=True)
1599
1601 """!
1602 Test if CMake can properly handle modules containing "lib",
1603 which is used internally as a prefix for module libraries
1604 @return None
1605 """
1606
1607 os.makedirs("contrib/calibre", exist_ok=True)
1608 os.makedirs("contrib/calibre/examples", exist_ok=True)
1609
1610 # Now test if we can have a library with "lib" in it
1611 with open("contrib/calibre/examples/CMakeLists.txt", "w", encoding="utf-8") as f:
1612 f.write("")
1613 with open("contrib/calibre/CMakeLists.txt", "w", encoding="utf-8") as f:
1614 f.write(
1615 """
1616 build_lib(
1617 LIBNAME calibre
1618 SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
1619 LIBRARIES_TO_LINK ${libcore}
1620 )
1621 """
1622 )
1623
1624 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
1625
1626 # This only checks if configuration passes
1627 self.assertEqual(return_code, 0)
1628
1629 # This checks if the contrib modules were printed correctly
1630 self.assertIn("calibre", stdout)
1631
1632 # This checks not only if "lib" from "calibre" was incorrectly removed,
1633 # but also if the pkgconfig file was generated with the correct name
1634 self.assertNotIn("care", stdout)
1635 self.assertTrue(
1636 os.path.exists(os.path.join(ns3_path, "cmake-cache", "pkgconfig", "ns3-calibre.pc"))
1637 )
1638
1639 # Check if we can build this library
1640 return_code, stdout, stderr = run_ns3("build calibre")
1641 self.assertEqual(return_code, 0)
1642 self.assertIn(cmake_build_target_command(target="calibre"), stdout)
1643
1644 shutil.rmtree("contrib/calibre", ignore_errors=True)
1645
1647 """!
1648 Test if CMake performance tracing works and produces the
1649 cmake_performance_trace.log file
1650 @return None
1651 """
1652 cmake_performance_trace_log = os.path.join(ns3_path, "cmake_performance_trace.log")
1653 if os.path.exists(cmake_performance_trace_log):
1654 os.remove(cmake_performance_trace_log)
1655
1656 return_code, stdout, stderr = run_ns3("configure --trace-performance")
1657 self.assertEqual(return_code, 0)
1658 if win32:
1659 self.assertIn("--profiling-format=google-trace --profiling-output=", stdout)
1660 else:
1661 self.assertIn(
1662 "--profiling-format=google-trace --profiling-output=./cmake_performance_trace.log",
1663 stdout,
1664 )
1665 self.assertTrue(os.path.exists(cmake_performance_trace_log))
1666
1668 """!
1669 Check if ENABLE_BUILD_VERSION and version.cache are working
1670 as expected
1671 @return None
1672 """
1673
1674 # Create Docker client instance and start it
1675 with DockerContainerManager(self, "ubuntu:22.04") as container:
1676 # Install basic packages
1677 container.execute("apt-get update")
1678 container.execute("apt-get install -y python3 ninja-build cmake g++")
1679
1680 # Clean ns-3 artifacts
1681 container.execute("./ns3 clean")
1682
1683 # Set path to version.cache file
1684 version_cache_file = os.path.join(ns3_path, "src/core/model/version.cache")
1685
1686 # First case: try without a version cache or Git
1687 if os.path.exists(version_cache_file):
1688 os.remove(version_cache_file)
1689
1690 # We need to catch the exception since the command will fail
1691 try:
1692 container.execute("./ns3 configure -G Ninja --enable-build-version")
1693 except DockerException:
1694 pass
1695 self.assertFalse(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1696
1697 # Second case: try with a version cache file but without Git (it should succeed)
1698 version_cache_contents = (
1699 "CLOSEST_TAG = '\"ns-3.0.0\"'\n"
1700 "VERSION_COMMIT_HASH = '\"0000000000\"'\n"
1701 "VERSION_DIRTY_FLAG = '0'\n"
1702 "VERSION_MAJOR = '3'\n"
1703 "VERSION_MINOR = '0'\n"
1704 "VERSION_PATCH = '0'\n"
1705 "VERSION_RELEASE_CANDIDATE = '\"\"'\n"
1706 "VERSION_TAG = '\"ns-3.0.0\"'\n"
1707 "VERSION_TAG_DISTANCE = '0'\n"
1708 "VERSION_BUILD_PROFILE = 'debug'\n"
1709 )
1710 with open(version_cache_file, "w", encoding="utf-8") as version:
1711 version.write(version_cache_contents)
1712
1713 # Configuration should now succeed
1714 container.execute("./ns3 clean")
1715 container.execute("./ns3 configure -G Ninja --enable-build-version")
1716 container.execute("./ns3 build core")
1717 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1718
1719 # And contents of version cache should be unchanged
1720 with open(version_cache_file, "r", encoding="utf-8") as version:
1721 self.assertEqual(version.read(), version_cache_contents)
1722
1723 # Third case: we rename the .git directory temporarily and reconfigure
1724 # to check if it gets configured successfully when Git is found but
1725 # there is not .git history
1726 os.rename(os.path.join(ns3_path, ".git"), os.path.join(ns3_path, "temp_git"))
1727 try:
1728 container.execute("apt-get install -y git")
1729 container.execute("./ns3 clean")
1730 container.execute("./ns3 configure -G Ninja --enable-build-version")
1731 container.execute("./ns3 build core")
1732 except DockerException:
1733 pass
1734 os.rename(os.path.join(ns3_path, "temp_git"), os.path.join(ns3_path, ".git"))
1735 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1736
1737 # Fourth case: test with Git and git history. Now the version.cache should be replaced.
1738 container.execute("./ns3 clean")
1739 container.execute("./ns3 configure -G Ninja --enable-build-version")
1740 container.execute("./ns3 build core")
1741 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1742 with open(version_cache_file, "r", encoding="utf-8") as version:
1743 self.assertNotEqual(version.read(), version_cache_contents)
1744
1745 # Remove version cache file if it exists
1746 if os.path.exists(version_cache_file):
1747 os.remove(version_cache_file)
1748
1750 """!
1751 Test filtering in examples and tests from specific modules
1752 @return None
1753 """
1754 # Try filtering enabled modules to core+network and their dependencies
1755 return_code, stdout, stderr = run_ns3(
1756 'configure -G "{generator}" --enable-examples --enable-tests'
1757 )
1758 self.config_ok(return_code, stdout, stderr)
1759
1760 modules_before_filtering = get_enabled_modules()
1761 programs_before_filtering = get_programs_list()
1762
1763 return_code, stdout, stderr = run_ns3(
1764 "configure -G \"{generator}\" --filter-module-examples-and-tests='core;network'"
1765 )
1766 self.config_ok(return_code, stdout, stderr)
1767
1768 modules_after_filtering = get_enabled_modules()
1769 programs_after_filtering = get_programs_list()
1770
1771 # At this point we should have the same number of modules
1772 self.assertEqual(len(modules_after_filtering), len(modules_before_filtering))
1773 # But less executables
1774 self.assertLess(len(programs_after_filtering), len(programs_before_filtering))
1775
1776 # Try filtering in only core
1777 return_code, stdout, stderr = run_ns3(
1778 "configure -G \"{generator}\" --filter-module-examples-and-tests='core'"
1779 )
1780 self.config_ok(return_code, stdout, stderr)
1781
1782 # At this point we should have the same number of modules
1783 self.assertEqual(len(get_enabled_modules()), len(modules_after_filtering))
1784 # But less executables
1785 self.assertLess(len(get_programs_list()), len(programs_after_filtering))
1786
1787 # Try cleaning the list of enabled modules to reset to the normal configuration.
1788 return_code, stdout, stderr = run_ns3(
1789 "configure -G \"{generator}\" --disable-examples --disable-tests --filter-module-examples-and-tests=''"
1790 )
1791 self.config_ok(return_code, stdout, stderr)
1792
1793 # At this point we should have the same amount of modules that we had when we started.
1794 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modulesns3_modules))
1795 self.assertEqual(len(get_programs_list()), len(self.ns3_executablesns3_executables))
1796
1798 """!
1799 Check if fast linkers LLD and Mold are correctly found and configured
1800 @return None
1801 """
1802
1803 run_ns3("clean")
1804 with DockerContainerManager(self, "gcc:12.1") as container:
1805 # Install basic packages
1806 container.execute("apt-get update")
1807 container.execute("apt-get install -y python3 ninja-build cmake g++ lld")
1808
1809 # Configure should detect and use lld
1810 container.execute("./ns3 configure -G Ninja")
1811
1812 # Check if configuration properly detected lld
1813 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1814 with open(
1815 os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
1816 ) as f:
1817 self.assertIn("-fuse-ld=lld", f.read())
1818
1819 # Try to build using the lld linker
1820 try:
1821 container.execute("./ns3 build core")
1822 except DockerException:
1823 self.assertTrue(False, "Build with lld failed")
1824
1825 # Now add mold to the PATH
1826 if not os.path.exists(f"./mold-1.4.2-{arch}-linux.tar.gz"):
1827 container.execute(
1828 f"wget https://github.com/rui314/mold/releases/download/v1.4.2/mold-1.4.2-{arch}-linux.tar.gz"
1829 )
1830 container.execute(
1831 f"tar xzfC mold-1.4.2-{arch}-linux.tar.gz /usr/local --strip-components=1"
1832 )
1833
1834 # Configure should detect and use mold
1835 run_ns3("clean")
1836 container.execute("./ns3 configure -G Ninja")
1837
1838 # Check if configuration properly detected mold
1839 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1840 with open(
1841 os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
1842 ) as f:
1843 self.assertIn("-fuse-ld=mold", f.read())
1844
1845 # Try to build using the lld linker
1846 try:
1847 container.execute("./ns3 build core")
1848 except DockerException:
1849 self.assertTrue(False, "Build with mold failed")
1850
1851 # Delete mold leftovers
1852 os.remove(f"./mold-1.4.2-{arch}-linux.tar.gz")
1853
1854 # Disable use of fast linkers
1855 container.execute("./ns3 configure -G Ninja -- -DNS3_FAST_LINKERS=OFF")
1856
1857 # Check if configuration properly disabled lld/mold usage
1858 self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
1859 with open(
1860 os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
1861 ) as f:
1862 self.assertNotIn("-fuse-ld=mold", f.read())
1863
1865 """!
1866 Check if NS3_CLANG_TIMETRACE feature is working
1867 Clang's -ftime-trace plus ClangAnalyzer report
1868 @return None
1869 """
1870
1871 run_ns3("clean")
1872 with DockerContainerManager(self, "ubuntu:22.04") as container:
1873 container.execute("apt-get update")
1874 container.execute("apt-get install -y python3 ninja-build cmake clang-12")
1875
1876 # Enable ClangTimeTrace without git (it should fail)
1877 try:
1878 container.execute(
1879 "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -DNS3_CLANG_TIMETRACE=ON"
1880 )
1881 except DockerException as e:
1882 self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
1883
1884 container.execute("apt-get install -y git")
1885
1886 # Enable ClangTimeTrace without git (it should succeed)
1887 try:
1888 container.execute(
1889 "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -DNS3_CLANG_TIMETRACE=ON"
1890 )
1891 except DockerException as e:
1892 self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
1893
1894 # Clean leftover time trace report
1895 time_trace_report_path = os.path.join(ns3_path, "ClangBuildAnalyzerReport.txt")
1896 if os.path.exists(time_trace_report_path):
1897 os.remove(time_trace_report_path)
1898
1899 # Build new time trace report
1900 try:
1901 container.execute("./ns3 build timeTraceReport")
1902 except DockerException as e:
1903 self.assertTrue(False, "Failed to build the ClangAnalyzer's time trace report")
1904
1905 # Check if the report exists
1906 self.assertTrue(os.path.exists(time_trace_report_path))
1907
1908 # Now try with GCC, which should fail during the configuration
1909 run_ns3("clean")
1910 container.execute("apt-get install -y g++")
1911 container.execute("apt-get remove -y clang-12")
1912
1913 try:
1914 container.execute(
1915 "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DNS3_CLANG_TIMETRACE=ON"
1916 )
1917 self.assertTrue(
1918 False, "ClangTimeTrace requires Clang, but GCC just passed the checks too"
1919 )
1920 except DockerException as e:
1921 self.assertIn("TimeTrace is a Clang feature", e.stderr)
1922
1924 """!
1925 Check if NS3_NINJA_TRACE feature is working
1926 Ninja's .ninja_log conversion to about://tracing
1927 json format conversion with Ninjatracing
1928 @return None
1929 """
1930
1931 run_ns3("clean")
1932 with DockerContainerManager(self, "ubuntu:20.04") as container:
1933 container.execute("apt-get update")
1934 container.execute("apt-get remove -y g++")
1935 container.execute("apt-get install -y python3 cmake g++-10 clang-11")
1936
1937 # Enable Ninja tracing without using the Ninja generator
1938 try:
1939 container.execute(
1940 "./ns3 configure --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-11"
1941 )
1942 except DockerException as e:
1943 self.assertIn("Ninjatracing requires the Ninja generator", e.stderr)
1944
1945 # Clean build system leftovers
1946 run_ns3("clean")
1947
1948 container.execute("apt-get install -y ninja-build")
1949 # Enable Ninjatracing support without git (should fail)
1950 try:
1951 container.execute(
1952 "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-11"
1953 )
1954 except DockerException as e:
1955 self.assertIn("could not find git for clone of NinjaTracing", e.stderr)
1956
1957 container.execute("apt-get install -y git")
1958 # Enable Ninjatracing support with git (it should succeed)
1959 try:
1960 container.execute(
1961 "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-11"
1962 )
1963 except DockerException as e:
1964 self.assertTrue(False, "Failed to configure with Ninjatracing")
1965
1966 # Clean leftover ninja trace
1967 ninja_trace_path = os.path.join(ns3_path, "ninja_performance_trace.json")
1968 if os.path.exists(ninja_trace_path):
1969 os.remove(ninja_trace_path)
1970
1971 # Build the core module
1972 container.execute("./ns3 build core")
1973
1974 # Build new ninja trace
1975 try:
1976 container.execute("./ns3 build ninjaTrace")
1977 except DockerException as e:
1978 self.assertTrue(False, "Failed to run Ninjatracing's tool to build the trace")
1979
1980 # Check if the report exists
1981 self.assertTrue(os.path.exists(ninja_trace_path))
1982 trace_size = os.stat(ninja_trace_path).st_size
1983 os.remove(ninja_trace_path)
1984
1985 run_ns3("clean")
1986
1987 # Enable Clang TimeTrace feature for more detailed traces
1988 try:
1989 container.execute(
1990 "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-11 -DNS3_CLANG_TIMETRACE=ON"
1991 )
1992 except DockerException as e:
1993 self.assertTrue(False, "Failed to configure Ninjatracing with Clang's TimeTrace")
1994
1995 # Build the core module
1996 container.execute("./ns3 build core")
1997
1998 # Build new ninja trace
1999 try:
2000 container.execute("./ns3 build ninjaTrace")
2001 except DockerException as e:
2002 self.assertTrue(False, "Failed to run Ninjatracing's tool to build the trace")
2003
2004 self.assertTrue(os.path.exists(ninja_trace_path))
2005 timetrace_size = os.stat(ninja_trace_path).st_size
2006 os.remove(ninja_trace_path)
2007
2008 # Check if timetrace's trace is bigger than the original trace (it should be)
2009 self.assertGreater(timetrace_size, trace_size)
2010
2012 """!
2013 Check if precompiled headers are being enabled correctly.
2014 @return None
2015 """
2016
2017 run_ns3("clean")
2018
2019 # Ubuntu 22.04 ships with:
2020 # - cmake 3.22: does support PCH
2021 # - ccache 4.5: compatible with pch
2022 with DockerContainerManager(self, "ubuntu:22.04") as container:
2023 container.execute("apt-get update")
2024 container.execute("apt-get install -y python3 cmake ccache g++")
2025 try:
2026 container.execute("./ns3 configure")
2027 except DockerException as e:
2028 self.assertTrue(False, "Precompiled headers should have been enabled")
2029
2031 """!
2032 Check for regressions in test object build.
2033 @return None
2034 """
2035 return_code, stdout, stderr = run_ns3("configure")
2036 self.assertEqual(return_code, 0)
2037
2038 test_module_cache = os.path.join(ns3_path, "cmake-cache", "src", "test")
2039 self.assertFalse(os.path.exists(test_module_cache))
2040
2041 return_code, stdout, stderr = run_ns3("configure --enable-tests")
2042 self.assertEqual(return_code, 0)
2043 self.assertTrue(os.path.exists(test_module_cache))
2044
2046 """!
2047 Check for regressions in a bare ns-3 configuration.
2048 @return None
2049 """
2050
2051 run_ns3("clean")
2052
2053 with DockerContainerManager(self, "ubuntu:22.04") as container:
2054 container.execute("apt-get update")
2055 container.execute("apt-get install -y python3 cmake g++")
2056 return_code = 0
2057 stdout = ""
2058 try:
2059 stdout = container.execute("./ns3 configure -d release")
2060 except DockerException as e:
2061 return_code = 1
2062 self.config_ok(return_code, stdout, stdout)
2063
2064 run_ns3("clean")
2065
2066
2068 """!
2069 Tests ns3 regarding building the project
2070 """
2071
2072 def setUp(self):
2073 """!
2074 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
2075 @return None
2076 """
2077 super().setUp()
2078
2080
2082 """!
2083 Try building the core library
2084 @return None
2085 """
2086 return_code, stdout, stderr = run_ns3("build core")
2087 self.assertEqual(return_code, 0)
2088 self.assertIn("Built target core", stdout)
2089
2091 """!
2092 Try building core-test library without tests enabled
2093 @return None
2094 """
2095 # tests are not enabled, so the target isn't available
2096 return_code, stdout, stderr = run_ns3("build core-test")
2097 self.assertEqual(return_code, 1)
2098 self.assertIn("Target to build does not exist: core-test", stdout)
2099
2101 """!
2102 Try building the project:
2103 @return None
2104 """
2105 return_code, stdout, stderr = run_ns3("build")
2106 self.assertEqual(return_code, 0)
2107 self.assertIn("Built target", stdout)
2108 for program in get_programs_list():
2109 self.assertTrue(os.path.exists(program), program)
2110 self.assertIn(cmake_build_project_command, stdout)
2111
2113 """!
2114 Try hiding task lines
2115 @return None
2116 """
2117 return_code, stdout, stderr = run_ns3("--quiet build")
2118 self.assertEqual(return_code, 0)
2119 self.assertIn(cmake_build_project_command, stdout)
2120
2122 """!
2123 Try removing an essential file to break the build
2124 @return None
2125 """
2126 # change an essential file to break the build.
2127 attribute_cc_path = os.sep.join([ns3_path, "src", "core", "model", "attribute.cc"])
2128 attribute_cc_bak_path = attribute_cc_path + ".bak"
2129 shutil.move(attribute_cc_path, attribute_cc_bak_path)
2130
2131 # build should break.
2132 return_code, stdout, stderr = run_ns3("build")
2133 self.assertNotEqual(return_code, 0)
2134
2135 # move file back.
2136 shutil.move(attribute_cc_bak_path, attribute_cc_path)
2137
2138 # Build should work again.
2139 return_code, stdout, stderr = run_ns3("build")
2140 self.assertEqual(return_code, 0)
2141
2143 """!
2144 Test if changing the version file affects the library names
2145 @return None
2146 """
2147 run_ns3("build")
2149
2150 version_file = os.sep.join([ns3_path, "VERSION"])
2151 with open(version_file, "w", encoding="utf-8") as f:
2152 f.write("3-00\n")
2153
2154 # Reconfigure.
2155 return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
2156 self.config_ok(return_code, stdout, stderr)
2157
2158 # Build.
2159 return_code, stdout, stderr = run_ns3("build")
2160 self.assertEqual(return_code, 0)
2161 self.assertIn("Built target", stdout)
2162
2163 # Programs with new versions.
2164 new_programs = get_programs_list()
2165
2166 # Check if they exist.
2167 for program in new_programs:
2168 self.assertTrue(os.path.exists(program))
2169
2170 # Check if we still have the same number of binaries.
2171 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
2172
2173 # Check if versions changed from 3-dev to 3-00.
2174 libraries = get_libraries_list()
2175 new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
2176 self.assertEqual(len(new_libraries), len(self.ns3_libraries))
2177 for library in new_libraries:
2178 self.assertNotIn("libns3-dev", library)
2179 self.assertIn("libns3-00", library)
2180 self.assertTrue(os.path.exists(library))
2181
2182 # Restore version file.
2183 with open(version_file, "w", encoding="utf-8") as f:
2184 f.write("3-dev\n")
2185
2187 """!
2188 Try setting a different output directory and if everything is
2189 in the right place and still working correctly
2190 @return None
2191 """
2192
2193 # Re-build to return to the original state.
2194 return_code, stdout, stderr = run_ns3("build")
2195 self.assertEqual(return_code, 0)
2196
2197 ## ns3_libraries holds a list of built module libraries # noqa
2199
2200 ## ns3_executables holds a list of executables in .lock-ns3 # noqa
2202
2203 # Delete built programs and libraries to check if they were restored later.
2204 for program in self.ns3_executablesns3_executables:
2205 os.remove(program)
2206 for library in self.ns3_libraries:
2207 os.remove(library)
2208
2209 # Reconfigure setting the output folder to ns-3-dev/build/release (both as an absolute path or relative).
2210 absolute_path = os.sep.join([ns3_path, "build", "release"])
2211 relative_path = os.sep.join(["build", "release"])
2212 for different_out_dir in [absolute_path, relative_path]:
2213 return_code, stdout, stderr = run_ns3(
2214 'configure -G "{generator}" --out=%s' % different_out_dir
2215 )
2216 self.config_ok(return_code, stdout, stderr)
2217 self.assertIn(
2218 "Build directory : %s" % absolute_path.replace(os.sep, "/"), stdout
2219 )
2220
2221 # Build
2222 run_ns3("build")
2223
2224 # Check if we have the same number of binaries and that they were built correctly.
2225 new_programs = get_programs_list()
2226 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
2227 for program in new_programs:
2228 self.assertTrue(os.path.exists(program))
2229
2230 # Check if we have the same number of libraries and that they were built correctly.
2231 libraries = get_libraries_list(os.sep.join([absolute_path, "lib"]))
2232 new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
2233 self.assertEqual(len(new_libraries), len(self.ns3_libraries))
2234 for library in new_libraries:
2235 self.assertTrue(os.path.exists(library))
2236
2237 # Remove files in the different output dir.
2238 shutil.rmtree(absolute_path)
2239
2240 # Restore original output directory.
2241 return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --out=''")
2242 self.config_ok(return_code, stdout, stderr)
2243 self.assertIn(
2244 "Build directory : %s" % usual_outdir.replace(os.sep, "/"), stdout
2245 )
2246
2247 # Try re-building.
2248 run_ns3("build")
2249
2250 # Check if we have the same binaries we had at the beginning.
2251 new_programs = get_programs_list()
2252 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
2253 for program in new_programs:
2254 self.assertTrue(os.path.exists(program))
2255
2256 # Check if we have the same libraries we had at the beginning.
2257 libraries = get_libraries_list()
2258 self.assertEqual(len(libraries), len(self.ns3_libraries))
2259 for library in libraries:
2260 self.assertTrue(os.path.exists(library))
2261
2263 """!
2264 Tries setting a ns3 version, then installing it.
2265 After that, tries searching for ns-3 with CMake's find_package(ns3).
2266 Finally, tries using core library in a 3rd-party project
2267 @return None
2268 """
2269 # Remove existing libraries from the previous step.
2270 libraries = get_libraries_list()
2271 for library in libraries:
2272 os.remove(library)
2273
2274 # 3-dev version format is not supported by CMake, so we use 3.01.
2275 version_file = os.sep.join([ns3_path, "VERSION"])
2276 with open(version_file, "w", encoding="utf-8") as f:
2277 f.write("3-01\n")
2278
2279 # Reconfigure setting the installation folder to ns-3-dev/build/install.
2280 install_prefix = os.sep.join([ns3_path, "build", "install"])
2281 return_code, stdout, stderr = run_ns3(
2282 'configure -G "{generator}" --prefix=%s' % install_prefix
2283 )
2284 self.config_ok(return_code, stdout, stderr)
2285
2286 # Build.
2287 run_ns3("build")
2288 libraries = get_libraries_list()
2289 headers = get_headers_list()
2290
2291 # Install.
2292 run_ns3("install")
2293
2294 # Find out if libraries were installed to lib or lib64 (Fedora thing).
2295 lib64 = os.path.exists(os.sep.join([install_prefix, "lib64"]))
2296 installed_libdir = os.sep.join([install_prefix, ("lib64" if lib64 else "lib")])
2297
2298 # Make sure all libraries were installed.
2299 installed_libraries = get_libraries_list(installed_libdir)
2300 installed_libraries_list = ";".join(installed_libraries)
2301 for library in libraries:
2302 library_name = os.path.basename(library)
2303 self.assertIn(library_name, installed_libraries_list)
2304
2305 # Make sure all headers were installed.
2306 installed_headers = get_headers_list(install_prefix)
2307 missing_headers = list(
2308 set([os.path.basename(x) for x in headers])
2309 - (set([os.path.basename(x) for x in installed_headers]))
2310 )
2311 self.assertEqual(len(missing_headers), 0)
2312
2313 # Now create a test CMake project and try to find_package ns-3.
2314 test_main_file = os.sep.join([install_prefix, "main.cpp"])
2315 with open(test_main_file, "w", encoding="utf-8") as f:
2316 f.write(
2317 """
2318 #include <ns3/core-module.h>
2319 using namespace ns3;
2320 int main ()
2321 {
2322 Simulator::Stop (Seconds (1.0));
2323 Simulator::Run ();
2324 Simulator::Destroy ();
2325 return 0;
2326 }
2327 """
2328 )
2329
2330 # We try to use this library without specifying a version,
2331 # specifying ns3-01 (text version with 'dev' is not supported)
2332 # and specifying ns3-00 (a wrong version)
2333 for version in ["", "3.01", "3.00"]:
2334 ns3_import_methods = []
2335
2336 # Import ns-3 libraries with as a CMake package
2337 cmake_find_package_import = """
2338 list(APPEND CMAKE_PREFIX_PATH ./{lib}/cmake/ns3)
2339 find_package(ns3 {version} COMPONENTS core)
2340 target_link_libraries(test PRIVATE ns3::core)
2341 """.format(
2342 lib=("lib64" if lib64 else "lib"), version=version
2343 )
2344 ns3_import_methods.append(cmake_find_package_import)
2345
2346 # Import ns-3 as pkg-config libraries
2347 pkgconfig_import = """
2348 list(APPEND CMAKE_PREFIX_PATH ./)
2349 include(FindPkgConfig)
2350 pkg_check_modules(ns3 REQUIRED IMPORTED_TARGET ns3-core{version})
2351 target_link_libraries(test PUBLIC PkgConfig::ns3)
2352 """.format(
2353 lib=("lib64" if lib64 else "lib"), version="=" + version if version else ""
2354 )
2355 if shutil.which("pkg-config"):
2356 ns3_import_methods.append(pkgconfig_import)
2357
2358 # Test the multiple ways of importing ns-3 libraries
2359 for import_method in ns3_import_methods:
2360 test_cmake_project = (
2361 """
2362 cmake_minimum_required(VERSION 3.12..3.12)
2363 project(ns3_consumer CXX)
2364 set(CMAKE_CXX_STANDARD 20)
2365 set(CMAKE_CXX_STANDARD_REQUIRED ON)
2366 add_executable(test main.cpp)
2367 """
2368 + import_method
2369 )
2370
2371 test_cmake_project_file = os.sep.join([install_prefix, "CMakeLists.txt"])
2372 with open(test_cmake_project_file, "w", encoding="utf-8") as f:
2373 f.write(test_cmake_project)
2374
2375 # Configure the test project
2376 cmake = shutil.which("cmake")
2377 return_code, stdout, stderr = run_program(
2378 cmake,
2379 '-DCMAKE_BUILD_TYPE=debug -G"{generator}" .'.format(
2380 generator=platform_makefiles
2381 ),
2382 cwd=install_prefix,
2383 )
2384
2385 if version == "3.00":
2386 self.assertEqual(return_code, 1)
2387 if import_method == cmake_find_package_import:
2388 self.assertIn(
2389 'Could not find a configuration file for package "ns3" that is compatible',
2390 stderr.replace("\n", ""),
2391 )
2392 elif import_method == pkgconfig_import:
2393 self.assertIn("not found", stderr.replace("\n", ""))
2394 else:
2395 raise Exception("Unknown import type")
2396 else:
2397 self.assertEqual(return_code, 0)
2398 self.assertIn("Build files", stdout)
2399
2400 # Build the test project making use of import ns-3
2401 return_code, stdout, stderr = run_program("cmake", "--build .", cwd=install_prefix)
2402
2403 if version == "3.00":
2404 self.assertEqual(return_code, 2, msg=stdout + stderr)
2405 self.assertGreater(len(stderr), 0)
2406 else:
2407 self.assertEqual(return_code, 0)
2408 self.assertIn("Built target", stdout)
2409
2410 # Try running the test program that imports ns-3
2411 if win32:
2412 test_program = os.path.join(install_prefix, "test.exe")
2413 env_sep = ";" if ";" in os.environ["PATH"] else ":"
2414 env = {
2415 "PATH": env_sep.join(
2416 [os.environ["PATH"], os.path.join(install_prefix, "lib")]
2417 )
2418 }
2419 else:
2420 test_program = "./test"
2421 env = None
2422 return_code, stdout, stderr = run_program(
2423 test_program, "", cwd=install_prefix, env=env
2424 )
2425 self.assertEqual(return_code, 0)
2426
2427 # Uninstall
2428 return_code, stdout, stderr = run_ns3("uninstall")
2429 self.assertIn("Built target uninstall", stdout)
2430
2431 # Restore 3-dev version file
2432 os.remove(version_file)
2433 with open(version_file, "w", encoding="utf-8") as f:
2434 f.write("3-dev\n")
2435
2437 """!
2438 Tries to build scratch-simulator and subdir/scratch-simulator-subdir
2439 @return None
2440 """
2441 # Build.
2442 targets = {
2443 "scratch/scratch-simulator": "scratch-simulator",
2444 "scratch/scratch-simulator.cc": "scratch-simulator",
2445 "scratch-simulator": "scratch-simulator",
2446 "scratch/subdir/scratch-subdir": "subdir_scratch-subdir",
2447 "subdir/scratch-subdir": "subdir_scratch-subdir",
2448 "scratch-subdir": "subdir_scratch-subdir",
2449 }
2450 for target_to_run, target_cmake in targets.items():
2451 # Test if build is working.
2452 build_line = "target scratch_%s" % target_cmake
2453 return_code, stdout, stderr = run_ns3("build %s" % target_to_run)
2454 self.assertEqual(return_code, 0)
2455 self.assertIn(build_line, stdout)
2456
2457 # Test if run is working
2458 return_code, stdout, stderr = run_ns3("run %s --verbose" % target_to_run)
2459 self.assertEqual(return_code, 0)
2460 self.assertIn(build_line, stdout)
2461 stdout = stdout.replace("scratch_%s" % target_cmake, "") # remove build lines
2462 self.assertIn(target_to_run.split("/")[-1].replace(".cc", ""), stdout)
2463
2465 """!
2466 Test if ns3 can alert correctly in case a shortcut collision happens
2467 @return None
2468 """
2469
2470 # First enable examples
2471 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
2472 self.assertEqual(return_code, 0)
2473
2474 # Copy second.cc from the tutorial examples to the scratch folder
2475 shutil.copy("./examples/tutorial/second.cc", "./scratch/second.cc")
2476
2477 # Reconfigure to re-scan the scratches
2478 return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
2479 self.assertEqual(return_code, 0)
2480
2481 # Try to run second and collide
2482 return_code, stdout, stderr = run_ns3("build second")
2483 self.assertEqual(return_code, 1)
2484 self.assertIn(
2485 'Build target "second" is ambiguous. Try one of these: "scratch/second", "examples/tutorial/second"',
2486 stdout.replace(os.sep, "/"),
2487 )
2488
2489 # Try to run scratch/second and succeed
2490 return_code, stdout, stderr = run_ns3("build scratch/second")
2491 self.assertEqual(return_code, 0)
2492 self.assertIn(cmake_build_target_command(target="scratch_second"), stdout)
2493
2494 # Try to run scratch/second and succeed
2495 return_code, stdout, stderr = run_ns3("build tutorial/second")
2496 self.assertEqual(return_code, 0)
2497 self.assertIn(cmake_build_target_command(target="second"), stdout)
2498
2499 # Try to run second and collide
2500 return_code, stdout, stderr = run_ns3("run second")
2501 self.assertEqual(return_code, 1)
2502 self.assertIn(
2503 'Run target "second" is ambiguous. Try one of these: "scratch/second", "examples/tutorial/second"',
2504 stdout.replace(os.sep, "/"),
2505 )
2506
2507 # Try to run scratch/second and succeed
2508 return_code, stdout, stderr = run_ns3("run scratch/second")
2509 self.assertEqual(return_code, 0)
2510
2511 # Try to run scratch/second and succeed
2512 return_code, stdout, stderr = run_ns3("run tutorial/second")
2513 self.assertEqual(return_code, 0)
2514
2515 # Remove second
2516 os.remove("./scratch/second.cc")
2517
2519 """!
2520 Test if we can build a static ns-3 library and link it to static programs
2521 @return None
2522 """
2523 if (not win32) and (arch == "aarch64"):
2524 if platform.libc_ver()[0] == "glibc":
2525 from packaging.version import Version
2526
2527 if Version(platform.libc_ver()[1]) < Version("2.37"):
2528 self.skipTest(
2529 "Static linking on ARM64 requires glibc 2.37 where fPIC was enabled (fpic is limited in number of GOT entries)"
2530 )
2531
2532 # First enable examples and static build
2533 return_code, stdout, stderr = run_ns3(
2534 'configure -G "{generator}" --enable-examples --disable-gtk --enable-static'
2535 )
2536
2537 if win32:
2538 # Configuration should fail explaining Windows
2539 # socket libraries cannot be statically linked
2540 self.assertEqual(return_code, 1)
2541 self.assertIn("Static builds are unsupported on Windows", stderr)
2542 else:
2543 # If configuration passes, we are half way done
2544 self.assertEqual(return_code, 0)
2545
2546 # Then try to build one example
2547 return_code, stdout, stderr = run_ns3("build sample-simulator")
2548 self.assertEqual(return_code, 0)
2549 self.assertIn("Built target", stdout)
2550
2551 # Maybe check the built binary for shared library references? Using objdump, otool, etc
2552
2554 """!
2555 Test if we can use python bindings
2556 @return None
2557 """
2558 try:
2559 import cppyy
2560 except ModuleNotFoundError:
2561 self.skipTest("Cppyy was not found")
2562
2563 # First enable examples and static build
2564 return_code, stdout, stderr = run_ns3(
2565 'configure -G "{generator}" --enable-examples --enable-python-bindings'
2566 )
2567
2568 # If configuration passes, we are half way done
2569 self.assertEqual(return_code, 0)
2570
2571 # Then build and run tests
2572 return_code, stdout, stderr = run_program("test.py", "", python=True)
2573 self.assertEqual(return_code, 0)
2574
2575 # Then try to run a specific test
2576 return_code, stdout, stderr = run_program("test.py", "-p mixed-wired-wireless", python=True)
2577 self.assertEqual(return_code, 0)
2578
2579 # Then try to run a specific test with the full relative path
2580 return_code, stdout, stderr = run_program(
2581 "test.py", "-p ./examples/wireless/mixed-wired-wireless", python=True
2582 )
2583 self.assertEqual(return_code, 0)
2584
2586 """!
2587 Test if we had regressions with brite, click and openflow modules
2588 that depend on homonymous libraries
2589 @return None
2590 """
2591 if shutil.which("git") is None:
2592 self.skipTest("Missing git")
2593
2594 if win32:
2595 self.skipTest("Optional components are not supported on Windows")
2596
2597 # First enable automatic components fetching
2598 return_code, stdout, stderr = run_ns3("configure -- -DNS3_FETCH_OPTIONAL_COMPONENTS=ON")
2599 self.assertEqual(return_code, 0)
2600
2601 # Build the optional components to check if their dependencies were fetched
2602 # and there were no build regressions
2603 return_code, stdout, stderr = run_ns3("build brite click openflow")
2604 self.assertEqual(return_code, 0)
2605
2607 """!
2608 Test if we can link contrib modules to src modules
2609 @return None
2610 """
2611 if shutil.which("git") is None:
2612 self.skipTest("Missing git")
2613
2614 destination_contrib = os.path.join(ns3_path, "contrib/test-contrib-dependency")
2615 destination_src = os.path.join(ns3_path, "src/test-src-dependent-on-contrib")
2616 # Remove pre-existing directories
2617 if os.path.exists(destination_contrib):
2618 shutil.rmtree(destination_contrib)
2619 if os.path.exists(destination_src):
2620 shutil.rmtree(destination_src)
2621
2622 # Always use a fresh copy
2623 shutil.copytree(
2624 os.path.join(ns3_path, "build-support/test-files/test-contrib-dependency"),
2625 destination_contrib,
2626 )
2627 shutil.copytree(
2628 os.path.join(ns3_path, "build-support/test-files/test-src-dependent-on-contrib"),
2629 destination_src,
2630 )
2631
2632 # Then configure
2633 return_code, stdout, stderr = run_ns3("configure --enable-examples")
2634 self.assertEqual(return_code, 0)
2635
2636 # Build the src module that depend on a contrib module
2637 return_code, stdout, stderr = run_ns3("run source-example")
2638 self.assertEqual(return_code, 0)
2639
2640 # Remove module copies
2641 shutil.rmtree(destination_contrib)
2642 shutil.rmtree(destination_src)
2643
2644
2646 """!
2647 Tests ns3 usage in more realistic scenarios
2648 """
2649
2650 def setUp(self):
2651 """!
2652 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
2653 Here examples, tests and documentation are also enabled.
2654 @return None
2655 """
2656
2657 super().setUp()
2658
2659 # On top of the release build configured by NS3ConfigureTestCase, also enable examples, tests and docs.
2660 return_code, stdout, stderr = run_ns3(
2661 'configure -d release -G "{generator}" --enable-examples --enable-tests'
2662 )
2663 self.config_ok(return_code, stdout, stderr)
2664
2665 # Check if .lock-ns3 exists, then read to get list of executables.
2666 self.assertTrue(os.path.exists(ns3_lock_filename))
2667
2668 ## ns3_executables holds a list of executables in .lock-ns3 # noqa
2670
2671 # Check if .lock-ns3 exists than read to get the list of enabled modules.
2672 self.assertTrue(os.path.exists(ns3_lock_filename))
2673
2674 ## ns3_modules holds a list to the modules enabled stored in .lock-ns3 # noqa
2676
2678 """!
2679 Try to build the project
2680 @return None
2681 """
2682 return_code, stdout, stderr = run_ns3("build")
2683 self.assertEqual(return_code, 0)
2684 self.assertIn("Built target", stdout)
2685 for program in get_programs_list():
2686 self.assertTrue(os.path.exists(program))
2687 libraries = get_libraries_list()
2688 for module in get_enabled_modules():
2689 self.assertIn(module.replace("ns3-", ""), ";".join(libraries))
2690 self.assertIn(cmake_build_project_command, stdout)
2691
2693 """!
2694 Try to build and run test-runner
2695 @return None
2696 """
2697 return_code, stdout, stderr = run_ns3('run "test-runner --list" --verbose')
2698 self.assertEqual(return_code, 0)
2699 self.assertIn("Built target test-runner", stdout)
2700 self.assertIn(cmake_build_target_command(target="test-runner"), stdout)
2701
2703 """!
2704 Try to build and run a library
2705 @return None
2706 """
2707 return_code, stdout, stderr = run_ns3("run core") # this should not work
2708 self.assertEqual(return_code, 1)
2709 self.assertIn("Couldn't find the specified program: core", stderr)
2710
2712 """!
2713 Try to build and run an unknown target
2714 @return None
2715 """
2716 return_code, stdout, stderr = run_ns3("run nonsense") # this should not work
2717 self.assertEqual(return_code, 1)
2718 self.assertIn("Couldn't find the specified program: nonsense", stderr)
2719
2721 """!
2722 Try to run test-runner without building
2723 @return None
2724 """
2725 return_code, stdout, stderr = run_ns3("build test-runner")
2726 self.assertEqual(return_code, 0)
2727
2728 return_code, stdout, stderr = run_ns3('run "test-runner --list" --no-build --verbose')
2729 self.assertEqual(return_code, 0)
2730 self.assertNotIn("Built target test-runner", stdout)
2731 self.assertNotIn(cmake_build_target_command(target="test-runner"), stdout)
2732
2734 """!
2735 Test ns3 fails to run a library
2736 @return None
2737 """
2738 return_code, stdout, stderr = run_ns3("run core --no-build") # this should not work
2739 self.assertEqual(return_code, 1)
2740 self.assertIn("Couldn't find the specified program: core", stderr)
2741
2743 """!
2744 Test ns3 fails to run an unknown program
2745 @return None
2746 """
2747 return_code, stdout, stderr = run_ns3("run nonsense --no-build") # this should not work
2748 self.assertEqual(return_code, 1)
2749 self.assertIn("Couldn't find the specified program: nonsense", stderr)
2750
2752 """!
2753 Test if scratch simulator is executed through gdb and lldb
2754 @return None
2755 """
2756 if shutil.which("gdb") is None:
2757 self.skipTest("Missing gdb")
2758
2759 return_code, stdout, stderr = run_ns3("build scratch-simulator")
2760 self.assertEqual(return_code, 0)
2761
2762 return_code, stdout, stderr = run_ns3(
2763 "run scratch-simulator --gdb --verbose --no-build", env={"gdb_eval": "1"}
2764 )
2765 self.assertEqual(return_code, 0)
2766 self.assertIn("scratch-simulator", stdout)
2767 if win32:
2768 self.assertIn("GNU gdb", stdout)
2769 else:
2770 self.assertIn("No debugging symbols found", stdout)
2771
2773 """!
2774 Test if scratch simulator is executed through valgrind
2775 @return None
2776 """
2777 if shutil.which("valgrind") is None:
2778 self.skipTest("Missing valgrind")
2779
2780 return_code, stdout, stderr = run_ns3("build scratch-simulator")
2781 self.assertEqual(return_code, 0)
2782
2783 return_code, stdout, stderr = run_ns3(
2784 "run scratch-simulator --valgrind --verbose --no-build"
2785 )
2786 self.assertEqual(return_code, 0)
2787 self.assertIn("scratch-simulator", stderr)
2788 self.assertIn("Memcheck", stderr)
2789
2791 """!
2792 Test the doxygen target that does trigger a full build
2793 @return None
2794 """
2795 if shutil.which("doxygen") is None:
2796 self.skipTest("Missing doxygen")
2797
2798 if shutil.which("bash") is None:
2799 self.skipTest("Missing bash")
2800
2801 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
2802
2803 doxygen_files = ["introspected-command-line.h", "introspected-doxygen.h"]
2804 for filename in doxygen_files:
2805 file_path = os.sep.join([doc_folder, filename])
2806 if os.path.exists(file_path):
2807 os.remove(file_path)
2808
2809 # Rebuilding dot images is super slow, so not removing doxygen products
2810 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
2811 # if os.path.exists(doxygen_build_folder):
2812 # shutil.rmtree(doxygen_build_folder)
2813
2814 return_code, stdout, stderr = run_ns3("docs doxygen")
2815 self.assertEqual(return_code, 0)
2816 self.assertIn(cmake_build_target_command(target="doxygen"), stdout)
2817 self.assertIn("Built target doxygen", stdout)
2818
2820 """!
2821 Test the doxygen target that doesn't trigger a full build
2822 @return None
2823 """
2824 if shutil.which("doxygen") is None:
2825 self.skipTest("Missing doxygen")
2826
2827 # Rebuilding dot images is super slow, so not removing doxygen products
2828 # doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
2829 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
2830 # if os.path.exists(doxygen_build_folder):
2831 # shutil.rmtree(doxygen_build_folder)
2832
2833 return_code, stdout, stderr = run_ns3("docs doxygen-no-build")
2834 self.assertEqual(return_code, 0)
2835 self.assertIn(cmake_build_target_command(target="doxygen-no-build"), stdout)
2836 self.assertIn("Built target doxygen-no-build", stdout)
2837
2839 """!
2840 Test every individual target for Sphinx-based documentation
2841 @return None
2842 """
2843 if shutil.which("sphinx-build") is None:
2844 self.skipTest("Missing sphinx")
2845
2846 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
2847
2848 # For each sphinx doc target.
2849 for target in ["installation", "contributing", "manual", "models", "tutorial"]:
2850 # First we need to clean old docs, or it will not make any sense.
2851 doc_build_folder = os.sep.join([doc_folder, target, "build"])
2852 doc_temp_folder = os.sep.join([doc_folder, target, "source-temp"])
2853 if os.path.exists(doc_build_folder):
2854 shutil.rmtree(doc_build_folder)
2855 if os.path.exists(doc_temp_folder):
2856 shutil.rmtree(doc_temp_folder)
2857
2858 # Build
2859 return_code, stdout, stderr = run_ns3("docs %s" % target)
2860 self.assertEqual(return_code, 0, target)
2861 self.assertIn(cmake_build_target_command(target="sphinx_%s" % target), stdout)
2862 self.assertIn("Built target sphinx_%s" % target, stdout)
2863
2864 # Check if the docs output folder exists
2865 doc_build_folder = os.sep.join([doc_folder, target, "build"])
2866 self.assertTrue(os.path.exists(doc_build_folder))
2867
2868 # Check if the all the different types are in place (latex, split HTML and single page HTML)
2869 for build_type in ["latex", "html", "singlehtml"]:
2870 self.assertTrue(os.path.exists(os.sep.join([doc_build_folder, build_type])))
2871
2873 """!
2874 Test the documentation target that builds
2875 both doxygen and sphinx based documentation
2876 @return None
2877 """
2878 if shutil.which("doxygen") is None:
2879 self.skipTest("Missing doxygen")
2880 if shutil.which("sphinx-build") is None:
2881 self.skipTest("Missing sphinx")
2882
2883 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
2884
2885 # First we need to clean old docs, or it will not make any sense.
2886
2887 # Rebuilding dot images is super slow, so not removing doxygen products
2888 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
2889 # if os.path.exists(doxygen_build_folder):
2890 # shutil.rmtree(doxygen_build_folder)
2891
2892 for target in ["manual", "models", "tutorial"]:
2893 doc_build_folder = os.sep.join([doc_folder, target, "build"])
2894 if os.path.exists(doc_build_folder):
2895 shutil.rmtree(doc_build_folder)
2896
2897 return_code, stdout, stderr = run_ns3("docs all")
2898 self.assertEqual(return_code, 0)
2899 self.assertIn(cmake_build_target_command(target="sphinx"), stdout)
2900 self.assertIn("Built target sphinx", stdout)
2901 self.assertIn(cmake_build_target_command(target="doxygen"), stdout)
2902 self.assertIn("Built target doxygen", stdout)
2903
2905 """!
2906 Try to set ownership of scratch-simulator from current user to root,
2907 and change execution permissions
2908 @return None
2909 """
2910
2911 # Test will be skipped if not defined
2912 sudo_password = os.getenv("SUDO_PASSWORD", None)
2913
2914 # Skip test if variable containing sudo password is the default value
2915 if sudo_password is None:
2916 self.skipTest("SUDO_PASSWORD environment variable was not specified")
2917
2918 enable_sudo = read_lock_entry("ENABLE_SUDO")
2919 self.assertFalse(enable_sudo is True)
2920
2921 # First we run to ensure the program was built
2922 return_code, stdout, stderr = run_ns3("run scratch-simulator")
2923 self.assertEqual(return_code, 0)
2924 self.assertIn("Built target scratch_scratch-simulator", stdout)
2925 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
2926 scratch_simulator_path = list(
2927 filter(lambda x: x if "scratch-simulator" in x else None, self.ns3_executablesns3_executables)
2928 )[-1]
2929 prev_fstat = os.stat(scratch_simulator_path) # we get the permissions before enabling sudo
2930
2931 # Now try setting the sudo bits from the run subparser
2932 return_code, stdout, stderr = run_ns3(
2933 "run scratch-simulator --enable-sudo", env={"SUDO_PASSWORD": sudo_password}
2934 )
2935 self.assertEqual(return_code, 0)
2936 self.assertIn("Built target scratch_scratch-simulator", stdout)
2937 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
2938 fstat = os.stat(scratch_simulator_path)
2939
2940 import stat
2941
2942 # If we are on Windows, these permissions mean absolutely nothing,
2943 # and on Fuse builds they might not make any sense, so we need to skip before failing
2944 likely_fuse_mount = (
2945 (prev_fstat.st_mode & stat.S_ISUID) == (fstat.st_mode & stat.S_ISUID)
2946 ) and prev_fstat.st_uid == 0 # noqa
2947
2948 if win32 or likely_fuse_mount:
2949 self.skipTest("Windows or likely a FUSE mount")
2950
2951 # If this is a valid platform, we can continue
2952 self.assertEqual(fstat.st_uid, 0) # check the file was correctly chown'ed by root
2953 self.assertEqual(
2954 fstat.st_mode & stat.S_ISUID, stat.S_ISUID
2955 ) # check if normal users can run as sudo
2956
2957 # Now try setting the sudo bits as a post-build step (as set by configure subparser)
2958 return_code, stdout, stderr = run_ns3("configure --enable-sudo")
2959 self.assertEqual(return_code, 0)
2960
2961 # Check if it was properly set in the lock file
2962 enable_sudo = read_lock_entry("ENABLE_SUDO")
2963 self.assertTrue(enable_sudo)
2964
2965 # Remove old executables
2966 for executable in self.ns3_executablesns3_executables:
2967 if os.path.exists(executable):
2968 os.remove(executable)
2969
2970 # Try to build and then set sudo bits as a post-build step
2971 return_code, stdout, stderr = run_ns3("build", env={"SUDO_PASSWORD": sudo_password})
2972 self.assertEqual(return_code, 0)
2973
2974 # Check if commands are being printed for every target
2975 self.assertIn("chown root", stdout)
2976 self.assertIn("chmod u+s", stdout)
2977 for executable in self.ns3_executablesns3_executables:
2978 self.assertIn(os.path.basename(executable), stdout)
2979
2980 # Check scratch simulator yet again
2981 fstat = os.stat(scratch_simulator_path)
2982 self.assertEqual(fstat.st_uid, 0) # check the file was correctly chown'ed by root
2983 self.assertEqual(
2984 fstat.st_mode & stat.S_ISUID, stat.S_ISUID
2985 ) # check if normal users can run as sudo
2986
2988 """!
2989 Check if command template is working
2990 @return None
2991 """
2992
2993 # Command templates that are empty or do not have a '%s' should fail
2994 return_code0, stdout0, stderr0 = run_ns3("run sample-simulator --command-template")
2995 self.assertEqual(return_code0, 2)
2996 self.assertIn("argument --command-template: expected one argument", stderr0)
2997
2998 return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template=" "')
2999 return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --command-template " "')
3000 return_code3, stdout3, stderr3 = run_ns3('run sample-simulator --command-template "echo "')
3001 self.assertEqual((return_code1, return_code2, return_code3), (1, 1, 1))
3002 for stderr in [stderr1, stderr2, stderr3]:
3003 self.assertIn("not all arguments converted during string formatting", stderr)
3004
3005 # Command templates with %s should at least continue and try to run the target
3006 return_code4, stdout4, _ = run_ns3(
3007 'run sample-simulator --command-template "%s --PrintVersion" --verbose'
3008 )
3009 return_code5, stdout5, _ = run_ns3(
3010 'run sample-simulator --command-template="%s --PrintVersion" --verbose'
3011 )
3012 self.assertEqual((return_code4, return_code5), (0, 0))
3013
3014 self.assertIn("sample-simulator{ext} --PrintVersion".format(ext=ext), stdout4)
3015 self.assertIn("sample-simulator{ext} --PrintVersion".format(ext=ext), stdout5)
3016
3018 """!
3019 Check if all flavors of different argument passing to
3020 executable targets are working
3021 @return None
3022 """
3023
3024 # Test if all argument passing flavors are working
3025 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --verbose')
3026 return_code1, stdout1, stderr1 = run_ns3(
3027 'run sample-simulator --command-template="%s --help" --verbose'
3028 )
3029 return_code2, stdout2, stderr2 = run_ns3("run sample-simulator --verbose -- --help")
3030
3031 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
3032 self.assertIn("sample-simulator{ext} --help".format(ext=ext), stdout0)
3033 self.assertIn("sample-simulator{ext} --help".format(ext=ext), stdout1)
3034 self.assertIn("sample-simulator{ext} --help".format(ext=ext), stdout2)
3035
3036 # Test if the same thing happens with an additional run argument (e.g. --no-build)
3037 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --no-build')
3038 return_code1, stdout1, stderr1 = run_ns3(
3039 'run sample-simulator --command-template="%s --help" --no-build'
3040 )
3041 return_code2, stdout2, stderr2 = run_ns3("run sample-simulator --no-build -- --help")
3042 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
3043 self.assertEqual(stdout0, stdout1)
3044 self.assertEqual(stdout1, stdout2)
3045 self.assertEqual(stderr0, stderr1)
3046 self.assertEqual(stderr1, stderr2)
3047
3048 # Now collect results for each argument individually
3049 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --PrintGlobals" --verbose')
3050 return_code1, stdout1, stderr1 = run_ns3('run "sample-simulator --PrintGroups" --verbose')
3051 return_code2, stdout2, stderr2 = run_ns3('run "sample-simulator --PrintTypeIds" --verbose')
3052
3053 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
3054 self.assertIn("sample-simulator{ext} --PrintGlobals".format(ext=ext), stdout0)
3055 self.assertIn("sample-simulator{ext} --PrintGroups".format(ext=ext), stdout1)
3056 self.assertIn("sample-simulator{ext} --PrintTypeIds".format(ext=ext), stdout2)
3057
3058 # Then check if all the arguments are correctly merged by checking the outputs
3059 cmd = 'run "sample-simulator --PrintGlobals" --command-template="%s --PrintGroups" --verbose -- --PrintTypeIds'
3060 return_code, stdout, stderr = run_ns3(cmd)
3061 self.assertEqual(return_code, 0)
3062
3063 # The order of the arguments is command template,
3064 # arguments passed with the target itself
3065 # and forwarded arguments after the -- separator
3066 self.assertIn(
3067 "sample-simulator{ext} --PrintGroups --PrintGlobals --PrintTypeIds".format(ext=ext),
3068 stdout,
3069 )
3070
3071 # Check if it complains about the missing -- separator
3072 cmd0 = 'run sample-simulator --command-template="%s " --PrintTypeIds'
3073 cmd1 = "run sample-simulator --PrintTypeIds"
3074
3075 return_code0, stdout0, stderr0 = run_ns3(cmd0)
3076 return_code1, stdout1, stderr1 = run_ns3(cmd1)
3077 self.assertEqual((return_code0, return_code1), (1, 1))
3078 self.assertIn("To forward configuration or runtime options, put them after '--'", stderr0)
3079 self.assertIn("To forward configuration or runtime options, put them after '--'", stderr1)
3080
3082 """!
3083 Test if scratch simulator is executed through lldb
3084 @return None
3085 """
3086 if shutil.which("lldb") is None:
3087 self.skipTest("Missing lldb")
3088
3089 return_code, stdout, stderr = run_ns3("build scratch-simulator")
3090 self.assertEqual(return_code, 0)
3091
3092 return_code, stdout, stderr = run_ns3("run scratch-simulator --lldb --verbose --no-build")
3093 self.assertEqual(return_code, 0)
3094 self.assertIn("scratch-simulator", stdout)
3095 self.assertIn("(lldb) target create", stdout)
3096
3098 """!
3099 Test if CPM and Vcpkg package managers are working properly
3100 @return None
3101 """
3102 # Clean the ns-3 configuration
3103 return_code, stdout, stderr = run_ns3("clean")
3104 self.assertEqual(return_code, 0)
3105
3106 # Cleanup VcPkg leftovers
3107 if os.path.exists("vcpkg"):
3108 shutil.rmtree("vcpkg")
3109
3110 # Copy a test module that consumes armadillo
3111 destination_src = os.path.join(ns3_path, "src/test-package-managers")
3112 # Remove pre-existing directories
3113 if os.path.exists(destination_src):
3114 shutil.rmtree(destination_src)
3115
3116 # Always use a fresh copy
3117 shutil.copytree(
3118 os.path.join(ns3_path, "build-support/test-files/test-package-managers"),
3119 destination_src,
3120 )
3121
3122 with DockerContainerManager(self, "ubuntu:22.04") as container:
3123 # Install toolchain
3124 container.execute("apt-get update")
3125 container.execute("apt-get install -y python3 cmake g++ ninja-build")
3126
3127 # Verify that Armadillo is not available and that we did not
3128 # add any new unnecessary dependency when features are not used
3129 try:
3130 container.execute("./ns3 configure -- -DTEST_PACKAGE_MANAGER:STRING=ON")
3131 self.skipTest("Armadillo is already installed")
3132 except DockerException as e:
3133 pass
3134
3135 # Clean cache to prevent dumb errors
3136 return_code, stdout, stderr = run_ns3("clean")
3137 self.assertEqual(return_code, 0)
3138
3139 # Install CPM and VcPkg shared dependency
3140 container.execute("apt-get install -y git")
3141
3142 # Install Armadillo with CPM
3143 try:
3144 container.execute(
3145 "./ns3 configure -- -DNS3_CPM=ON -DTEST_PACKAGE_MANAGER:STRING=CPM"
3146 )
3147 except DockerException as e:
3148 self.fail()
3149
3150 # Try to build module using CPM's Armadillo
3151 try:
3152 container.execute("./ns3 build test-package-managers")
3153 except DockerException as e:
3154 self.fail()
3155
3156 # Clean cache to prevent dumb errors
3157 return_code, stdout, stderr = run_ns3("clean")
3158 self.assertEqual(return_code, 0)
3159
3160 if arch != "aarch64":
3161 # Install VcPkg dependencies
3162 container.execute("apt-get install -y zip unzip tar curl")
3163
3164 # Install Armadillo dependencies
3165 container.execute("apt-get install -y pkg-config gfortran")
3166
3167 # Install VcPkg
3168 try:
3169 container.execute("./ns3 configure -- -DNS3_VCPKG=ON")
3170 except DockerException as e:
3171 self.fail()
3172
3173 # Install Armadillo with VcPkg
3174 try:
3175 container.execute("./ns3 configure -- -DTEST_PACKAGE_MANAGER:STRING=VCPKG")
3176 except DockerException as e:
3177 self.fail()
3178
3179 # Try to build module using VcPkg's Armadillo
3180 try:
3181 container.execute("./ns3 build test-package-managers")
3182 except DockerException as e:
3183 self.fail()
3184
3185 # Remove test module
3186 if os.path.exists(destination_src):
3187 shutil.rmtree(destination_src)
3188
3189
3190class NS3QualityControlTestCase(unittest.TestCase):
3191 """!
3192 ns-3 tests to control the quality of the repository over time,
3193 by checking the state of URLs listed and more
3194 """
3195
3197 """!
3198 Test if all urls in source files are alive
3199 @return None
3200 """
3201
3202 # Skip this test if Django is not available
3203 try:
3204 import django
3205 except ImportError:
3206 django = None # noqa
3207 self.skipTest("Django URL validators are not available")
3208
3209 # Skip this test if requests library is not available
3210 try:
3211 import requests
3212 import urllib3
3213
3214 urllib3.disable_warnings()
3215 except ImportError:
3216 requests = None # noqa
3217 self.skipTest("Requests library is not available")
3218
3219 regex = re.compile(r"((http|https)://[^\ \n\‍)\"\'\}><\]\;\`\\]*)") # noqa
3220 skipped_files = []
3221
3222 whitelisted_urls = {
3223 "https://gitlab.com/your-user-name/ns-3-dev",
3224 "https://www.nsnam.org/release/ns-allinone-3.31.rc1.tar.bz2",
3225 "https://www.nsnam.org/release/ns-allinone-3.X.rcX.tar.bz2",
3226 "https://www.nsnam.org/releases/ns-3-x",
3227 "https://www.nsnam.org/releases/ns-allinone-3.(x-1",
3228 "https://www.nsnam.org/releases/ns-allinone-3.x.tar.bz2",
3229 "https://ns-buildmaster.ee.washington.edu:8010/",
3230 # split due to command-line formatting
3231 "https://cmake.org/cmake/help/latest/manual/cmake-",
3232 "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_",
3233 # Dia placeholder xmlns address
3234 "http://www.lysator.liu.se/~alla/dia/",
3235 # Fails due to bad regex
3236 "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_Digital_Terrestrial_Television_Broadcasting_(DTTB",
3237 "http://en.wikipedia.org/wiki/Namespace_(computer_science",
3238 "http://en.wikipedia.org/wiki/Bonobo_(component_model",
3239 "http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85",
3240 # historical links
3241 "http://www.research.att.com/info/kpv/",
3242 "http://www.research.att.com/~gsf/",
3243 "http://nsnam.isi.edu/nsnam/index.php/Contributed_Code",
3244 "http://scan5.coverity.com/cgi-bin/upload.py",
3245 # terminal output
3246 "https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-x86_64.tar.gz-",
3247 "http://mirrors.kernel.org/fedora/releases/11/Everything/i386/os/Packages/",
3248 }
3249
3250 # Scan for all URLs in all files we can parse
3251 files_and_urls = set()
3252 unique_urls = set()
3253 for topdir in ["bindings", "doc", "examples", "src", "utils"]:
3254 for root, dirs, files in os.walk(topdir):
3255 # do not parse files in build directories
3256 if "build" in root or "_static" in root or "source-temp" in root or "html" in root:
3257 continue
3258 for file in files:
3259 filepath = os.path.join(root, file)
3260
3261 # skip everything that isn't a file
3262 if not os.path.isfile(filepath):
3263 continue
3264
3265 # skip svg files
3266 if file.endswith(".svg"):
3267 continue
3268
3269 try:
3270 with open(filepath, "r", encoding="utf-8") as f:
3271 matches = regex.findall(f.read())
3272
3273 # Get first group for each match (containing the URL)
3274 # and strip final punctuation or commas in matched links
3275 # commonly found in the docs
3276 urls = list(
3277 map(lambda x: x[0][:-1] if x[0][-1] in ".," else x[0], matches)
3278 )
3279 except UnicodeDecodeError:
3280 skipped_files.append(filepath)
3281 continue
3282
3283 # Search for new unique URLs and add keep track of their associated source file
3284 for url in set(urls) - unique_urls - whitelisted_urls:
3285 unique_urls.add(url)
3286 files_and_urls.add((filepath, url))
3287
3288 # Instantiate the Django URL validator
3289 from django.core.exceptions import ValidationError # noqa
3290 from django.core.validators import URLValidator # noqa
3291
3292 validate_url = URLValidator()
3293
3294 # User agent string to make ACM and Elsevier let us check if links to papers are working
3295 headers = {
3296 "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
3297 # noqa
3298 }
3299
3300 def test_file_url(args):
3301 test_filepath, test_url = args
3302 dead_link_msg = None
3303
3304 # Skip invalid URLs
3305 try:
3306 validate_url(test_url)
3307 except ValidationError:
3308 dead_link_msg = "%s: URL %s, invalid URL" % (test_filepath, test_url)
3309 except Exception as e:
3310 self.assertEqual(False, True, msg=e.__str__())
3311
3312 if dead_link_msg is not None:
3313 return dead_link_msg
3314 tries = 3
3315 # Check if valid URLs are alive
3316 while tries > 0:
3317 # Not verifying the certificate (verify=False) is potentially dangerous
3318 # HEAD checks are not as reliable as GET ones,
3319 # in some cases they may return bogus error codes and reasons
3320 try:
3321 response = requests.get(test_url, verify=False, headers=headers, timeout=50)
3322
3323 # In case of success and redirection
3324 if response.status_code in [200, 301]:
3325 dead_link_msg = None
3326 break
3327
3328 # People use the wrong code, but the reason
3329 # can still be correct
3330 if response.status_code in [302, 308, 500, 503]:
3331 if response.reason.lower() in [
3332 "found",
3333 "moved temporarily",
3334 "permanent redirect",
3335 "ok",
3336 "service temporarily unavailable",
3337 ]:
3338 dead_link_msg = None
3339 break
3340 # In case it didn't pass in any of the previous tests,
3341 # set dead_link_msg with the most recent error and try again
3342 dead_link_msg = "%s: URL %s: returned code %d" % (
3343 test_filepath,
3344 test_url,
3345 response.status_code,
3346 )
3347 except requests.exceptions.InvalidURL:
3348 dead_link_msg = "%s: URL %s: invalid URL" % (test_filepath, test_url)
3349 except requests.exceptions.SSLError:
3350 dead_link_msg = "%s: URL %s: SSL error" % (test_filepath, test_url)
3351 except requests.exceptions.TooManyRedirects:
3352 dead_link_msg = "%s: URL %s: too many redirects" % (test_filepath, test_url)
3353 except Exception as e:
3354 try:
3355 error_msg = e.args[0].reason.__str__()
3356 except AttributeError:
3357 error_msg = e.args[0]
3358 dead_link_msg = "%s: URL %s: failed with exception: %s" % (
3359 test_filepath,
3360 test_url,
3361 error_msg,
3362 )
3363 tries -= 1
3364 return dead_link_msg
3365
3366 # Dispatch threads to test multiple URLs concurrently
3367 from concurrent.futures import ThreadPoolExecutor
3368
3369 with ThreadPoolExecutor(max_workers=100) as executor:
3370 dead_links = list(executor.map(test_file_url, list(files_and_urls)))
3371
3372 # Filter out None entries
3373 dead_links = list(sorted(filter(lambda x: x is not None, dead_links)))
3374 self.assertEqual(len(dead_links), 0, msg="\n".join(["Dead links found:", *dead_links]))
3375
3377 """!
3378 Test if all tests can be executed without hitting major memory bugs
3379 @return None
3380 """
3381 return_code, stdout, stderr = run_ns3(
3382 "configure --enable-tests --enable-examples --enable-sanitizers -d optimized"
3383 )
3384 self.assertEqual(return_code, 0)
3385
3386 test_return_code, stdout, stderr = run_program("test.py", "", python=True)
3387 self.assertEqual(test_return_code, 0)
3388
3390 """!
3391 Check if images in the docs are above a brightness threshold.
3392 This should prevent screenshots with dark UI themes.
3393 @return None
3394 """
3395 if shutil.which("convert") is None:
3396 self.skipTest("Imagemagick was not found")
3397
3398 from pathlib import Path
3399
3400 # Scan for images
3401 image_extensions = ["png", "jpg"]
3402 images = []
3403 for extension in image_extensions:
3404 images += list(Path("./doc").glob("**/figures/*.{ext}".format(ext=extension)))
3405 images += list(Path("./doc").glob("**/figures/**/*.{ext}".format(ext=extension)))
3406
3407 # Get the brightness of an image on a scale of 0-100%
3408 imagemagick_get_image_brightness = 'convert {image} -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]" info:'
3409
3410 # We could invert colors of target image to increase its brightness
3411 # convert source.png -channel RGB -negate target.png
3412 brightness_threshold = 50
3413 for image in images:
3414 brightness = subprocess.check_output(
3415 imagemagick_get_image_brightness.format(image=image).split()
3416 )
3417 brightness = float(brightness.decode().strip("'\""))
3418 self.assertGreater(
3419 brightness,
3420 brightness_threshold,
3421 "Image darker than threshold (%d < %d): %s"
3422 % (brightness, brightness_threshold, image),
3423 )
3424
3426 """!
3427 Check if one of the log statements of examples/tests contains/exposes a bug.
3428 @return None
3429 """
3430 # First enable examples and tests with sanitizers
3431 return_code, stdout, stderr = run_ns3(
3432 'configure -G "{generator}" -d release --enable-examples --enable-tests --enable-sanitizers'
3433 )
3434 self.assertEqual(return_code, 0)
3435
3436 # Then build and run tests setting the environment variable
3437 return_code, stdout, stderr = run_program(
3438 "test.py", "", python=True, env={"TEST_LOGS": "1"}
3439 )
3440 self.assertEqual(return_code, 0)
3441
3442
3443def main():
3444 """!
3445 Main function
3446 @return None
3447 """
3448
3449 test_completeness = {
3450 "style": [
3451 NS3UnusedSourcesTestCase,
3452 NS3StyleTestCase,
3453 ],
3454 "build": [
3455 NS3CommonSettingsTestCase,
3456 NS3ConfigureBuildProfileTestCase,
3457 NS3ConfigureTestCase,
3458 NS3BuildBaseTestCase,
3459 NS3ExpectedUseTestCase,
3460 ],
3461 "complete": [
3462 NS3UnusedSourcesTestCase,
3463 NS3StyleTestCase,
3464 NS3CommonSettingsTestCase,
3465 NS3ConfigureBuildProfileTestCase,
3466 NS3ConfigureTestCase,
3467 NS3BuildBaseTestCase,
3468 NS3ExpectedUseTestCase,
3469 NS3QualityControlTestCase,
3470 ],
3471 "extras": [
3472 NS3DependenciesTestCase,
3473 ],
3474 }
3475
3476 import argparse
3477
3478 parser = argparse.ArgumentParser("Test suite for the ns-3 buildsystem")
3479 parser.add_argument(
3480 "-c", "--completeness", choices=test_completeness.keys(), default="complete"
3481 )
3482 parser.add_argument("-tn", "--test-name", action="store", default=None, type=str)
3483 parser.add_argument("-rtn", "--resume-from-test-name", action="store", default=None, type=str)
3484 parser.add_argument("-q", "--quiet", action="store_true", default=False)
3485 args = parser.parse_args(sys.argv[1:])
3486
3487 loader = unittest.TestLoader()
3488 suite = unittest.TestSuite()
3489
3490 # Put tests cases in order
3491 for testCase in test_completeness[args.completeness]:
3492 suite.addTests(loader.loadTestsFromTestCase(testCase))
3493
3494 # Filter tests by name
3495 if args.test_name:
3496 # Generate a dictionary of test names and their objects
3497 tests = dict(map(lambda x: (x._testMethodName, x), suite._tests))
3498
3499 tests_to_run = set(map(lambda x: x if args.test_name in x else None, tests.keys()))
3500 tests_to_remove = set(tests) - set(tests_to_run)
3501 for test_to_remove in tests_to_remove:
3502 suite._tests.remove(tests[test_to_remove])
3503
3504 # Filter tests after a specific name (e.g. to restart from a failing test)
3505 if args.resume_from_test_name:
3506 # Generate a dictionary of test names and their objects
3507 tests = dict(map(lambda x: (x._testMethodName, x), suite._tests))
3508 keys = list(tests.keys())
3509
3510 while args.resume_from_test_name not in keys[0] and len(tests) > 0:
3511 suite._tests.remove(tests[keys[0]])
3512 keys.pop(0)
3513
3514 # Before running, check if ns3rc exists and save it
3515 ns3rc_script_bak = ns3rc_script + ".bak"
3516 if os.path.exists(ns3rc_script) and not os.path.exists(ns3rc_script_bak):
3517 shutil.move(ns3rc_script, ns3rc_script_bak)
3518
3519 # Run tests and fail as fast as possible
3520 runner = unittest.TextTestRunner(failfast=True, verbosity=1 if args.quiet else 2)
3521 runner.run(suite)
3522
3523 # After completing the tests successfully, restore the ns3rc file
3524 if os.path.exists(ns3rc_script_bak):
3525 shutil.move(ns3rc_script_bak, ns3rc_script)
3526
3527
3528if __name__ == "__main__":
3529 main()
Python-on-whales wrapper for Docker-based ns-3 tests.
Definition test-ns3.py:187
__init__(self, unittest.TestCase currentTestCase, str containerName="ubuntu:latest")
Create and start container with containerName in the current ns-3 directory.
Definition test-ns3.py:192
__enter__(self)
Return the managed container when entiring the block "with DockerContainerManager() as container".
Definition test-ns3.py:234
__exit__(self, exc_type, exc_val, exc_tb)
Clean up the managed container at the end of the block "with DockerContainerManager() as container".
Definition test-ns3.py:242
container
The Python-on-whales container instance.
Definition test-ns3.py:219
Generic test case with basic function inherited by more complex tests.
Definition test-ns3.py:787
config_ok(self, return_code, stdout, stderr)
Check if configuration for release mode worked normally.
Definition test-ns3.py:792
setUp(self)
Clean configuration/build artifacts before testing configuration and build settings After configuring...
Definition test-ns3.py:805
ns3_executables
ns3_executables holds a list of executables in .lock-ns3 # noqa
Definition test-ns3.py:827
ns3_modules
ns3_modules holds a list to the modules enabled stored in .lock-ns3 # noqa
Definition test-ns3.py:832
Tests ns3 regarding building the project.
Definition test-ns3.py:2067
test_08_InstallationAndUninstallation(self)
Tries setting a ns3 version, then installing it.
Definition test-ns3.py:2262
test_12_CppyyBindings(self)
Test if we can use python bindings.
Definition test-ns3.py:2553
test_13_FetchOptionalComponents(self)
Test if we had regressions with brite, click and openflow modules that depend on homonymous libraries...
Definition test-ns3.py:2585
test_07_OutputDirectory(self)
Try setting a different output directory and if everything is in the right place and still working co...
Definition test-ns3.py:2186
test_02_BuildNonExistingTargets(self)
Try building core-test library without tests enabled.
Definition test-ns3.py:2090
test_01_BuildExistingTargets(self)
Try building the core library.
Definition test-ns3.py:2081
setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned.
Definition test-ns3.py:2072
test_04_BuildProjectNoTaskLines(self)
Try hiding task lines.
Definition test-ns3.py:2112
test_11_StaticBuilds(self)
Test if we can build a static ns-3 library and link it to static programs.
Definition test-ns3.py:2518
test_06_TestVersionFile(self)
Test if changing the version file affects the library names.
Definition test-ns3.py:2142
test_14_LinkContribModuleToSrcModule(self)
Test if we can link contrib modules to src modules.
Definition test-ns3.py:2606
test_03_BuildProject(self)
Try building the project:
Definition test-ns3.py:2100
test_09_Scratches(self)
Tries to build scratch-simulator and subdir/scratch-simulator-subdir.
Definition test-ns3.py:2436
test_05_BreakBuild(self)
Try removing an essential file to break the build.
Definition test-ns3.py:2121
test_10_AmbiguityCheck(self)
Test if ns3 can alert correctly in case a shortcut collision happens.
Definition test-ns3.py:2464
ns3_executables
ns3_executables holds a list of executables in .lock-ns3 # noqa
Definition test-ns3.py:2171
ns3_libraries
ns3_libraries holds a list of built module libraries # noqa
Definition test-ns3.py:2079
ns3 tests related to generic options
Definition test-ns3.py:602
test_01_NoOption(self)
Test not passing any arguments to.
Definition test-ns3.py:616
test_05_CheckVersion(self)
Test only passing 'show version' argument to ns3.
Definition test-ns3.py:652
test_04_CheckProfile(self)
Test only passing 'show profile' argument to ns3.
Definition test-ns3.py:643
test_03_CheckConfig(self)
Test only passing 'show config' argument to ns3.
Definition test-ns3.py:634
setUp(self)
Clean configuration/build artifacts before common commands.
Definition test-ns3.py:607
test_02_NoTaskLines(self)
Test only passing –quiet argument to ns3.
Definition test-ns3.py:625
ns3 tests related to build profiles
Definition test-ns3.py:662
test_05_TYPO(self)
Test a build type with another typo.
Definition test-ns3.py:737
setUp(self)
Clean configuration/build artifacts before testing configuration settings.
Definition test-ns3.py:667
test_03_Optimized(self)
Test the optimized build.
Definition test-ns3.py:707
test_06_OverwriteDefaultSettings(self)
Replace settings set by default (e.g.
Definition test-ns3.py:746
test_01_Debug(self)
Test the debug build.
Definition test-ns3.py:676
test_02_Release(self)
Test the release build.
Definition test-ns3.py:697
test_04_Typo(self)
Test a build type with a typo.
Definition test-ns3.py:728
Test ns3 configuration options.
Definition test-ns3.py:835
test_11_CheckProfile(self)
Test passing 'show profile' argument to ns3 to get the build profile.
Definition test-ns3.py:1303
test_02_Tests(self)
Test enabling and disabling tests.
Definition test-ns3.py:870
test_15_InvalidLibrariesToLink(self)
Test if CMake and ns3 fail in the expected ways when:
Definition test-ns3.py:1489
test_25_CheckBareConfig(self)
Check for regressions in a bare ns-3 configuration.
Definition test-ns3.py:2045
test_22_NinjaTrace(self)
Check if NS3_NINJA_TRACE feature is working Ninja's .ninja_log conversion to about://tracing json for...
Definition test-ns3.py:1923
test_12_CheckVersion(self)
Test passing 'show version' argument to ns3 to get the build version.
Definition test-ns3.py:1312
test_14_MpiCommandTemplate(self)
Test if ns3 is inserting additional arguments by MPICH and OpenMPI to run on the CI.
Definition test-ns3.py:1432
test_18_CheckBuildVersionAndVersionCache(self)
Check if ENABLE_BUILD_VERSION and version.cache are working as expected.
Definition test-ns3.py:1667
test_06_DisableModulesComma(self)
Test disabling comma-separated (waf-style) examples.
Definition test-ns3.py:977
type
type contains the ns3rc variant type (deprecated python-based or current cmake-based)
Definition test-ns3.py:1035
test_09_PropagationOfReturnCode(self)
Test if ns3 is propagating back the return code from the executables called with the run command.
Definition test-ns3.py:1219
test_19_FilterModuleExamplesAndTests(self)
Test filtering in examples and tests from specific modules.
Definition test-ns3.py:1749
test_01_Examples(self)
Test enabling and disabling examples.
Definition test-ns3.py:847
test_07_Ns3rc(self)
Test loading settings from the ns3rc config file.
Definition test-ns3.py:1001
test_03_EnableModules(self)
Test enabling specific modules.
Definition test-ns3.py:897
test_10_CheckConfig(self)
Test passing 'show config' argument to ns3 to get the configuration table.
Definition test-ns3.py:1294
test_24_CheckTestSettings(self)
Check for regressions in test object build.
Definition test-ns3.py:2030
test_08_DryRun(self)
Test dry-run (printing commands to be executed instead of running them)
Definition test-ns3.py:1163
test_05_EnableModulesComma(self)
Test enabling comma-separated (waf-style) examples.
Definition test-ns3.py:953
test_21_ClangTimeTrace(self)
Check if NS3_CLANG_TIMETRACE feature is working Clang's -ftime-trace plus ClangAnalyzer report.
Definition test-ns3.py:1864
test_23_PrecompiledHeaders(self)
Check if precompiled headers are being enabled correctly.
Definition test-ns3.py:2011
setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned.
Definition test-ns3.py:840
test_13_Scratches(self)
Test if CMake target names for scratches and ns3 shortcuts are working correctly.
Definition test-ns3.py:1327
ns3_executables
ns3_executables
Definition test-ns3.py:859
test_16_LibrariesContainingLib(self)
Test if CMake can properly handle modules containing "lib", which is used internally as a prefix for ...
Definition test-ns3.py:1600
test_17_CMakePerformanceTracing(self)
Test if CMake performance tracing works and produces the cmake_performance_trace.log file.
Definition test-ns3.py:1646
test_04_DisableModules(self)
Test disabling specific modules.
Definition test-ns3.py:929
test_20_CheckFastLinkers(self)
Check if fast linkers LLD and Mold are correctly found and configured.
Definition test-ns3.py:1797
ns-3 tests related to dependencies
Definition test-ns3.py:382
test_01_CheckIfIncludedHeadersMatchLinkedModules(self)
Checks if headers from different modules (src/A, contrib/B) that are included by the current module (...
Definition test-ns3.py:387
Tests ns3 usage in more realistic scenarios.
Definition test-ns3.py:2645
test_12_SphinxDocumentation(self)
Test every individual target for Sphinx-based documentation.
Definition test-ns3.py:2838
test_03_BuildAndRunExistingLibraryTarget(self)
Try to build and run a library.
Definition test-ns3.py:2702
test_01_BuildProject(self)
Try to build the project.
Definition test-ns3.py:2677
test_08_RunNoBuildGdb(self)
Test if scratch simulator is executed through gdb and lldb.
Definition test-ns3.py:2751
test_11_DoxygenWithoutBuild(self)
Test the doxygen target that doesn't trigger a full build.
Definition test-ns3.py:2819
test_06_RunNoBuildExistingLibraryTarget(self)
Test ns3 fails to run a library.
Definition test-ns3.py:2733
test_14_EnableSudo(self)
Try to set ownership of scratch-simulator from current user to root, and change execution permissions...
Definition test-ns3.py:2904
ns3_modules
ns3_modules holds a list to the modules enabled stored in .lock-ns3 # noqa
Definition test-ns3.py:2675
setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned Here examples,...
Definition test-ns3.py:2650
test_02_BuildAndRunExistingExecutableTarget(self)
Try to build and run test-runner.
Definition test-ns3.py:2692
test_18_CpmAndVcpkgManagers(self)
Test if CPM and Vcpkg package managers are working properly.
Definition test-ns3.py:3097
test_15_CommandTemplate(self)
Check if command template is working.
Definition test-ns3.py:2987
test_07_RunNoBuildNonExistingExecutableTarget(self)
Test ns3 fails to run an unknown program.
Definition test-ns3.py:2742
test_05_RunNoBuildExistingExecutableTarget(self)
Try to run test-runner without building.
Definition test-ns3.py:2720
test_16_ForwardArgumentsToRunTargets(self)
Check if all flavors of different argument passing to executable targets are working.
Definition test-ns3.py:3017
test_17_RunNoBuildLldb(self)
Test if scratch simulator is executed through lldb.
Definition test-ns3.py:3081
ns3_executables
ns3_executables holds a list of executables in .lock-ns3 # noqa
Definition test-ns3.py:2669
test_09_RunNoBuildValgrind(self)
Test if scratch simulator is executed through valgrind.
Definition test-ns3.py:2772
test_04_BuildAndRunNonExistingTarget(self)
Try to build and run an unknown target.
Definition test-ns3.py:2711
test_13_Documentation(self)
Test the documentation target that builds both doxygen and sphinx based documentation.
Definition test-ns3.py:2872
test_10_DoxygenWithBuild(self)
Test the doxygen target that does trigger a full build.
Definition test-ns3.py:2790
ns-3 tests to control the quality of the repository over time, by checking the state of URLs listed a...
Definition test-ns3.py:3190
test_02_MemoryCheckWithSanitizers(self)
Test if all tests can be executed without hitting major memory bugs.
Definition test-ns3.py:3376
test_03_CheckImageBrightness(self)
Check if images in the docs are above a brightness threshold.
Definition test-ns3.py:3389
test_04_CheckForBrokenLogs(self)
Check if one of the log statements of examples/tests contains/exposes a bug.
Definition test-ns3.py:3425
test_01_CheckForDeadLinksInSources(self)
Test if all urls in source files are alive.
Definition test-ns3.py:3196
ns-3 tests to check if the source code, whitespaces and CMake formatting are according to the coding ...
Definition test-ns3.py:536
test_01_CheckCMakeFormat(self)
Check if there is any difference between tracked file after applying cmake-format.
Definition test-ns3.py:574
None setUp(self)
Import GitRepo and load the original diff state of the repository before the tests.
Definition test-ns3.py:547
ns-3 tests related to checking if source files were left behind, not being used by CMake
Definition test-ns3.py:255
dict directory_and_files
dictionary containing directories with .cc source files # noqa
Definition test-ns3.py:261
test_01_UnusedExampleSources(self)
Test if all example source files are being used in their respective CMakeLists.txt.
Definition test-ns3.py:279
test_03_UnusedUtilsSources(self)
Test if all utils source files are being used in their respective CMakeLists.txt.
Definition test-ns3.py:351
setUp(self)
Scan all C++ source files and add them to a list based on their path.
Definition test-ns3.py:263
test_02_UnusedModuleSources(self)
Test if all module source files are being used in their respective CMakeLists.txt.
Definition test-ns3.py:308
read_lock_entry(entry)
Read interesting entries from the .lock-ns3 file.
Definition test-ns3.py:159
get_libraries_list(lib_outdir=usual_lib_outdir)
Gets a list of built libraries.
Definition test-ns3.py:140
get_headers_list(outdir=usual_outdir)
Gets a list of header files.
Definition test-ns3.py:150
get_enabled_modules()
Definition test-ns3.py:179
cmake_build_target_command
Definition test-ns3.py:39
run_ns3(args, env=None, generator=platform_makefiles)
Runs the ns3 wrapper script with arguments.
Definition test-ns3.py:51
run_program(program, args, python=False, cwd=ns3_path, env=None)
Runs a program with the given arguments and returns a tuple containing (error code,...
Definition test-ns3.py:74
get_programs_list()
Extracts the programs list from .lock-ns3.
Definition test-ns3.py:123
get_test_enabled()
Check if tests are enabled in the .lock-ns3.
Definition test-ns3.py:171