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