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