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_script = os.sep.join([ns3_path, "ns3"])
37ns3rc_script = os.sep.join([ns3_path, ".ns3rc"])
38usual_outdir = os.sep.join([ns3_path, "build"])
39usual_build_status_script = os.sep.join([usual_outdir, "build-status.py"])
40usual_c4che_script = os.sep.join([usual_outdir, "c4che", "_cache.py"])
41usual_lib_outdir = os.sep.join([usual_outdir, "lib"])
42
43# Move the current working directory to the ns-3-dev folder
44os.chdir(ns3_path)
45
46# Cmake commands
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=max(1, os.cpu_count() - 1)
50 )
51
52
53def run_ns3(args, env=None):
54 """!
55 Runs the ns3 wrapper script with arguments
56 @param args: string containing arguments that will get split before calling ns3
57 @param env: environment variables dictionary
58 @return tuple containing (error code, stdout and stderr)
59 """
60 return run_program(ns3_script, args, python=True, env=env)
61
62
63# Adapted from https://github.com/metabrainz/picard/blob/master/picard/util/__init__.py
64def run_program(program, args, python=False, cwd=ns3_path, env=None):
65 """!
66 Runs a program with the given arguments and returns a tuple containing (error code, stdout and stderr)
67 @param program: program to execute (or python script)
68 @param args: string containing arguments that will get split before calling the program
69 @param python: flag indicating whether the program is a python script
70 @param cwd: the working directory used that will be the root folder for the execution
71 @param env: environment variables dictionary
72 @return tuple containing (error code, stdout and stderr)
73 """
74 if type(args) != str:
75 raise Exception("args should be a string")
76
77 # Include python interpreter if running a python script
78 if python:
79 arguments = [sys.executable, program]
80 else:
81 arguments = [program]
82
83 if args != "":
84 arguments.extend(re.findall("(?:\".*?\"|\S)+", args))
85
86 for i in range(len(arguments)):
87 arguments[i] = arguments[i].replace("\"", "")
88
89 # Forward environment variables used by the ns3 script
90 current_env = os.environ.copy()
91
92 # Add different environment variables
93 if env:
94 current_env.update(env)
95
96 # Call program with arguments
97 ret = subprocess.run(
98 arguments,
99 stdin=subprocess.DEVNULL,
100 stdout=subprocess.PIPE,
101 stderr=subprocess.PIPE,
102 cwd=cwd, # run process from the ns-3-dev path
103 env=current_env
104 )
105 # Return (error code, stdout and stderr)
106 return ret.returncode, ret.stdout.decode(sys.stdout.encoding), ret.stderr.decode(sys.stderr.encoding)
107
108
109def get_programs_list(build_status_script_path=usual_build_status_script):
110 """!
111 Extracts the programs list from build-status.py
112 @param build_status_script_path: path containing build-status.py
113 @return list of programs.
114 """
115 values = {}
116 with open(build_status_script_path) as f:
117 exec(f.read(), globals(), values)
118 return values["ns3_runnable_programs"]
119
120
121def get_libraries_list(lib_outdir=usual_lib_outdir):
122 """!
123 Gets a list of built libraries
124 @param lib_outdir: path containing libraries
125 @return list of built libraries.
126 """
127 return glob.glob(lib_outdir + '/*', recursive=True)
128
129
130def get_headers_list(outdir=usual_outdir):
131 """!
132 Gets a list of header files
133 @param outdir: path containing headers
134 @return list of headers.
135 """
136 return glob.glob(outdir + '/**/*.h', recursive=True)
137
138
139def read_c4che_entry(entry, c4che_script_path=usual_c4che_script):
140 """!
141 Read interesting entries from the c4che/_cache.py file
142 @param entry: entry to read from c4che/_cache.py
143 @param c4che_script_path: path containing _cache.py
144 @return value of the requested entry.
145 """
146 values = {}
147 with open(c4che_script_path) as f:
148 exec(f.read(), globals(), values)
149 return values.get(entry, None)
150
151
153 """!
154 Check if tests are enabled in the c4che/_cache.py
155 @return bool.
156 """
157 return read_c4che_entry("ENABLE_TESTS")
158
159
161 """
162 Check if tests are enabled in the c4che/_cache.py
163 @return list of enabled modules (prefixed with 'ns3-').
164 """
165 return read_c4che_entry("NS3_ENABLED_MODULES")
166
167
168class NS3RunWafTargets(unittest.TestCase):
169 """!
170 ns3 tests related to compatibility with Waf-produced binaries
171 """
172
173
174 cleaned_once = False
175
176 def setUp(self):
177 """!
178 Clean the default build directory, then configure and build ns-3 with waf
179 @return None
180 """
181 if not NS3RunWafTargets.cleaned_once:
182 NS3RunWafTargets.cleaned_once = True
183 run_ns3("clean")
184 return_code, stdout, stderr = run_program("waf", "configure --enable-examples --enable-tests", python=True)
185 self.assertEqual(return_code, 0)
186 self.assertIn("finished successfully", stdout)
187
188 return_code, stdout, stderr = run_program("waf", "build", python=True)
189 self.assertEqual(return_code, 0)
190 self.assertIn("finished successfully", stdout)
191
193 """!
194 Try to load executables built by waf
195 @return None
196 """
197 # Check if build-status.py exists, then read to get list of executables.
198 self.assertTrue(os.path.exists(usual_build_status_script))
199
201 self.assertGreater(len(self.ns3_executables), 0)
202
204 """!
205 Try to load modules built by waf
206 @return None
207 """
208 # Check if c4che.py exists than read to get the list of enabled modules.
209 self.assertTrue(os.path.exists(usual_c4che_script))
210
212 self.assertGreater(len(self.ns3_modules), 0)
213
215 """!
216 Try to run an executable built by waf
217 @return None
218 """
219 return_code, stdout, stderr = run_ns3("run scratch-simulator --no-build")
220 self.assertEqual(return_code, 0)
221 self.assertIn("Scratch Simulator", stderr)
222
224 """!
225 Try to run a different executable built by waf
226 @return None
227 """
228 return_code, stdout, stderr = run_ns3("run command-line-example --verbose --no-build")
229 self.assertEqual(return_code, 0)
230 self.assertIn("command-line-example", stdout)
231
233 """!
234 Try to run a test case built by waf that calls the ns3 wrapper script
235 @return None
236 """
237 return_code, stdout, stderr = run_program("test.py", "--no-build -s core-example-simulator", True)
238 self.assertEqual(return_code, 0)
239 self.assertIn("PASS", stdout)
240
242 """!
243 Try to run a different test case built by waf that calls the ns3 wrapper script
244 @return None
245 """
246 return_code, stdout, stderr = run_program("test.py", "--no-build -s examples-as-tests-test-suite", True)
247 self.assertEqual(return_code, 0)
248 self.assertIn("PASS", stdout)
249
251 """!
252 Try to run test cases built by waf that calls the ns3 wrapper script
253 when the output directory is set to a different path
254 @return None
255 """
256 run_ns3("clean")
257
258 return_code, stdout, stderr = run_program("waf",
259 "configure --enable-examples --enable-tests --out build/debug",
260 python=True)
261 self.assertEqual(return_code, 0)
262 self.assertIn("finished successfully", stdout)
263
264 return_code, stdout, stderr = run_program("waf",
265 '--run "test-runner --suite=core-example-simulator --verbose"',
266 True)
267 self.assertEqual(return_code, 0)
268 self.assertIn("PASS", stdout)
269
270
271class NS3CommonSettingsTestCase(unittest.TestCase):
272 """!
273 ns3 tests related to generic options
274 """
275
276 def setUp(self):
277 """!
278 Clean configuration/build artifacts before common commands
279 @return None
280 """
281 super().setUp()
282 # No special setup for common test cases other than making sure we are working on a clean directory.
283 run_ns3("clean")
284
286 """!
287 Test not passing any arguments to
288 @return None
289 """
290 return_code, stdout, stderr = run_ns3("")
291 self.assertEqual(return_code, 1)
292 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
293
295 """!
296 Test only passing --quiet argument to ns3
297 @return None
298 """
299 return_code, stdout, stderr = run_ns3("--quiet")
300 self.assertEqual(return_code, 1)
301 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
302
304 """!
305 Test only passing --check-config argument to ns3
306 @return None
307 """
308 return_code, stdout, stderr = run_ns3("--check-config")
309 self.assertEqual(return_code, 1)
310 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
311
313 """!
314 Test only passing --check-profile argument to ns3
315 @return None
316 """
317 return_code, stdout, stderr = run_ns3("--check-profile")
318 self.assertEqual(return_code, 1)
319 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
320
322 """!
323 Test only passing --check-version argument to ns3
324 @return None
325 """
326 return_code, stdout, stderr = run_ns3("--check-version")
327 self.assertEqual(return_code, 1)
328 self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
329
330
331class NS3ConfigureBuildProfileTestCase(unittest.TestCase):
332 """!
333 ns3 tests related to build profiles
334 """
335
336 def setUp(self):
337 """!
338 Clean configuration/build artifacts before testing configuration settings
339 @return None
340 """
341 super().setUp()
342 # No special setup for common test cases other than making sure we are working on a clean directory.
343 run_ns3("clean")
344
345 def test_01_Debug(self):
346 """!
347 Test the debug build
348 @return None
349 """
350 return_code, stdout, stderr = run_ns3("configure -d debug --enable-verbose")
351 self.assertEqual(return_code, 0)
352 self.assertIn("Build profile : debug", stdout)
353 self.assertIn("Build files have been written to", stdout)
354
355 # Build core to check if profile suffixes match the expected.
356 return_code, stdout, stderr = run_ns3("build core")
357 self.assertEqual(return_code, 0)
358 self.assertIn("Built target libcore", stdout)
359
360 libraries = get_libraries_list()
361 self.assertGreater(len(libraries), 0)
362 self.assertIn("core-debug", libraries[0])
363
365 """!
366 Test the release build
367 @return None
368 """
369 return_code, stdout, stderr = run_ns3("configure -d release")
370 self.assertEqual(return_code, 0)
371 self.assertIn("Build profile : release", stdout)
372 self.assertIn("Build files have been written to", stdout)
373
375 """!
376 Test the optimized build
377 @return None
378 """
379 return_code, stdout, stderr = run_ns3("configure -d optimized --enable-verbose")
380 self.assertEqual(return_code, 0)
381 self.assertIn("Build profile : optimized", stdout)
382 self.assertIn("Build files have been written to", stdout)
383
384 # Build core to check if profile suffixes match the expected
385 return_code, stdout, stderr = run_ns3("build core")
386 self.assertEqual(return_code, 0)
387 self.assertIn("Built target libcore", stdout)
388
389 libraries = get_libraries_list()
390 self.assertGreater(len(libraries), 0)
391 self.assertIn("core-optimized", libraries[0])
392
393 def test_04_Typo(self):
394 """!
395 Test a build type with a typo
396 @return None
397 """
398 return_code, stdout, stderr = run_ns3("configure -d Optimized")
399 self.assertEqual(return_code, 2)
400 self.assertIn("invalid choice: 'Optimized'", stderr)
401
402 def test_05_TYPO(self):
403 """!
404 Test a build type with another typo
405 @return None
406 """
407 return_code, stdout, stderr = run_ns3("configure -d OPTIMIZED")
408 self.assertEqual(return_code, 2)
409 self.assertIn("invalid choice: 'OPTIMIZED'", stderr)
410
411
412class NS3BaseTestCase(unittest.TestCase):
413 """!
414 Generic test case with basic function inherited by more complex tests.
415 """
416
417
418 cleaned_once = False
419
420 def config_ok(self, return_code, stdout):
421 """!
422 Check if configuration for release mode worked normally
423 @param return_code: return code from CMake
424 @param stdout: output from CMake.
425 @return None
426 """
427 self.assertEqual(return_code, 0)
428 self.assertIn("Build profile : release", stdout)
429 self.assertIn("Build files have been written to", stdout)
430
431 def setUp(self):
432 """!
433 Clean configuration/build artifacts before testing configuration and build settings
434 After configuring the build as release,
435 check if configuration worked and check expected output files.
436 @return None
437 """
438 super().setUp()
439
440 if os.path.exists(ns3rc_script):
441 os.remove(ns3rc_script)
442
443 # We only clear it once and then update the settings by changing flags or consuming ns3rc.
444 if not NS3BaseTestCase.cleaned_once:
445 NS3BaseTestCase.cleaned_once = True
446 run_ns3("clean")
447 return_code, stdout, stderr = run_ns3("configure -d release --enable-verbose")
448 self.config_ok(return_code, stdout)
449
450 # Check if build-status.py exists, then read to get list of executables.
451 self.assertTrue(os.path.exists(usual_build_status_script))
452
454
455 # Check if c4che.py exists than read to get the list of enabled modules.
456 self.assertTrue(os.path.exists(usual_c4che_script))
457
459
460
462 """!
463 Test ns3 configuration options
464 """
465
466
467 cleaned_once = False
468
469 def setUp(self):
470 """!
471 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
472 @return None
473 """
474 if not NS3ConfigureTestCase.cleaned_once:
475 NS3ConfigureTestCase.cleaned_once = True
476 NS3BaseTestCase.cleaned_once = False
477 super().setUp()
478
480 """!
481 Test enabling and disabling examples
482 @return None
483 """
484 return_code, stdout, stderr = run_ns3("configure --enable-examples")
485
486 # This just tests if we didn't break anything, not that we actually have enabled anything.
487 self.config_ok(return_code, stdout)
488
489 # If nothing went wrong, we should have more executables in the list after enabling the examples.
490 self.assertGreater(len(get_programs_list()), len(self.ns3_executables))
491
492 # Now we disabled them back.
493 return_code, stdout, stderr = run_ns3("configure --disable-examples")
494
495 # This just tests if we didn't break anything, not that we actually have enabled anything.
496 self.config_ok(return_code, stdout)
497
498 # Then check if they went back to the original list.
499 self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
500
501 def test_02_Tests(self):
502 """!
503 Test enabling and disabling tests
504 @return None
505 """
506 # Try enabling tests
507 return_code, stdout, stderr = run_ns3("configure --enable-tests")
508 self.config_ok(return_code, stdout)
509
510 # Then try building the libcore test
511 return_code, stdout, stderr = run_ns3("build core-test")
512
513 # If nothing went wrong, this should have worked
514 self.assertEqual(return_code, 0)
515 self.assertIn("Built target libcore-test", stdout)
516
517 # Now we disabled the tests
518 return_code, stdout, stderr = run_ns3("configure --disable-tests")
519 self.config_ok(return_code, stdout)
520
521 # Now building the library test should fail
522 return_code, stdout, stderr = run_ns3("build core-test")
523
524 # Then check if they went back to the original list
525 self.assertGreater(len(stderr), 0)
526
528 """!
529 Test enabling specific modules
530 @return None
531 """
532 # Try filtering enabled modules to network+Wi-Fi and their dependencies
533 return_code, stdout, stderr = run_ns3("configure --enable-modules='network;wifi'")
534 self.config_ok(return_code, stdout)
535
536 # At this point we should have fewer modules
537 enabled_modules = get_enabled_modules()
538 self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
539 self.assertIn("ns3-network", enabled_modules)
540 self.assertIn("ns3-wifi", enabled_modules)
541
542 # Try cleaning the list of enabled modules to reset to the normal configuration.
543 return_code, stdout, stderr = run_ns3("configure --enable-modules=''")
544 self.config_ok(return_code, stdout)
545
546 # At this point we should have the same amount of modules that we had when we started.
547 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
548
550 """!
551 Test disabling specific modules
552 @return None
553 """
554 # Try filtering disabled modules to disable lte and modules that depend on it.
555 return_code, stdout, stderr = run_ns3("configure --disable-modules='lte;wimax'")
556 self.config_ok(return_code, stdout)
557
558 # At this point we should have fewer modules.
559 enabled_modules = get_enabled_modules()
560 self.assertLess(len(enabled_modules), len(self.ns3_modules))
561 self.assertNotIn("ns3-lte", enabled_modules)
562 self.assertNotIn("ns3-wimax", enabled_modules)
563
564 # Try cleaning the list of enabled modules to reset to the normal configuration.
565 return_code, stdout, stderr = run_ns3("configure --disable-modules=''")
566 self.config_ok(return_code, stdout)
567
568 # At this point we should have the same amount of modules that we had when we started.
569 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
570
572 """!
573 Test enabling comma-separated (waf-style) examples
574 @return None
575 """
576 # Try filtering enabled modules to network+Wi-Fi and their dependencies.
577 return_code, stdout, stderr = run_ns3("configure --enable-modules='network,wifi'")
578 self.config_ok(return_code, stdout)
579
580 # At this point we should have fewer modules.
581 enabled_modules = get_enabled_modules()
582 self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
583 self.assertIn("ns3-network", enabled_modules)
584 self.assertIn("ns3-wifi", enabled_modules)
585
586 # Try cleaning the list of enabled modules to reset to the normal configuration.
587 return_code, stdout, stderr = run_ns3("configure --enable-modules=''")
588 self.config_ok(return_code, stdout)
589
590 # At this point we should have the same amount of modules that we had when we started.
591 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
592
594 """!
595 Test disabling comma-separated (waf-style) examples
596 @return None
597 """
598 # Try filtering disabled modules to disable lte and modules that depend on it.
599 return_code, stdout, stderr = run_ns3("configure --disable-modules='lte,mpi'")
600 self.config_ok(return_code, stdout)
601
602 # At this point we should have fewer modules.
603 enabled_modules = get_enabled_modules()
604 self.assertLess(len(enabled_modules), len(self.ns3_modules))
605 self.assertNotIn("ns3-lte", enabled_modules)
606 self.assertNotIn("ns3-mpi", enabled_modules)
607
608 # Try cleaning the list of enabled modules to reset to the normal configuration.
609 return_code, stdout, stderr = run_ns3("configure --disable-modules=''")
610 self.config_ok(return_code, stdout)
611
612 # At this point we should have the same amount of modules that we had when we started.
613 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
614
615 def test_07_Ns3rc(self):
616 """!
617 Test loading settings from the ns3rc config file
618 @return None
619 """
620 ns3rc_template = "# ! /usr/bin/env python\
621 \
622 # A list of the modules that will be enabled when ns-3 is run.\
623 # Modules that depend on the listed modules will be enabled also.\
624 #\
625 # All modules can be enabled by choosing 'all_modules'.\
626 modules_enabled = [{modules}]\
627 \
628 # Set this equal to true if you want examples to be run.\
629 examples_enabled = {examples}\
630 \
631 # Set this equal to true if you want tests to be run.\
632 tests_enabled = {tests}\
633 "
634
635 # Now we repeat the command line tests but with the ns3rc file.
636 with open(ns3rc_script, "w") as f:
637 f.write(ns3rc_template.format(modules="'lte'", examples="False", tests="True"))
638
639 # Reconfigure.
640 return_code, stdout, stderr = run_ns3("configure")
641 self.config_ok(return_code, stdout)
642
643 # Check.
644 enabled_modules = get_enabled_modules()
645 self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
646 self.assertIn("ns3-lte", enabled_modules)
647 self.assertTrue(get_test_enabled())
648 self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
649
650 # Replace the ns3rc file
651 with open(ns3rc_script, "w") as f:
652 f.write(ns3rc_template.format(modules="'wifi'", examples="True", tests="False"))
653
654 # Reconfigure
655 return_code, stdout, stderr = run_ns3("configure")
656 self.config_ok(return_code, stdout)
657
658 # Check
659 enabled_modules = get_enabled_modules()
660 self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
661 self.assertIn("ns3-wifi", enabled_modules)
662 self.assertFalse(get_test_enabled())
663 self.assertGreater(len(get_programs_list()), len(self.ns3_executables))
664
665 # Then we roll back by removing the ns3rc config file
666 os.remove(ns3rc_script)
667
668 # Reconfigure
669 return_code, stdout, stderr = run_ns3("configure")
670 self.config_ok(return_code, stdout)
671
672 # Check
673 self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
674 self.assertFalse(get_test_enabled())
675 self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
676
677 def test_08_DryRun(self):
678 """!
679 Test dry-run (printing commands to be executed instead of running them)
680 @return None
681 """
682 run_ns3("clean")
683
684 # Try dry-run before and after the positional commands (outputs should match)
685 for positional_command in ["configure", "build", "clean"]:
686 return_code, stdout, stderr = run_ns3("--dry-run %s" % positional_command)
687 return_code1, stdout1, stderr1 = run_ns3("%s --dry-run" % positional_command)
688
689 self.assertEqual(return_code, return_code1)
690 self.assertEqual(stdout, stdout1)
691 self.assertEqual(stderr, stderr1)
692
693 # Build target before using below
694 run_ns3("configure -d release --enable-verbose")
695 run_ns3("build scratch-simulator")
696
697 # Run all cases and then check outputs
698 return_code0, stdout0, stderr0 = run_ns3("--dry-run run scratch-simulator")
699 return_code1, stdout1, stderr1 = run_ns3("run scratch-simulator --verbose")
700 return_code2, stdout2, stderr2 = run_ns3("--dry-run run scratch-simulator --no-build")
701 return_code3, stdout3, stderr3 = run_ns3("run scratch-simulator --no-build")
702
703 # Return code and stderr should be the same for all of them.
704 self.assertEqual(sum([return_code0, return_code1, return_code2, return_code3]), 0)
705 self.assertEqual([stderr0, stderr1, stderr2, stderr3], [""] * 4)
706
707 scratch_path = None
708 for program in get_programs_list():
709 if "scratch-simulator" in program and "subdir" not in program:
710 scratch_path = program
711 break
712
713 # Scratches currently have a 'scratch_' prefix in their CMake targets
714 # Case 0: dry-run + run (should print commands to build target and then run)
715 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout0)
716 self.assertIn(scratch_path, stdout0)
717
718 # Case 1: run (should print all the commands of case 1 plus CMake output from build)
719 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout1)
720 self.assertIn("Built target", stdout1)
721 self.assertIn(scratch_path, stdout1)
722
723 # Case 2: dry-run + run-no-build (should print commands to run the target)
724 self.assertIn(scratch_path, stdout2)
725
726 # Case 3: run-no-build (should print the target output only)
727 self.assertEqual("", stdout3)
728
730 """!
731 Test if ns3 is propagating back the return code from the executables called with the run command
732 @return None
733 """
734 # From this point forward we are reconfiguring in debug mode
735 return_code, _, _ = run_ns3("clean")
736 self.assertEqual(return_code, 0)
737
738 return_code, _, _ = run_ns3("configure --enable-examples --enable-tests")
739 self.assertEqual(return_code, 0)
740
741 # Build necessary executables
742 return_code, stdout, stderr = run_ns3("build command-line-example test-runner")
743 self.assertEqual(return_code, 0)
744
745 # Now some tests will succeed normally
746 return_code, stdout, stderr = run_ns3("run \"test-runner --test-name=command-line\" --no-build")
747 self.assertEqual(return_code, 0)
748
749 # Now some tests will fail during NS_COMMANDLINE_INTROSPECTION
750 return_code, stdout, stderr = run_ns3("run \"test-runner --test-name=command-line\" --no-build",
751 env={"NS_COMMANDLINE_INTROSPECTION": ".."}
752 )
753 self.assertNotEqual(return_code, 0)
754
756 """!
757 Test passing --check-config argument to ns3 to get the configuration table
758 @return None
759 """
760 return_code, stdout, stderr = run_ns3("--check-config")
761 self.assertEqual(return_code, 0)
762 self.assertIn("Summary of optional NS-3 features", stdout)
763
765 """!
766 Test passing --check-profile argument to ns3 to get the build profile
767 @return None
768 """
769 return_code, stdout, stderr = run_ns3("--check-profile")
770 self.assertEqual(return_code, 0)
771 self.assertIn("Build profile: debug", stdout)
772
774 """!
775 Test passing --check-version argument to ns3 to get the build version
776 @return None
777 """
778 return_code, stdout, stderr = run_ns3("--check-version")
779 self.assertEqual(return_code, 0)
780 self.assertIn("ns-3 version:", stdout)
781
783 """!
784 Test if CMake target names for scratches and ns3 shortcuts
785 are working correctly
786 """
787
788 test_files = ["scratch/main.cc",
789 "scratch/empty.cc",
790 "scratch/subdir1/main.cc",
791 "scratch/subdir2/main.cc"]
792
793 # Create test scratch files
794 for path in test_files:
795 filepath = os.path.join(ns3_path, path)
796 os.makedirs(os.path.dirname(filepath), exist_ok=True)
797 with open(filepath, "w") as f:
798 if "main" in path:
799 f.write("int main (int argc, char *argv[]){}")
800 else:
801 # no main function will prevent this target from
802 # being created, we should skip it and continue
803 # processing without crashing
804 f.write("")
805
806 # Reload the cmake cache to pick them up
807 return_code, stdout, stderr = run_ns3("configure")
808 self.assertEqual(return_code, 0)
809
810 # Try to build them with ns3 and cmake
811 for path in test_files:
812 path = path.replace(".cc", "")
813 return_code1, stdout1, stderr1 = run_program("cmake", "--build . --target %s"
814 % path.replace("/", "_"),
815 cwd=os.path.join(ns3_path, "cmake_cache"))
816 return_code2, stdout2, stderr2 = run_ns3("build %s" % path)
817 if "main" in path:
818 self.assertEqual(return_code1, 0)
819 self.assertEqual(return_code2, 0)
820 else:
821 self.assertEqual(return_code1, 2)
822 self.assertEqual(return_code2, 1)
823
824 # Try to run them
825 for path in test_files:
826 path = path.replace(".cc", "")
827 return_code, stdout, stderr = run_ns3("run %s --no-build" % path)
828 if "main" in path:
829 self.assertEqual(return_code, 0)
830 else:
831 self.assertEqual(return_code, 1)
832
833 # Delete the test files and reconfigure to clean them up
834 for path in test_files:
835 source_absolute_path = os.path.join(ns3_path, path)
836 os.remove(source_absolute_path)
837 if "empty" in path:
838 continue
839 filename = os.path.basename(path).replace(".cc", "")
840 executable_absolute_path = os.path.dirname(os.path.join(ns3_path, "build", path))
841 executable_name = list(filter(lambda x: filename in x,
842 os.listdir(executable_absolute_path)
843 )
844 )[0]
845
846 os.remove(os.path.join(executable_absolute_path, executable_name))
847 if path not in ["scratch/main.cc", "scratch/empty.cc"]:
848 os.rmdir(os.path.dirname(source_absolute_path))
849
850 return_code, stdout, stderr = run_ns3("configure")
851 self.assertEqual(return_code, 0)
852
854 """!
855 Test if ns3 is inserting additional arguments by MPICH and OpenMPI to run on the CI
856 """
857 # Skip test if mpi is not installed
858 if shutil.which("mpiexec") is None:
859 return
860
861 # Ensure sample simulator was built
862 return_code, stdout, stderr = run_ns3("build sample-simulator")
863 self.assertEqual(return_code, 0)
864
865 # Get executable path
866 sample_simulator_path = list(filter(lambda x: "sample-simulator" in x, self.ns3_executables))[0]
867
868 mpi_command = "--dry-run run sample-simulator --command-template=\"mpiexec -np 2 %s\""
869 non_mpi_command = "--dry-run run sample-simulator --command-template=\"echo %s\""
870
871 # Get the commands to run sample-simulator in two processes with mpi
872 return_code, stdout, stderr = run_ns3(mpi_command)
873 self.assertEqual(return_code, 0)
874 self.assertIn("mpiexec -np 2 %s" % sample_simulator_path, stdout)
875
876 # Get the commands to run sample-simulator in two processes with mpi, now with the environment variable
877 return_code, stdout, stderr = run_ns3(mpi_command, env={"MPI_CI": "1"})
878 self.assertEqual(return_code, 0)
879 if shutil.which("ompi_info"):
880 self.assertIn("mpiexec --allow-run-as-root --oversubscribe -np 2 %s" % sample_simulator_path, stdout)
881 else:
882 self.assertIn("mpiexec --allow-run-as-root -np 2 %s" % sample_simulator_path, stdout)
883
884 # Now we repeat for the non-mpi command
885 return_code, stdout, stderr = run_ns3(non_mpi_command)
886 self.assertEqual(return_code, 0)
887 self.assertIn("echo %s" % sample_simulator_path, stdout)
888
889 # Again the non-mpi command, with the MPI_CI environment variable set
890 return_code, stdout, stderr = run_ns3(non_mpi_command, env={"MPI_CI": "1"})
891 self.assertEqual(return_code, 0)
892 self.assertIn("echo %s" % sample_simulator_path, stdout)
893
894
896 """!
897 Tests ns3 regarding building the project
898 """
899
900
901 cleaned_once = False
902
903 def setUp(self):
904 """!
905 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
906 @return None
907 """
908 if not NS3BuildBaseTestCase.cleaned_once:
909 NS3BuildBaseTestCase.cleaned_once = True
910 NS3BaseTestCase.cleaned_once = False
911 super().setUp()
912
914
916 """!
917 Try building the core library
918 @return None
919 """
920 return_code, stdout, stderr = run_ns3("build core")
921 self.assertEqual(return_code, 0)
922 self.assertIn("Built target libcore", stdout)
923
925 """!
926 Try building core-test library without tests enabled
927 @return None
928 """
929 # tests are not enabled, so the target isn't available
930 return_code, stdout, stderr = run_ns3("build core-test")
931 self.assertGreater(len(stderr), 0)
932
934 """!
935 Try building the project:
936 @return None
937 """
938 return_code, stdout, stderr = run_ns3("build")
939 self.assertEqual(return_code, 0)
940 self.assertIn("Built target", stdout)
941 for program in get_programs_list():
942 self.assertTrue(os.path.exists(program))
943 self.assertIn(cmake_build_project_command, stdout)
944
946 """!
947 Try hiding task lines
948 @return None
949 """
950 return_code, stdout, stderr = run_ns3("--quiet build")
951 self.assertEqual(return_code, 0)
952 self.assertIn(cmake_build_project_command, stdout)
953
955 """!
956 Try removing an essential file to break the build
957 @return None
958 """
959 # change an essential file to break the build.
960 attribute_cc_path = os.sep.join([ns3_path, "src", "core", "model", "attribute.cc"])
961 attribute_cc_bak_path = attribute_cc_path + ".bak"
962 shutil.move(attribute_cc_path, attribute_cc_bak_path)
963
964 # build should break.
965 return_code, stdout, stderr = run_ns3("build")
966 self.assertNotEqual(return_code, 0)
967
968 # move file back.
969 shutil.move(attribute_cc_bak_path, attribute_cc_path)
970
971 # build should work again.
972 return_code, stdout, stderr = run_ns3("build")
973 self.assertEqual(return_code, 0)
974
976 """!
977 Test if changing the version file affects the library names
978 @return None
979 """
980 version_file = os.sep.join([ns3_path, "VERSION"])
981 with open(version_file, "w") as f:
982 f.write("3-00\n")
983
984 # Reconfigure.
985 return_code, stdout, stderr = run_ns3("configure")
986 self.config_ok(return_code, stdout)
987
988 # Build.
989 return_code, stdout, stderr = run_ns3("build")
990 self.assertEqual(return_code, 0)
991 self.assertIn("Built target", stdout)
992
993 # Programs with new versions.
994 new_programs = get_programs_list()
995
996 # Check if they exist.
997 for program in new_programs:
998 self.assertTrue(os.path.exists(program))
999
1000 # Check if we still have the same number of binaries.
1001 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
1002
1003 # Check if versions changed from 3-dev to 3-00.
1004 libraries = get_libraries_list()
1005 new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
1006 self.assertEqual(len(new_libraries), len(self.ns3_libraries))
1007 for library in new_libraries:
1008 self.assertNotIn("libns3-dev", library)
1009 self.assertIn("libns3-00", library)
1010 self.assertTrue(os.path.exists(library))
1011
1012 # Restore version file.
1013 with open(version_file, "w") as f:
1014 f.write("3-dev\n")
1015
1016 # Reset flag to let it clean the build.
1017 NS3BuildBaseTestCase.cleaned_once = False
1018
1020 """!
1021 Try setting a different output directory and if everything is
1022 in the right place and still working correctly
1023 @return None
1024 """
1025 # Re-build to return to the original state.
1026 run_ns3("build")
1027
1028
1030
1031
1033
1034 # Delete built programs and libraries to check if they were restored later.
1035 for program in self.ns3_executablesns3_executables:
1036 os.remove(program)
1037 for library in self.ns3_libraries:
1038 os.remove(library)
1039
1040 # Reconfigure setting the output folder to ns-3-dev/build/release (both as an absolute path or relative).
1041 absolute_path = os.sep.join([ns3_path, "build", "release"])
1042 relative_path = os.sep.join(["build", "release"])
1043 for different_out_dir in [absolute_path, relative_path]:
1044 return_code, stdout, stderr = run_ns3("configure --out=%s" % different_out_dir)
1045 self.config_ok(return_code, stdout)
1046 self.assertIn("Build directory : %s" % absolute_path, stdout)
1047
1048 # Build
1049 run_ns3("build")
1050
1051 # Check if we have the same number of binaries and that they were built correctly.
1052 new_programs = get_programs_list(os.sep.join([absolute_path, "build-status.py"]))
1053 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
1054 for program in new_programs:
1055 self.assertTrue(os.path.exists(program))
1056
1057 # Check if we have the same number of libraries and that they were built correctly.
1058 libraries = get_libraries_list(os.sep.join([absolute_path, "lib"]))
1059 new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
1060 self.assertEqual(len(new_libraries), len(self.ns3_libraries))
1061 for library in new_libraries:
1062 self.assertTrue(os.path.exists(library))
1063
1064 # Remove files in the different output dir.
1065 shutil.rmtree(absolute_path)
1066
1067 # Restore original output directory.
1068 return_code, stdout, stderr = run_ns3("configure --out=''")
1069 self.config_ok(return_code, stdout)
1070 self.assertIn("Build directory : %s" % usual_outdir, stdout)
1071
1072 # Try re-building.
1073 run_ns3("build")
1074
1075 # Check if we have the same binaries we had at the beginning.
1076 new_programs = get_programs_list()
1077 self.assertEqual(len(new_programs), len(self.ns3_executablesns3_executables))
1078 for program in new_programs:
1079 self.assertTrue(os.path.exists(program))
1080
1081 # Check if we have the same libraries we had at the beginning.
1082 libraries = get_libraries_list()
1083 self.assertEqual(len(libraries), len(self.ns3_libraries))
1084 for library in libraries:
1085 self.assertTrue(os.path.exists(library))
1086
1088 """!
1089 Tries setting a ns3 version, then installing it.
1090 After that, tries searching for ns-3 with CMake's find_package(ns3).
1091 Finally, tries using core library in a 3rd-party project
1092 @return None
1093 """
1094 # Remove existing libraries from the previous step.
1095 libraries = get_libraries_list()
1096 for library in libraries:
1097 os.remove(library)
1098
1099 # 3-dev version format is not supported by CMake, so we use 3.01.
1100 version_file = os.sep.join([ns3_path, "VERSION"])
1101 with open(version_file, "w") as f:
1102 f.write("3-01\n")
1103
1104 # Reconfigure setting the installation folder to ns-3-dev/build/install.
1105 install_prefix = os.sep.join([ns3_path, "build", "install"])
1106 return_code, stdout, stderr = run_ns3("configure --prefix=%s" % install_prefix)
1107 self.config_ok(return_code, stdout)
1108
1109 # Build.
1110 run_ns3("build")
1111 libraries = get_libraries_list()
1112 headers = get_headers_list()
1113
1114 # Install.
1115 run_ns3("install")
1116
1117 # Find out if libraries were installed to lib or lib64 (Fedora thing).
1118 lib64 = os.path.exists(os.sep.join([install_prefix, "lib64"]))
1119 installed_libdir = os.sep.join([install_prefix, ("lib64" if lib64 else "lib")])
1120
1121 # Make sure all libraries were installed.
1122 installed_libraries = get_libraries_list(installed_libdir)
1123 installed_libraries_list = ";".join(installed_libraries)
1124 for library in libraries:
1125 library_name = os.path.basename(library)
1126 self.assertIn(library_name, installed_libraries_list)
1127
1128 # Make sure all headers were installed.
1129 installed_headers = get_headers_list(install_prefix)
1130 missing_headers = list(set([os.path.basename(x) for x in headers])
1131 - (set([os.path.basename(x) for x in installed_headers]))
1132 )
1133 self.assertEqual(len(missing_headers), 0)
1134
1135 # Now create a test CMake project and try to find_package ns-3.
1136 test_main_file = os.sep.join([install_prefix, "main.cpp"])
1137 with open(test_main_file, "w") as f:
1138 f.write("""
1139 #include <ns3/core-module.h>
1140 using namespace ns3;
1141 int main ()
1142 {
1143 Simulator::Stop (Seconds (1.0));
1144 Simulator::Run ();
1145 Simulator::Destroy ();
1146 return 0;
1147 }
1148 """)
1149
1150 # We try to use this library without specifying a version,
1151 # specifying ns3-01 (text version with 'dev' is not supported)
1152 # and specifying ns3-00 (a wrong version)
1153 for version in ["", "3.01", "3.00"]:
1154 find_package_import = """
1155 list(APPEND CMAKE_PREFIX_PATH ./{lib}/cmake/ns3)
1156 find_package(ns3 {version} COMPONENTS libcore)
1157 target_link_libraries(test PRIVATE ns3::libcore)
1158 """.format(lib=("lib64" if lib64 else "lib"), version=version)
1159 pkgconfig_import = """
1160 list(APPEND CMAKE_PREFIX_PATH ./)
1161 include(FindPkgConfig)
1162 pkg_check_modules(ns3 REQUIRED IMPORTED_TARGET ns3-core{version})
1163 target_link_libraries(test PUBLIC PkgConfig::ns3)
1164 """.format(lib=("lib64" if lib64 else "lib"),
1165 version="="+version if version else ""
1166 )
1167
1168 for import_type in [pkgconfig_import, find_package_import]:
1169 test_cmake_project = """
1170 cmake_minimum_required(VERSION 3.10..3.10)
1171 project(ns3_consumer CXX)
1172 add_executable(test main.cpp)
1173 """ + import_type
1174
1175 test_cmake_project_file = os.sep.join([install_prefix, "CMakeLists.txt"])
1176 with open(test_cmake_project_file, "w") as f:
1177 f.write(test_cmake_project)
1178
1179 # Configure the test project
1180 cmake = shutil.which("cmake")
1181 return_code, stdout, stderr = run_program(cmake,
1182 "-DCMAKE_BUILD_TYPE=debug .",
1183 cwd=install_prefix)
1184 if version == "3.00":
1185 self.assertEqual(return_code, 1)
1186 if import_type == find_package_import:
1187 self.assertIn('Could not find a configuration file for package "ns3" that is compatible',
1188 stderr.replace("\n", ""))
1189 elif import_type == pkgconfig_import:
1190 self.assertIn('A required package was not found',
1191 stderr.replace("\n", ""))
1192 else:
1193 raise Exception("Unknown import type")
1194 else:
1195 self.assertEqual(return_code, 0)
1196 self.assertIn("Build files", stdout)
1197
1198 # Build the test project making use of import ns-3
1199 return_code, stdout, stderr = run_program("cmake", "--build .", cwd=install_prefix)
1200
1201 if version == "3.00":
1202 self.assertEqual(return_code, 2)
1203 self.assertGreater(len(stderr), 0)
1204 else:
1205 self.assertEqual(return_code, 0)
1206 self.assertIn("Built target", stdout)
1207
1208 # Try running the test program that imports ns-3
1209 return_code, stdout, stderr = run_program("./test", "", cwd=install_prefix)
1210 self.assertEqual(return_code, 0)
1211
1212 # Uninstall
1213 return_code, stdout, stderr = run_ns3("uninstall")
1214 self.assertIn("Built target uninstall", stdout)
1215
1216 # Restore 3-dev version file
1217 with open(version_file, "w") as f:
1218 f.write("3-dev\n")
1219
1220 # Reset flag to let it clean the build
1221 NS3BuildBaseTestCase.cleaned_once = False
1222
1224 """!
1225 Tries to build scratch-simulator and subdir/scratch-simulator-subdir
1226 @return None
1227 """
1228 # Build.
1229 targets = {"scratch/scratch-simulator": "scratch-simulator",
1230 "scratch-simulator": "scratch-simulator",
1231 "scratch/subdir/scratch-simulator-subdir": "subdir_scratch-simulator-subdir",
1232 "subdir/scratch-simulator-subdir": "subdir_scratch-simulator-subdir",
1233 "scratch-simulator-subdir": "subdir_scratch-simulator-subdir",
1234 }
1235 for (target_to_run, target_cmake) in targets.items():
1236 # Test if build is working.
1237 build_line = "target scratch_%s" % target_cmake
1238 return_code, stdout, stderr = run_ns3("build %s" % target_to_run)
1239 self.assertEqual(return_code, 0)
1240 self.assertIn(build_line, stdout)
1241
1242 # Test if run is working
1243 return_code, stdout, stderr = run_ns3("run %s --verbose" % target_to_run)
1244 self.assertEqual(return_code, 0)
1245 self.assertIn(build_line, stdout)
1246 stdout = stdout.replace("scratch_%s" % target_cmake, "") # remove build lines
1247 self.assertIn(target_to_run.split("/")[-1], stdout)
1248
1249
1251 """!
1252 Tests ns3 usage in more realistic scenarios
1253 """
1254
1255
1256 cleaned_once = False
1257
1258 def setUp(self):
1259 """!
1260 Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned
1261 Here examples, tests and documentation are also enabled.
1262 @return None
1263 """
1264 if not NS3ExpectedUseTestCase.cleaned_once:
1265 NS3ExpectedUseTestCase.cleaned_once = True
1266 NS3BaseTestCase.cleaned_once = False
1267 super().setUp()
1268
1269 # On top of the release build configured by NS3ConfigureTestCase, also enable examples, tests and docs.
1270 return_code, stdout, stderr = run_ns3("configure --enable-examples --enable-tests")
1271 self.config_ok(return_code, stdout)
1272
1273 # Check if build-status.py exists, then read to get list of executables.
1274 self.assertTrue(os.path.exists(usual_build_status_script))
1275
1276
1278
1279 # Check if c4che.py exists than read to get the list of enabled modules.
1280 self.assertTrue(os.path.exists(usual_c4che_script))
1281
1282
1284
1286 """!
1287 Try to build the project
1288 @return None
1289 """
1290 return_code, stdout, stderr = run_ns3("build")
1291 self.assertEqual(return_code, 0)
1292 self.assertIn("Built target", stdout)
1293 for program in get_programs_list():
1294 self.assertTrue(os.path.exists(program))
1295 libraries = get_libraries_list()
1296 for module in get_enabled_modules():
1297 self.assertIn(module.replace("ns3-", ""), ";".join(libraries))
1298 self.assertIn(cmake_build_project_command, stdout)
1299
1301 """!
1302 Try to build and run test-runner
1303 @return None
1304 """
1305 return_code, stdout, stderr = run_ns3('run "test-runner --list" --verbose')
1306 self.assertEqual(return_code, 0)
1307 self.assertIn("Built target test-runner", stdout)
1308 self.assertIn(cmake_build_target_command(target="test-runner"), stdout)
1309
1311 """!
1312 Try to build and run a library
1313 @return None
1314 """
1315 return_code, stdout, stderr = run_ns3("run core") # this should not work
1316 self.assertEqual(return_code, 1)
1317 self.assertIn("Couldn't find the specified program: core", stderr)
1318
1320 """!
1321 Try to build and run an unknown target
1322 @return None
1323 """
1324 return_code, stdout, stderr = run_ns3("run nonsense") # this should not work
1325 self.assertEqual(return_code, 1)
1326 self.assertIn("Couldn't find the specified program: nonsense", stderr)
1327
1329 """!
1330 Try to run test-runner without building
1331 @return None
1332 """
1333 return_code, stdout, stderr = run_ns3('run "test-runner --list" --no-build --verbose')
1334 self.assertEqual(return_code, 0)
1335 self.assertNotIn("Built target test-runner", stdout)
1336 self.assertNotIn(cmake_build_target_command(target="test-runner"), stdout)
1337
1339 """!
1340 Test ns3 fails to run a library
1341 @return None
1342 """
1343 return_code, stdout, stderr = run_ns3("run core --no-build") # this should not work
1344 self.assertEqual(return_code, 1)
1345 self.assertIn("Couldn't find the specified program: core", stderr)
1346
1348 """!
1349 Test ns3 fails to run an unknown program
1350 @return None
1351 """
1352 return_code, stdout, stderr = run_ns3("run nonsense --no-build") # this should not work
1353 self.assertEqual(return_code, 1)
1354 self.assertIn("Couldn't find the specified program: nonsense", stderr)
1355
1357 """!
1358 Test if scratch simulator is executed through gdb
1359 @return None
1360 """
1361 return_code, stdout, stderr = run_ns3("run scratch-simulator --gdb --verbose --no-build")
1362 self.assertEqual(return_code, 0)
1363 self.assertIn("scratch-simulator", stdout)
1364 self.assertIn("No debugging symbols found", stdout)
1365
1367 """!
1368 Test if scratch simulator is executed through valgrind
1369 @return None
1370 """
1371 return_code, stdout, stderr = run_ns3("run scratch-simulator --valgrind --verbose --no-build")
1372 self.assertEqual(return_code, 0)
1373 self.assertIn("scratch-simulator", stderr)
1374 self.assertIn("Memcheck", stderr)
1375
1377 """!
1378 Test the doxygen target that does trigger a full build
1379 @return None
1380 """
1381 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
1382
1383 doxygen_files = ["introspected-command-line.h", "introspected-doxygen.h"]
1384 for filename in doxygen_files:
1385 file_path = os.sep.join([doc_folder, filename])
1386 if os.path.exists(file_path):
1387 os.remove(file_path)
1388
1389 # Rebuilding dot images is super slow, so not removing doxygen products
1390 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
1391 # if os.path.exists(doxygen_build_folder):
1392 # shutil.rmtree(doxygen_build_folder)
1393
1394 return_code, stdout, stderr = run_ns3("docs doxygen")
1395 self.assertEqual(return_code, 0)
1396 self.assertIn(cmake_build_target_command(target="doxygen"), stdout)
1397 self.assertIn("Built target doxygen", stdout)
1398
1400 """!
1401 Test the doxygen target that doesn't trigger a full build
1402 @return None
1403 """
1404 # Rebuilding dot images is super slow, so not removing doxygen products
1405 # doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
1406 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
1407 # if os.path.exists(doxygen_build_folder):
1408 # shutil.rmtree(doxygen_build_folder)
1409
1410 return_code, stdout, stderr = run_ns3("docs doxygen-no-build")
1411 self.assertEqual(return_code, 0)
1412 self.assertIn(cmake_build_target_command(target="doxygen-no-build"), stdout)
1413 self.assertIn("Built target doxygen-no-build", stdout)
1414
1416 """!
1417 Test every individual target for Sphinx-based documentation
1418 @return None
1419 """
1420 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
1421
1422 # First we need to clean old docs, or it will not make any sense.
1423 for target in ["manual", "models", "tutorial"]:
1424 doc_build_folder = os.sep.join([doc_folder, target, "build"])
1425 if os.path.exists(doc_build_folder):
1426 shutil.rmtree(doc_build_folder)
1427
1428 # For each sphinx doc target.
1429 for target in ["manual", "models", "tutorial"]:
1430 # Build
1431 return_code, stdout, stderr = run_ns3("docs %s" % target)
1432 self.assertEqual(return_code, 0)
1433 self.assertIn(cmake_build_target_command(target="sphinx_%s" % target), stdout)
1434 self.assertIn("Built target sphinx_%s" % target, stdout)
1435
1436 # Check if the docs output folder exists
1437 doc_build_folder = os.sep.join([doc_folder, target, "build"])
1438 self.assertTrue(os.path.exists(doc_build_folder))
1439
1440 # Check if the all the different types are in place (latex, split HTML and single page HTML)
1441 for build_type in ["latex", "html", "singlehtml"]:
1442 self.assertTrue(os.path.exists(os.sep.join([doc_build_folder, build_type])))
1443
1445 """!
1446 Test the documentation target that builds
1447 both doxygen and sphinx based documentation
1448 @return None
1449 """
1450 doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
1451
1452 # First we need to clean old docs, or it will not make any sense.
1453
1454 # Rebuilding dot images is super slow, so not removing doxygen products
1455 # doxygen_build_folder = os.sep.join([doc_folder, "html"])
1456 # if os.path.exists(doxygen_build_folder):
1457 # shutil.rmtree(doxygen_build_folder)
1458
1459 for target in ["manual", "models", "tutorial"]:
1460 doc_build_folder = os.sep.join([doc_folder, target, "build"])
1461 if os.path.exists(doc_build_folder):
1462 shutil.rmtree(doc_build_folder)
1463
1464 return_code, stdout, stderr = run_ns3("docs all")
1465 self.assertEqual(return_code, 0)
1466 self.assertIn(cmake_build_target_command(target="sphinx"), stdout)
1467 self.assertIn("Built target sphinx", stdout)
1468 self.assertIn(cmake_build_target_command(target="doxygen"), stdout)
1469 self.assertIn("Built target doxygen", stdout)
1470
1471 def test_14_Check(self):
1472 """!
1473 Test if ns3 --check is working as expected
1474 @return None
1475 """
1476 return_code, stdout, stderr = run_ns3("--check")
1477 self.assertEqual(return_code, 0)
1478
1480 """!
1481 Try to set ownership of scratch-simulator from current user to root,
1482 and change execution permissions
1483 @return None
1484 """
1485
1486 # Test will be skipped if not defined
1487 sudo_password = os.getenv("SUDO_PASSWORD", None)
1488
1489 # Skip test if variable containing sudo password is the default value
1490 if sudo_password is None:
1491 return
1492
1493 enable_sudo = read_c4che_entry("ENABLE_SUDO")
1494 self.assertFalse(enable_sudo is True)
1495
1496 # First we run to ensure the program was built
1497 return_code, stdout, stderr = run_ns3('run scratch-simulator')
1498 self.assertEqual(return_code, 0)
1499 self.assertIn("Built target scratch_scratch-simulator", stdout)
1500 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
1501 scratch_simulator_path = list(filter(lambda x: x if "scratch-simulator" in x else None,
1503 )
1504 )[-1]
1505 prev_fstat = os.stat(scratch_simulator_path) # we get the permissions before enabling sudo
1506
1507 # Now try setting the sudo bits from the run subparser
1508 return_code, stdout, stderr = run_ns3('run scratch-simulator --enable-sudo',
1509 env={"SUDO_PASSWORD": sudo_password})
1510 self.assertEqual(return_code, 0)
1511 self.assertIn("Built target scratch_scratch-simulator", stdout)
1512 self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
1513 fstat = os.stat(scratch_simulator_path)
1514
1515 import stat
1516 # If we are on Windows, these permissions mean absolutely nothing,
1517 # and on Fuse builds they might not make any sense, so we need to skip before failing
1518 likely_fuse_mount = ((prev_fstat.st_mode & stat.S_ISUID) == (fstat.st_mode & stat.S_ISUID)) and \
1519 prev_fstat.st_uid == 0
1520
1521 if sys.platform == "win32" or likely_fuse_mount:
1522 return
1523
1524 # If this is a valid platform, we can continue
1525 self.assertEqual(fstat.st_uid, 0) # check the file was correctly chown'ed by root
1526 self.assertEqual(fstat.st_mode & stat.S_ISUID, stat.S_ISUID) # check if normal users can run as sudo
1527
1528 # Now try setting the sudo bits as a post-build step (as set by configure subparser)
1529 return_code, stdout, stderr = run_ns3('configure --enable-sudo')
1530 self.assertEqual(return_code, 0)
1531
1532 # Check if it was properly set in the c4che file
1533 enable_sudo = read_c4che_entry("ENABLE_SUDO")
1534 self.assertTrue(enable_sudo)
1535
1536 # Remove old executables
1537 for executable in self.ns3_executablesns3_executables:
1538 if os.path.exists(executable):
1539 os.remove(executable)
1540
1541 # Try to build and then set sudo bits as a post-build step
1542 return_code, stdout, stderr = run_ns3('build', env={"SUDO_PASSWORD": sudo_password})
1543 self.assertEqual(return_code, 0)
1544
1545 # Check if commands are being printed for every target
1546 self.assertIn("chown root", stdout)
1547 self.assertIn("chmod u+s", stdout)
1548 for executable in self.ns3_executablesns3_executables:
1549 self.assertIn(os.path.basename(executable), stdout)
1550
1551 # Check scratch simulator yet again
1552 fstat = os.stat(scratch_simulator_path)
1553 self.assertEqual(fstat.st_uid, 0) # check the file was correctly chown'ed by root
1554 self.assertEqual(fstat.st_mode & stat.S_ISUID, stat.S_ISUID) # check if normal users can run as sudo
1555
1557 """!
1558 Check if command template is working
1559 @return None
1560 """
1561
1562 # Command templates that are empty or do not have a %s should fail
1563 return_code0, stdout0, stderr0 = run_ns3('run sample-simulator --command-template')
1564 self.assertEqual(return_code0, 2)
1565 self.assertIn("argument --command-template: expected one argument", stderr0)
1566
1567 return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template=" "')
1568 return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --command-template " "')
1569 return_code3, stdout3, stderr3 = run_ns3('run sample-simulator --command-template "echo "')
1570 self.assertEqual((return_code1, return_code2, return_code3), (1, 1, 1))
1571 self.assertIn("not all arguments converted during string formatting", stderr1)
1572 self.assertEqual(stderr1, stderr2)
1573 self.assertEqual(stderr2, stderr3)
1574
1575 # Command templates with %s should at least continue and try to run the target
1576 return_code4, stdout4, _ = run_ns3('run sample-simulator --command-template "%s --PrintVersion" --verbose')
1577 return_code5, stdout5, _ = run_ns3('run sample-simulator --command-template="%s --PrintVersion" --verbose')
1578 self.assertEqual((return_code4, return_code5), (0, 0))
1579 self.assertIn("sample-simulator --PrintVersion", stdout4)
1580 self.assertIn("sample-simulator --PrintVersion", stdout5)
1581
1583 """!
1584 Check if all flavors of different argument passing to
1585 executable targets are working
1586 @return None
1587 """
1588
1589 # Test if all argument passing flavors are working
1590 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --verbose')
1591 return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template="%s --help" --verbose')
1592 return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --verbose -- --help')
1593
1594 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
1595 self.assertIn("sample-simulator --help", stdout0)
1596 self.assertIn("sample-simulator --help", stdout1)
1597 self.assertIn("sample-simulator --help", stdout2)
1598
1599 # Test if the same thing happens with an additional run argument (e.g. --no-build)
1600 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --no-build')
1601 return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template="%s --help" --no-build')
1602 return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --no-build -- --help')
1603 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
1604 self.assertEqual(stdout0, stdout1)
1605 self.assertEqual(stdout1, stdout2)
1606 self.assertEqual(stderr0, stderr1)
1607 self.assertEqual(stderr1, stderr2)
1608
1609 # Now collect results for each argument individually
1610 return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --PrintGlobals" --verbose')
1611 return_code1, stdout1, stderr1 = run_ns3('run "sample-simulator --PrintGroups" --verbose')
1612 return_code2, stdout2, stderr2 = run_ns3('run "sample-simulator --PrintTypeIds" --verbose')
1613
1614 self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
1615 self.assertIn("sample-simulator --PrintGlobals", stdout0)
1616 self.assertIn("sample-simulator --PrintGroups", stdout1)
1617 self.assertIn("sample-simulator --PrintTypeIds", stdout2)
1618
1619 # Then check if all the arguments are correctly merged by checking the outputs
1620 cmd = 'run "sample-simulator --PrintGlobals" --command-template="%s --PrintGroups" --verbose -- --PrintTypeIds'
1621 return_code, stdout, stderr = run_ns3(cmd)
1622 self.assertEqual(return_code, 0)
1623
1624 # The order of the arguments is command template,
1625 # arguments passed with the target itself
1626 # and forwarded arguments after the -- separator
1627 self.assertIn("sample-simulator --PrintGroups --PrintGlobals --PrintTypeIds", stdout)
1628
1629 # Check if it complains about the missing -- separator
1630 cmd0 = 'run sample-simulator --command-template="%s " --PrintTypeIds'
1631 cmd1 = 'run sample-simulator --PrintTypeIds'
1632
1633 return_code0, stdout0, stderr0 = run_ns3(cmd0)
1634 return_code1, stdout1, stderr1 = run_ns3(cmd1)
1635 self.assertEqual((return_code0, return_code1), (1, 1))
1636 self.assertIn("To forward configuration or runtime options, put them after '--'", stderr0)
1637 self.assertIn("To forward configuration or runtime options, put them after '--'", stderr1)
1638
1639
1640if __name__ == '__main__':
1641 loader = unittest.TestLoader()
1642 suite = unittest.TestSuite()
1643
1644 # Put tests cases in order
1645 suite.addTests(loader.loadTestsFromTestCase(NS3RunWafTargets))
1646 suite.addTests(loader.loadTestsFromTestCase(NS3CommonSettingsTestCase))
1647 suite.addTests(loader.loadTestsFromTestCase(NS3ConfigureBuildProfileTestCase))
1648 suite.addTests(loader.loadTestsFromTestCase(NS3ConfigureTestCase))
1649 suite.addTests(loader.loadTestsFromTestCase(NS3BuildBaseTestCase))
1650 suite.addTests(loader.loadTestsFromTestCase(NS3ExpectedUseTestCase))
1651
1652 # Before running, check if ns3rc exists and save it
1653 ns3rc_script_bak = ns3rc_script + ".bak"
1654 if os.path.exists(ns3rc_script) and not os.path.exists(ns3rc_script_bak):
1655 shutil.move(ns3rc_script, ns3rc_script_bak)
1656
1657 # Run tests and fail as fast as possible
1658 runner = unittest.TextTestRunner(failfast=True)
1659 result = runner.run(suite)
1660
1661 # After completing the tests successfully, restore the ns3rc file
1662 if os.path.exists(ns3rc_script_bak):
1663 shutil.move(ns3rc_script_bak, ns3rc_script)
#define max(a, b)
Definition: 80211b.c:43
Generic test case with basic function inherited by more complex tests.
Definition: test-ns3.py:412
def config_ok(self, return_code, stdout)
Check if configuration for release mode worked normally.
Definition: test-ns3.py:420
ns3_executables
ns3_executables holds a list of executables in build-status.py
Definition: test-ns3.py:453
def setUp(self)
Clean configuration/build artifacts before testing configuration and build settings After configuring...
Definition: test-ns3.py:431
ns3_modules
ns3_modules holds a list to the modules enabled stored in c4che.py
Definition: test-ns3.py:458
Tests ns3 regarding building the project.
Definition: test-ns3.py:895
def test_06_TestVersionFile(self)
Test if changing the version file affects the library names.
Definition: test-ns3.py:975
def test_01_BuildExistingTargets(self)
Try building the core library.
Definition: test-ns3.py:915
def test_08_InstallationAndUninstallation(self)
Tries setting a ns3 version, then installing it.
Definition: test-ns3.py:1087
def setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned.
Definition: test-ns3.py:903
def test_02_BuildNonExistingTargets(self)
Try building core-test library without tests enabled.
Definition: test-ns3.py:924
def test_04_BuildProjectNoTaskLines(self)
Try hiding task lines.
Definition: test-ns3.py:945
def test_03_BuildProject(self)
Try building the project:
Definition: test-ns3.py:933
def test_09_Scratches(self)
Tries to build scratch-simulator and subdir/scratch-simulator-subdir.
Definition: test-ns3.py:1223
def test_05_BreakBuild(self)
Try removing an essential file to break the build.
Definition: test-ns3.py:954
ns3_executables
ns3_executables holds a list of executables in build-status.py
Definition: test-ns3.py:1032
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:1019
ns3_libraries
ns3_libraries holds a list of built module libraries
Definition: test-ns3.py:913
ns3 tests related to generic options
Definition: test-ns3.py:271
def test_05_CheckVersion(self)
Test only passing –check-version argument to ns3.
Definition: test-ns3.py:321
def setUp(self)
Clean configuration/build artifacts before common commands.
Definition: test-ns3.py:276
def test_01_NoOption(self)
Test not passing any arguments to.
Definition: test-ns3.py:285
def test_02_NoTaskLines(self)
Test only passing –quiet argument to ns3.
Definition: test-ns3.py:294
def test_03_CheckConfig(self)
Test only passing –check-config argument to ns3.
Definition: test-ns3.py:303
def test_04_CheckProfile(self)
Test only passing –check-profile argument to ns3.
Definition: test-ns3.py:312
ns3 tests related to build profiles
Definition: test-ns3.py:331
def test_05_TYPO(self)
Test a build type with another typo.
Definition: test-ns3.py:402
def test_02_Release(self)
Test the release build.
Definition: test-ns3.py:364
def test_01_Debug(self)
Test the debug build.
Definition: test-ns3.py:345
def setUp(self)
Clean configuration/build artifacts before testing configuration settings.
Definition: test-ns3.py:336
def test_03_Optimized(self)
Test the optimized build.
Definition: test-ns3.py:374
def test_04_Typo(self)
Test a build type with a typo.
Definition: test-ns3.py:393
Test ns3 configuration options.
Definition: test-ns3.py:461
def setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned.
Definition: test-ns3.py:469
def test_06_DisableModulesComma(self)
Test disabling comma-separated (waf-style) examples.
Definition: test-ns3.py:593
def test_04_DisableModules(self)
Test disabling specific modules.
Definition: test-ns3.py:549
def test_03_EnableModules(self)
Test enabling specific modules.
Definition: test-ns3.py:527
def test_02_Tests(self)
Test enabling and disabling tests.
Definition: test-ns3.py:501
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:729
def test_12_CheckVersion(self)
Test passing –check-version argument to ns3 to get the build version.
Definition: test-ns3.py:773
def test_05_EnableModulesComma(self)
Test enabling comma-separated (waf-style) examples.
Definition: test-ns3.py:571
def test_01_Examples(self)
Test enabling and disabling examples.
Definition: test-ns3.py:479
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:853
def test_07_Ns3rc(self)
Test loading settings from the ns3rc config file.
Definition: test-ns3.py:615
def test_13_Scratches(self)
Test if CMake target names for scratches and ns3 shortcuts are working correctly.
Definition: test-ns3.py:782
def test_08_DryRun(self)
Test dry-run (printing commands to be executed instead of running them)
Definition: test-ns3.py:677
def test_10_CheckConfig(self)
Test passing –check-config argument to ns3 to get the configuration table.
Definition: test-ns3.py:755
def test_11_CheckProfile(self)
Test passing –check-profile argument to ns3 to get the build profile.
Definition: test-ns3.py:764
Tests ns3 usage in more realistic scenarios.
Definition: test-ns3.py:1250
def test_10_DoxygenWithBuild(self)
Test the doxygen target that does trigger a full build.
Definition: test-ns3.py:1376
def test_02_BuildAndRunExistingExecutableTarget(self)
Try to build and run test-runner.
Definition: test-ns3.py:1300
def test_08_RunNoBuildGdb(self)
Test if scratch simulator is executed through gdb.
Definition: test-ns3.py:1356
def test_16_CommandTemplate(self)
Check if command template is working.
Definition: test-ns3.py:1556
def test_17_ForwardArgumentsToRunTargets(self)
Check if all flavors of different argument passing to executable targets are working.
Definition: test-ns3.py:1582
def test_05_RunNoBuildExistingExecutableTarget(self)
Try to run test-runner without building.
Definition: test-ns3.py:1328
def test_06_RunNoBuildExistingLibraryTarget(self)
Test ns3 fails to run a library.
Definition: test-ns3.py:1338
def test_03_BuildAndRunExistingLibraryTarget(self)
Try to build and run a library.
Definition: test-ns3.py:1310
def test_01_BuildProject(self)
Try to build the project.
Definition: test-ns3.py:1285
ns3_modules
ns3_modules holds a list to the modules enabled stored in c4che.py
Definition: test-ns3.py:1283
def test_04_BuildAndRunNonExistingTarget(self)
Try to build and run an unknown target.
Definition: test-ns3.py:1319
def test_07_RunNoBuildNonExistingExecutableTarget(self)
Test ns3 fails to run an unknown program.
Definition: test-ns3.py:1347
def test_15_EnableSudo(self)
Try to set ownership of scratch-simulator from current user to root, and change execution permissions...
Definition: test-ns3.py:1479
ns3_executables
ns3_executables holds a list of executables in build-status.py
Definition: test-ns3.py:1277
def test_09_RunNoBuildValgrind(self)
Test if scratch simulator is executed through valgrind.
Definition: test-ns3.py:1366
def test_13_Documentation(self)
Test the documentation target that builds both doxygen and sphinx based documentation.
Definition: test-ns3.py:1444
def setUp(self)
Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned Here examples,...
Definition: test-ns3.py:1258
def test_11_DoxygenWithoutBuild(self)
Test the doxygen target that doesn't trigger a full build.
Definition: test-ns3.py:1399
def test_14_Check(self)
Test if ns3 –check is working as expected.
Definition: test-ns3.py:1471
def test_12_SphinxDocumentation(self)
Test every individual target for Sphinx-based documentation.
Definition: test-ns3.py:1415
ns3 tests related to compatibility with Waf-produced binaries
Definition: test-ns3.py:168
def test_01_loadExecutables(self)
Try to load executables built by waf.
Definition: test-ns3.py:192
def test_03_runNobuildScratchSim(self)
Try to run an executable built by waf.
Definition: test-ns3.py:214
def test_06_runTestCaseExamplesAsTestsTestSuite(self)
Try to run a different test case built by waf that calls the ns3 wrapper script.
Definition: test-ns3.py:241
def test_05_runTestCaseCoreExampleSimulator(self)
Try to run a test case built by waf that calls the ns3 wrapper script.
Definition: test-ns3.py:232
def setUp(self)
Clean the default build directory, then configure and build ns-3 with waf.
Definition: test-ns3.py:176
ns3_modules
ns3_modules holds a list to the modules enabled stored in c4che.py
Definition: test-ns3.py:211
ns3_executables
ns3_executables holds a list of executables in build-status.py
Definition: test-ns3.py:200
def test_07_runCoreExampleSimulator(self)
Try to run test cases built by waf that calls the ns3 wrapper script when the output directory is set...
Definition: test-ns3.py:250
def test_04_runNobuildExample(self)
Try to run a different executable built by waf.
Definition: test-ns3.py:223
def test_02_loadModules(self)
Try to load modules built by waf.
Definition: test-ns3.py:203
string project
Definition: conf.py:43
def read_c4che_entry(entry, c4che_script_path=usual_c4che_script)
Read interesting entries from the c4che/_cache.py file.
Definition: test-ns3.py:139
def get_libraries_list(lib_outdir=usual_lib_outdir)
Gets a list of built libraries.
Definition: test-ns3.py:121
def get_test_enabled()
Check if tests are enabled in the c4che/_cache.py.
Definition: test-ns3.py:152
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:130
def get_programs_list(build_status_script_path=usual_build_status_script)
Extracts the programs list from build-status.py.
Definition: test-ns3.py:109
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:64
def get_enabled_modules()
Definition: test-ns3.py:160
def run_ns3(args, env=None)
Runs the ns3 wrapper script with arguments.
Definition: test-ns3.py:53
def replace(pre, post, main_cmd_list)
Definition: test-waf.py:49
#define list