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