1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/doc/testing/Makefile Sat Sep 12 19:44:17 2009 -0700
1.3 @@ -0,0 +1,44 @@
1.4 +TEXI2HTML = texi2html
1.5 +TEXI2PDF = texi2dvi --pdf
1.6 +EPSTOPDF = epstopdf
1.7 +DIA = dia
1.8 +CONVERT = convert
1.9 +CSS = --css-include=testing.css
1.10 +SPLIT = --split section
1.11 +
1.12 +FIGURES = figures
1.13 +VPATH = $(FIGURES)
1.14 +
1.15 +IMAGES_EPS = \
1.16 +
1.17 +IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
1.18 +IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
1.19 +
1.20 +IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
1.21 +
1.22 +CHAPTERS = \
1.23 + testing.texi \
1.24 + overview.texi \
1.25 + propagation-loss.texi \
1.26 +
1.27 +%.eps : %.dia; $(DIA) -t eps $< -e $@
1.28 +%.png : %.dia; $(DIA) -t png $< -e $@
1.29 +%.pdf : %.eps; $(EPSTOPDF) $< -o=$@
1.30 +
1.31 +all: $(IMAGES) testing.pdf testing.html testing/testing.html
1.32 +
1.33 +testing.pdf: $(IMAGES) $(CHAPTERS)
1.34 + $(TEXI2PDF) testing.texi
1.35 +
1.36 +testing.html: $(IMAGES) $(CHAPTERS)
1.37 + $(TEXI2HTML) ${CSS} testing.texi
1.38 +
1.39 +testing/testing.html: $(IMAGES) $(CHAPTERS)
1.40 + $(TEXI2HTML) ${CSS} ${SPLIT} testing.texi
1.41 +
1.42 +figures-clean:
1.43 + rm -rf $(IMAGES)
1.44 +
1.45 +clean: figures-clean
1.46 + rm -rf testing.aux testing.cp testing.cps testing.fn testing.ky testing.pg
1.47 + rm -rf testing.tp testing.vr testing.toc testing.log testing.pdf testing.html testing/
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/doc/testing/background.texi Sat Sep 12 19:44:17 2009 -0700
2.3 @@ -0,0 +1,215 @@
2.4 +@c ========================================================================
2.5 +@c Background
2.6 +@c ========================================================================
2.7 +
2.8 +@node Background
2.9 +@chapter Background
2.10 +
2.11 +Writing defect-free software is a difficult proposition. There are many
2.12 +dimensions to the problem and there is much confusion regarding what is
2.13 +meant by different terms in different contexts. We have found it worthwhile
2.14 +to spend a little time reviewing the subject and defining some terms.
2.15 +
2.16 +Software testing may be loosely defined as the process of executing a program
2.17 +with the intent of finding errors. When one enters a discussion regarding
2.18 +software testing, it quickly becomes apparent that there are many distinct
2.19 +mind-sets with which one can approach the subject.
2.20 +
2.21 +For example, one could break the process into broad functional categories
2.22 +like ``correctness testing,'' ``performance testing,'' ``robustness testing''
2.23 +and ``security testing.'' Another way to look at the problem is by life-cycle:
2.24 +``requirements testing,'' ``design testing,'' ``acceptance testing,'' and
2.25 +``maintenance testing.'' Yet another view is by the scope of the tested system.
2.26 +In this case one may speak of ``unit testing,'' ``component testing,''
2.27 +``integration testing,'' and ``system testing.'' These terms are also not
2.28 +standardized in any way, and so ``maintenance testing'' and ``regression
2.29 +testing'' may be heard interchangeably. Additionally, these terms are often
2.30 +misused.
2.31 +
2.32 +There are also a number of different philosophical approaches to software
2.33 +testing. For example, some organizations advocate writing test programs
2.34 +before actually imlementing the desired software, yielding ``test-driven
2.35 +development.'' Some organizations advocate testing from a customer perspective
2.36 +as soon as possible, following a parallel with the agile development process:
2.37 +``test early and test often.'' This is sometimes called ``agile testing.'' It
2.38 +seems that there is at least one approach to testing for every development
2.39 +methodology.
2.40 +
2.41 +The @command{ns-3} project is not in the business of advocating for any one of
2.42 +these processes, but the project as a whole has requirements that help inform
2.43 +the test process.
2.44 +
2.45 +Like all major software products, @command{ns-3} has a number of qualities that
2.46 +must be present for the product to succeed. From a testing perspective, some
2.47 +of these qualities that must be addressed are that @command{ns-3} must be
2.48 +``correct,'' ``robust,'' ``performant'' and ``maintainable.'' Ideally there
2.49 +should be metrics for each of these dimensions that are checked by the tests
2.50 +to identify when the product fails to meed its expectations / requirements.
2.51 +
2.52 +@node Correctness
2.53 +@section Correctness
2.54 +
2.55 +The essential purpose of testing is to determine that a piece of software
2.56 +behaves ``correctly.'' For @command{ns-3} this means that if we simulate
2.57 +something, the simulation should faithfully represent some physical entity or
2.58 +process to a specified accuracy and precision.
2.59 +
2.60 +It turns out that there are two perspectives from which one can view
2.61 +correctness. Verifying that a particular process is implemented according
2.62 +to its specification is generically called verification. The process of
2.63 +deciding that the specification is correct is generically called validation.
2.64 +
2.65 +@node ValidationAndVerification
2.66 +@section Validation and Verification
2.67 +
2.68 +A computer model is a mathematical or logical representation of something. It
2.69 +can represent a vehicle, a frog or a networking card. Models can also represent
2.70 +processes such as global warming, freeway traffic flow or a specification of a
2.71 +networking protocol. Models can be completely faithful representations of a
2.72 +logical process specification, but they necessarily can never completely
2.73 +simulate a physical object or process. In most cases, a number of
2.74 +simplifications are made to the model to make simulation computationally
2.75 +tractable.
2.76 +
2.77 +Every model has a @emph{target system} that it is attempting to simulate. The
2.78 +first step in creating a simulation model is to identify this target system and
2.79 +the level of detail and accuracy that the simulation is desired to reproduce.
2.80 +In the case of a logical process, the target system may be identified as ``TCP
2.81 +as defined by RFC 793.'' In this case, it will probably be desirable to create
2.82 +a model that completely and faithfully reproduces RFC 793. In the case of a
2.83 +physical process this will not be possible. If, for example, you would like to
2.84 +simulate a wireless networking card, you may determine that you need, ``an
2.85 +accurate MAC-level implementation of the 802.11 specification and [...] a
2.86 +not-so-slow PHY-level model of the 802.11a specification.''
2.87 +
2.88 +Once this is done, one can develop an abstract model of the target system. This
2.89 +is typically an exercise in managing the tradeoffs between complexity, resource
2.90 +requiremens and accuracy. The process of developing an abstract model has been
2.91 +called @emph{model qualification} in the literature. In the case of a TCP
2.92 +protocol, this process results in a design for a collection of objects,
2.93 +interactions and behaviors that will fully implement RFC 793 in @command{ns-3}.
2.94 +In the case of the wireless card, this process results in a number of tradeoffs
2.95 +to allow the physical layer to be simulated and the design of a network device
2.96 +and channel for ns-3, along with the desired objects, interactions and behaviors.
2.97 +
2.98 +This abstract model is then developed into an @command{ns-3} model that
2.99 +implements the abstract model as a computer program. The process of getting the
2.100 +implementation to agree with the abstract model is called @emph{model
2.101 +verification} in the literature.
2.102 +
2.103 +The process so far is open loop. What remains is to make a determination that a
2.104 +given ns-3 model has some connection to some reality -- that a model is an
2.105 +accurate representation of a real system, whether a logical process or a physical
2.106 +entity.
2.107 +
2.108 +If one is going to use a simulation model to try and predict how some real
2.109 +system is going to behave, there must be some reason to believe your results --
2.110 +i.e., can one trust that an inference made from the model translates into a
2.111 +correct prediction for the real system. The process of getting the ns-3 model
2.112 +behavior to agree with the desired target system behavior as defined by the model
2.113 +qualification process is called @emph{model validation} in the literature. In the
2.114 +case of a TCP implementation, you may want to compare the behavior of your ns-3
2.115 +TCP model to some reference implementation in order to validate your model. In
2.116 +the case of a wireless physical layer simulation, you may want to compare the
2.117 +behavior of your model to that of real hardware in a controlled setting,
2.118 +
2.119 +The @command{ns-3} testing environment provides tools to allow for both model
2.120 +validation and testing, and encourages the publication of validation results.
2.121 +
2.122 +@node Robustness
2.123 +@section Robustness
2.124 +
2.125 +Robustness is the quality of being able to withstand stresses, or changes in
2.126 +environments, inputs or calculations, etc. A system or design is ``robust''
2.127 +if it can deal with such changes with minimal loss of functionality.
2.128 +
2.129 +This kind of testing is usually done with a particular focus. For example, the
2.130 +system as a whole can be run on many different system configurations to
2.131 +demonstrate that it can perform correctly in a large number of environments.
2.132 +
2.133 +The system can be also be stressed by operating close to or beyond capacity by
2.134 +generating or simulating resource exhaustion of various kinds. This genre of
2.135 +testing is called ``stress testing.''
2.136 +
2.137 +The system and its components may be exposed to so-called ``clean tests'' that
2.138 +demostrate a positive result -- that is that the system operates correctly in
2.139 +response to a large variation of expected configurations.
2.140 +
2.141 +The system and its components may also be exposed to ``dirty tests'' which
2.142 +provide inputs outside the expected range. For example, if a module expects a
2.143 +zero-terminated string representation of an integer, a dirty test might provide
2.144 +an unterminated string of random characters to verify that the system does not
2.145 +crash as a result of this unexpected input. Unfortunately, detecting such
2.146 +``dirty'' input and taking preventive measures to ensure the system does not
2.147 +fail catasrophically can require a huge amount of development overhead. In
2.148 +order to reduce development time, a decision was taken early on in the project
2.149 +to minimize the amount of parameter validation and error handling in the
2.150 +@command{ns-3} codebase. For this reason, we do not spend much time on dirty
2.151 +testing -- it would just uncover the results of the design decision we know
2.152 +we took.
2.153 +
2.154 +We do want to deonstrate that @command{ns-3} software does work across some set
2.155 +of conditions. We borrow a couple of definitions to narrow this down a bit.
2.156 +The @emph{domain of applicability} is a set of prescribed conditions for which
2.157 +the model has been tested, compared against reality to the extent possible, and
2.158 +judged suitable for use. The @emph{range of accuracy} is an agreement between
2.159 +the computerized model and reality within a domain of applicability.
2.160 +
2.161 +The @command{ns-3} testing environment provides tools to allow for setting up
2.162 +and running test environments over multiple systems (buildbot) and provides
2.163 +classes to encourage clean tests to verify the operation of the system over the
2.164 +expected ``domain of applicability'' and ``range of accuraccy.''
2.165 +
2.166 +@node Performant
2.167 +@section Performant
2.168 +
2.169 +Okay, ``performant'' isn't a real English word. It is, however, a very concise
2.170 +neologism that is quite often used to describe what we want @command{ns-3} to
2.171 +be: powerful and fast enough to get the job done.
2.172 +
2.173 +This is really about the broad subject of software performance testing. One of
2.174 +the key things that is done is to compare two systems to find which performs
2.175 +better (cf benchmarks). This is used to demonstrate that, for example,
2.176 +@code{ns-3} can perform a basic kind of simulation at least as fast as a
2.177 +competing product, or can be used to identify parts of the system that perform
2.178 +badly.
2.179 +
2.180 +In the @code{ns-3} test framework, we provide support for timing various kinds
2.181 +of tests.
2.182 +
2.183 +@node Maintainability
2.184 +@section Maintainability
2.185 +
2.186 +A software product must be maintainable. This is, again, a very broad
2.187 +statement, but a testing framework can help with the task. Once a model has
2.188 +been developed, validated and verified, we can repeatedly execute the suite
2.189 +of tests for the entire system to ensure that it remains valid and verified
2.190 +over its lifetime.
2.191 +
2.192 +When a feature stops functioning as intended after some kind of change to the
2.193 +system is integrated, it is called generically a regression. Originally the
2.194 +term regression referred to a change that caused a previously fixed bug to
2.195 +reappear, but the term has evolved to describe any kind of change that breaks
2.196 +existing functionality. There are many kinds of regressions that may occur
2.197 +in practice.
2.198 +
2.199 +A @emph{local regression} is one in which a change affects the changed component
2.200 +directy. For example, if a component is modified to allocate and free memory
2.201 +but stale pointers are used, the component itself fails.
2.202 +
2.203 +A @emph{remote regression} is one in which a change to one component breaks
2.204 +functionality in another component. This reflects violation of an implied but
2.205 +possibly unrecognized contract between components.
2.206 +
2.207 +An @emph{unmasked regression} is one that creates a situation where a previously
2.208 +existing bug that had no affect is suddenly exposed in the system. This may
2.209 +be as simple as exercising a code path for the first time.
2.210 +
2.211 +A @emph{performance regression} is one that causes the performance requirements
2.212 +of the system to be violated. For example, doing some work in a low level
2.213 +function that may be repeated large numbers of times may suddenly render the
2.214 +system unusable from certain perspectives.
2.215 +
2.216 +The @command{ns-3} testing framework provides tools for automating the process
2.217 +used to validate and verify the code in nightly test suites to help quickly
2.218 +identify possible regressions.
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/doc/testing/how-to-write-tests.texi Sat Sep 12 19:44:17 2009 -0700
3.3 @@ -0,0 +1,8 @@
3.4 +@c ========================================================================
3.5 +@c How to write tests
3.6 +@c ========================================================================
3.7 +
3.8 +@node How to write tests
3.9 +@chapter How to write tests
3.10 +
3.11 +To be completed.
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/doc/testing/overview.texi Sat Sep 12 19:44:17 2009 -0700
4.3 @@ -0,0 +1,16 @@
4.4 +@c ========================================================================
4.5 +@c Overview
4.6 +@c ========================================================================
4.7 +
4.8 +@node Overview
4.9 +@chapter Overview
4.10 +
4.11 +This document is concerned with the testing and validation of @command{ns-3}
4.12 +software.
4.13 +
4.14 +This document provides
4.15 +@itemize @bullet
4.16 +@item a description of the ns-3 testing framework;
4.17 +@item a guide to model developers or new model contributors for how to write tests;
4.18 +@item validation and verification results reported to date.
4.19 +@end itemize
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/doc/testing/propagation-loss.texi Sat Sep 12 19:44:17 2009 -0700
5.3 @@ -0,0 +1,121 @@
5.4 +@node Propagation Loss Models
5.5 +@chapter Propagation Loss Models
5.6 +@anchor{chap:propagation-loss-models}
5.7 +
5.8 +This chapter describes validation of ns-3 propagation loss models.
5.9 +
5.10 +@section FriisPropagationLossModel
5.11 +
5.12 +@subsection Model reference
5.13 +
5.14 +From source: @uref{http://www.scribd.com/doc/6650712/Wireless-CommunicationsPrinciples-and-Practice-Theodore-S,, Wireless Communications-Principles and Practice ,Theodore S Rappaport pg. 71 }
5.15 +
5.16 +Given equation:
5.17 +@verbatim
5.18 +Pr = Pt*Gt*Gr*lmb^2/((4*pi)^2*d^2*L)
5.19 +
5.20 +Pt = 10^(17.0206/10)/10^3 = .05035702
5.21 +Pr = .05035702*.125^2/((4*pi)^2*d*1) = 4.98265e-6/d^2
5.22 +
5.23 +bandwidth = 2.2*10^7
5.24 +m_noiseFigure = 5.01187
5.25 +noiseFloor = ((Thermal noise (K)* BOLTZMANN * bandwidth)* m_noiseFigure)
5.26 +noiseFloor = ((290*1.3803*10^-23*2.2*10^7)*5.01187) = 4.41361e-13W
5.27 +no interference, so SNR = Pr/4.41361e-13W
5.28 +
5.29 +Distance :: Pr :: SNR
5.30 +100 4.98265e-10W 1128.93
5.31 +500 1.99306e-11W 45.1571
5.32 +1000 4.98265e-12W 11.2893
5.33 +2000 1.24566e-12W 2.82232
5.34 +3000 5.53628e-13W 1.25436
5.35 +4000 3.11416e-13W 0.70558
5.36 +5000 1.99306e-13W 0.451571
5.37 +6000 1.38407e-13W 0.313591
5.38 +@end verbatim
5.39 +
5.40 +@subsection Validation test
5.41 +
5.42 +Test program available online at: @uref{http://xxx.xxx.com,,}
5.43 +
5.44 +Taken at default settings (packetSize = 1000, numPackets = 1, lambda = 0.125, 802.11b at 2.4GHz):
5.45 +@verbatim
5.46 +Distance :: Pr :: SNR
5.47 +100 4.98265e-10W 1128.93
5.48 +500 1.99306e-11W 45.1571
5.49 +1000 4.98265e-12W 11.2893
5.50 +2000 1.24566e-12W 2.82232
5.51 +3000 5.53628e-13W 1.25436
5.52 +4000 3.11416e-13W 0.70558
5.53 +5000 1.99306e-13W 0.451571
5.54 +6000 1.38407e-13W 0.313591
5.55 +7000 1.01687e-13W 0.230393
5.56 +8000 7.78539e-14W 0.176395
5.57 +@end verbatim
5.58 +
5.59 +@subsection Discussion
5.60 +
5.61 +As can be seen, the SNR outputted from the simulator, and the SNR computed from the source's equation are identical.
5.62 +
5.63 +@section LogDistancePropagationLossModel
5.64 +
5.65 +@subsection Model reference
5.66 +
5.67 +From source: @uref{http://www.plextek.co.uk/papers/aps2005mcw.pdf,, Urban Propagation Measurements and Statistical Path Loss Model at 3.5 GHz, Marcus C. Walden, Frank J. Rowsell}
5.68 +
5.69 +Given equation:
5.70 +@verbatim
5.71 +PL{dBm} = PL(d0) + 10*n*log(d/d0) + Xs
5.72 +
5.73 +PL(1) from friis at 2.4GHz: 40.045997dBm
5.74 +PL{dBm} = 10*log(.050357/Pr) = 40.045997 + 10*n*log(d) + Xg
5.75 +Pr = .050357/(10^((40.045997 + 10*n*log(d) + Xg)/10))
5.76 +
5.77 +bandwidth = 2.2*10^7
5.78 +m_noiseFigure = 5.01187
5.79 +no interference, so SNR = Pr/4.41361e-13W
5.80 +@end verbatim
5.81 +
5.82 +taking Xg to be constant at 0 to match ns-3 output:
5.83 +@verbatim
5.84 +Distance :: Pr :: SNR
5.85 +10 4.98265e-9 11289.3
5.86 +20 6.22831e-10 1411.16
5.87 +40 7.78539e-11 176.407
5.88 +60 2.30678e-11 52.2652
5.89 +80 9.73173e-12 22.0494
5.90 +100 4.98265e-12 11.2893
5.91 +200 6.22831e-13 1.41116
5.92 +500 3.98612e-14 .090314
5.93 +1000 4.98265e-15 .011289
5.94 +@end verbatim
5.95 +
5.96 +@subsection Validation test
5.97 +
5.98 +Test program available online at: @uref{http://xxx.xxx.com,,}
5.99 +
5.100 +Taken at default settings (packetSize = 1000, numPackets = 1, exponent = 3, reference loss = 46.6777, 802.11b at 2.4GHz)
5.101 +@verbatim
5.102 +Distance :: Pr :: snr
5.103 +10 4.98471e-9 11293.9
5.104 +20 6.23089e-10 1411.74
5.105 +40 7.78861e-11 176.468
5.106 +60 2.30774e-11 52.2868
5.107 +80 9.72576e-12 22.0585
5.108 +100 4.98471e-12 11.2939
5.109 +200 6.23089e-13 1.41174
5.110 +500 3.98777e-14 0.0903516
5.111 +1000 4.98471e-15 0.0112939
5.112 +@end verbatim
5.113 +
5.114 +
5.115 +@subsection Discussion
5.116 +There is a ~.04% error between these results. I do not believe this is
5.117 +due to rounding, as the results taken from the equation from the source
5.118 +match exactly with the Friis results taken at one less power of ten.
5.119 +(Friis and LogDistance can be modeled by Pt*Gt*Gr*lmb^2/((4*pi)^2*d^n*L),
5.120 +where n is the exponent. n is 2 for Friis, and 3 for logDistance, which
5.121 +accounts for the power of ten. ie: Friis at 100m is equivalent to LogDistance
5.122 +at 10m.) Perhaps the ns-3 takes the random number into account despite
5.123 +not being listed in the source.
5.124 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/doc/testing/testing-framework.texi Sat Sep 12 19:44:17 2009 -0700
6.3 @@ -0,0 +1,567 @@
6.4 +@c ========================================================================
6.5 +@c Testing framework
6.6 +@c ========================================================================
6.7 +
6.8 +@node TestingFramework
6.9 +@chapter Testing Framework
6.10 +
6.11 +@node BuildBots
6.12 +@section Buildbots
6.13 +
6.14 +The @command{ns-3} testing framework is composed of several major pieces. At
6.15 +the highest level are the buildbots (build robots). If you are unfamiliar with
6.16 +this system look at @uref{http://djmitche.github.com/buildbot/docs/0.7.11/}.
6.17 +This is an open-source automated system that allows @command{ns-3} to be rebuilt
6.18 +and tested each time something has changed. By running the buildbots on a number
6.19 +of different systems we can ensure that @command{ns-3} builds and executes
6.20 +properly on all of its supported systems.
6.21 +
6.22 +Users (and developers) typically will not interact with the buildbot system other
6.23 +than to read its messages regarding test results. If a failure is detected in
6.24 +one of the automated build and test jobs, the buildbot will send an email to the
6.25 +@emph{ns-developers} mailing list. This email will look something like:
6.26 +
6.27 +@verbatim
6.28 + The Buildbot has detected a new failure of osx-ppc-g++-4.2 on NsNam.
6.29 + Full details are available at:
6.30 + http://ns-regression.ee.washington.edu:8010/builders/osx-ppc-g%2B%2B-4.2/builds/0
6.31 +
6.32 + Buildbot URL: http://ns-regression.ee.washington.edu:8010/
6.33 +
6.34 + Buildslave for this Build: darwin-ppc
6.35 +
6.36 + Build Reason: The web-page 'force build' button was pressed by 'ww': ww
6.37 +
6.38 + Build Source Stamp: HEAD
6.39 + Blamelist:
6.40 +
6.41 + BUILD FAILED: failed shell_5 shell_6 shell_7 shell_8 shell_9 shell_10 shell_11 shell_12 shell_13 shell_14 shell_15
6.42 +
6.43 + sincerely,
6.44 + -The Buildbot
6.45 +@end verbatim
6.46 +
6.47 +In the full details URL shown in the email, one can search for the keyword
6.48 +@code{failed} and select the @code{stdio} link for the corresponding step to see
6.49 +the reason for the failure.
6.50 +
6.51 +The buildbot will do its job quietly if there are no errors, and the system will
6.52 +undergo build and test cycles every day to verify that all is well.
6.53 +
6.54 +@node Testpy
6.55 +@section Test.py
6.56 +The buildbots use a Python program, @command{test.py}, that is reponsible for
6.57 +running all of the tests and collecting the resulting reports into a human-
6.58 +readable form. This program is also available for use by users and developers
6.59 +as well.
6.60 +
6.61 +@command{test.py} is very flexible in allowing the user to specify the number
6.62 +and kind of tests to run; and also the amount and kind of output to generate.
6.63 +
6.64 +By default, @command{test.py} will run all available tests and report status
6.65 +back in a very concise form. Running the command,
6.66 +
6.67 +@verbatim
6.68 + ./test.py
6.69 +@end verbatim
6.70 +
6.71 +will result in a number of @code{PASS}, @code{FAIL}, @code{CRASH} or @code{SKIP}
6.72 +indications followed by the kind of test that was run and its display name.
6.73 +
6.74 +@verbatim
6.75 + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.76 + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.77 + 'build' finished successfully (0.939s)
6.78 + FAIL: TestSuite ns3-wifi-propagation-loss-models
6.79 + PASS: TestSuite object-name-service
6.80 + PASS: TestSuite pcap-file-object
6.81 + PASS: TestSuite ns3-tcp-cwnd
6.82 + ...
6.83 +
6.84 + PASS: TestSuite ns3-tcp-interoperability
6.85 + PASS: Example csma-broadcast
6.86 + PASS: Example csma-multicast
6.87 +@end verbatim
6.88 +
6.89 +This mode is indented to be used by users who are interested in determining if
6.90 +their distribution is working correctly, and by developers who are interested
6.91 +in determining if changes they have made have caused any regressions.
6.92 +
6.93 +If one specifies an optional output style, one can generate detailed descriptions
6.94 +of the tests and status. Available styles are @command{text} and @command{HTML}.
6.95 +The buildbots will select the HTML option to generate HTML test reports for the
6.96 +nightly builds using,
6.97 +
6.98 +@verbatim
6.99 + ./test.py --html=nightly.html
6.100 +@end verbatim
6.101 +
6.102 +In this case, an HTML file named ``nightly.html'' would be created with a pretty
6.103 +summary of the testing done. A ``human readable'' format is available for users
6.104 +interested in the details.
6.105 +
6.106 +@verbatim
6.107 + ./test.py --text=results.txt
6.108 +@end verbatim
6.109 +
6.110 +In the example above, the test suite checking the @command{ns-3} wireless
6.111 +device propagation loss models failed. By default no further information is
6.112 +provided.
6.113 +
6.114 +To further explore the failure, @command{test.py} allows a single test suite
6.115 +to be specified. Running the command,
6.116 +
6.117 +@verbatim
6.118 + ./test.py --suite=ns3-wifi-propagation-loss-models
6.119 +@end verbatim
6.120 +
6.121 +results in that single test suite being run.
6.122 +
6.123 +@verbatim
6.124 + FAIL: TestSuite ns3-wifi-propagation-loss-models
6.125 +@end verbatim
6.126 +
6.127 +To find detailed information regarding the failure, one must specify the kind
6.128 +of output desired. For example, most people will probably be interested in
6.129 +a text file:
6.130 +
6.131 +@verbatim
6.132 + ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
6.133 +@end verbatim
6.134 +
6.135 +This will result in that single test suite being run with the test status written to
6.136 +the file ``results.txt''.
6.137 +
6.138 +You should find something similar to the following in that file:
6.139 +
6.140 +@verbatim
6.141 +FAIL: Test Suite ``ns3-wifi-propagation-loss-models'' (real 0.02 user 0.01 system 0.00)
6.142 + PASS: Test Case "Check ... Friis ... model ..." (real 0.01 user 0.00 system 0.00)
6.143 + FAIL: Test Case "Check ... Log Distance ... model" (real 0.01 user 0.01 system 0.00)
6.144 + Details:
6.145 + Message: Got unexpected SNR value
6.146 + Condition: [long description of what actually failed]
6.147 + Actual: 176.395
6.148 + Limit: 176.407 +- 0.0005
6.149 + File: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
6.150 + Line: 360
6.151 +@end verbatim
6.152 +
6.153 +Notice that the Test Suite is composed of two Test Cases. The first test case
6.154 +checked the Friis propagation loss model and passed. The second test case
6.155 +failed checking the Log Distance propagation model. In this case, an SNR of
6.156 +176.395 was found, and the test expected a value of 176.407 correct to three
6.157 +decimal places. The file which implemented the failing test is listed as well
6.158 +as the line of code which triggered the failure.
6.159 +
6.160 +If you desire, you could just as easily have written an HTML file using the
6.161 +@code{--html} option as described above.
6.162 +
6.163 +Typically a user will run all tests at least once after downloading
6.164 +@command{ns-3} to ensure that his or her enviornment has been built correctly
6.165 +and is generating correct results according to the test suites. Developers
6.166 +will typically run the test suites before and after making a change to ensure
6.167 +that they have not introduced a regression with their changes. In this case,
6.168 +developers may not want to run all tests, but only a subset. For example,
6.169 +the developer might only want to run the unit tests periodically while making
6.170 +changes to a repository. In this case, @code{test.py} can be told to constrain
6.171 +the types of tests being run to a particular class of tests. The follwoing
6.172 +command will result in only the unit tests being run:
6.173 +
6.174 +@verbatim
6.175 + ./test.py --constrain=unit
6.176 +@end verbatim
6.177 +
6.178 +Similarly, the following command will result in only the example smoke tests
6.179 +being run:
6.180 +
6.181 +@verbatim
6.182 + ./test.py --constrain=unit
6.183 +@end verbatim
6.184 +
6.185 +To see a quick list of the legal kinds of constraints, you can ask for them
6.186 +to be listed. The following command
6.187 +
6.188 +@verbatim
6.189 + ./test.py --kinds
6.190 +@end verbatim
6.191 +
6.192 +will result in the following list being displayed:
6.193 +
6.194 +@verbatim
6.195 + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.196 + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.197 + 'build' finished successfully (0.939s)Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.198 + bvt: Build Verification Tests (to see if build completed successfully)
6.199 + unit: Unit Tests (within modules to check basic functionality)
6.200 + system: System Tests (spans modules to check integration of modules)
6.201 + example: Examples (to see if example programs run successfully)
6.202 + performance: Performance Tests (check to see if the system is as fast as expected)
6.203 +@end verbatim
6.204 +
6.205 +This list is displayed in increasing order of complexity of the tests. Any of these
6.206 +kinds of test can be provided as a constraint using the @code{--constraint} option.
6.207 +
6.208 +To see a quick list of all of the test suites available, you can ask for them
6.209 +to be listed. The following command,
6.210 +
6.211 +@verbatim
6.212 + ./test.py --list
6.213 +@end verbatim
6.214 +
6.215 +will result in a list of the test suite being displayed, similar to :
6.216 +
6.217 +@verbatim
6.218 + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.219 + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.220 + 'build' finished successfully (0.939s)
6.221 + ns3-wifi-propagation-loss-models
6.222 + ns3-tcp-cwnd
6.223 + ns3-tcp-interoperability
6.224 + pcap-file-object
6.225 + object-name-service
6.226 + random-number-generators
6.227 +@end verbatim
6.228 +
6.229 +Any of these listed suites can be selected to be run by itself using the
6.230 +@code{--suite} option as shown above.
6.231 +
6.232 +Similarly to test suites, one can run a single example program using the @code{--example}
6.233 +option.
6.234 +
6.235 +@verbatim
6.236 + ./test.py --example=udp-echo
6.237 +@end verbatim
6.238 +
6.239 +results in that single example being run.
6.240 +
6.241 +@verbatim
6.242 + PASS: Example udp-echo
6.243 +@end verbatim
6.244 +
6.245 +Normally when example programs are executed, they write a large amount of trace
6.246 +file data. This is normally saved to the base directory of the distribution
6.247 +(e.g., /home/user/ns-3-dev). When @command{test.py} runs an example, it really
6.248 +is completely unconcerned with the trace files. It just wants to to determine
6.249 +if the example can be built and run without error. Since this is the case, the
6.250 +trace files are written into a @code{/tmp/unchecked-traces} directory. If you
6.251 +run the above example, you should be able to find the associated
6.252 +@code{udp-echo.tr} and @code{udp-echo-n-1.pcap} files there.
6.253 +
6.254 +The list of available examples is defined by the contents of the ``examples''
6.255 +directory in the distribution. If you select an example for execution using
6.256 +the @code{--example} option, @code{test.py} will not make any attempt to decide
6.257 +if the example has been configured or not, it will just try to run it and
6.258 +report the result of the attempt.
6.259 +
6.260 +When @command{test.py} runs, by default it will first ensure that the system has
6.261 +been completely built. This can be defeated by selecting the @code{--nowaf}
6.262 +option.
6.263 +
6.264 +@verbatim
6.265 + ./test.py --list --nowaf
6.266 +@end verbatim
6.267 +
6.268 +will result in a list of the currently built test suites being displayed, similar to :
6.269 +
6.270 +@verbatim
6.271 + ns3-wifi-propagation-loss-models
6.272 + ns3-tcp-cwnd
6.273 + ns3-tcp-interoperability
6.274 + pcap-file-object
6.275 + object-name-service
6.276 + random-number-generators
6.277 +@end verbatim
6.278 +
6.279 +Note the absence of the @command{Waf} build messages.
6.280 +
6.281 +Finally, @code{test.py} provides a @command{--verbose} option which will print
6.282 +large amounts of information about its progress. It is not expected that this
6.283 +will be terribly useful for most users.
6.284 +
6.285 +@node TestTaxonomy
6.286 +@section Test Taxonomy
6.287 +
6.288 +As mentioned above, tests are grouped into a number of broadly defined
6.289 +classifications to allow users to selectively run tests to address the different
6.290 +kinds of testing that need to be done.
6.291 +
6.292 +@itemize @bullet
6.293 +@item Build Verification Tests
6.294 +@item Unit Tests
6.295 +@item System Tests
6.296 +@item Examples
6.297 +@item Performance Tests
6.298 +@end itemize
6.299 +
6.300 +@node BuildVerificationTests
6.301 +@subsection Build Verification Tests
6.302 +
6.303 +These are relatively simple tests that are built along with the distribution
6.304 +and are used to make sure that the build is pretty much working. Our
6.305 +current unit tests live in the source files of the code they test and are
6.306 +built into the ns-3 modules; and so fit the description of BVTs. BVTs live
6.307 +in the same source code that is built into the ns-3 code. Our current tests
6.308 +are examples of this kind of test.
6.309 +
6.310 +@node UnitTests
6.311 +@subsection Unit Tests
6.312 +
6.313 +Unit tests are more involved tests that go into detail to make sure that a
6.314 +piece of code works as advertized in isolation. There is really no reason
6.315 +for this kind of test to be built into an ns-3 module. It turns out, for
6.316 +example, that the unit tests for the object name service are about the same
6.317 +size as the object name service code itself. Unit tests are tests that
6.318 +check a single bit of functionality that are not built into the ns-3 code,
6.319 +but live in the same directory as the code it tests. It is possible that
6.320 +these tests check integration of multiple implementation files in a module
6.321 +as well. The file src/core/names-test-suite.cc is an example of this kind
6.322 +of test. The file src/common/pcap-file-test-suite.cc is another example
6.323 +that uses a known good pcap file as a test vector file. This file is stored
6.324 +locally in the src/common directory.
6.325 +
6.326 +@node SystemTests
6.327 +@subsection System Tests
6.328 +
6.329 +System tests are those that involve more than one module in the system. We
6.330 +have lots of this kind of test running in our current regression framework,
6.331 +but they are overloaded examples. We provide a new place for this kind of
6.332 +test in the directory ``src/tests''. The file
6.333 +src/test/ns3tcp/ns3-interop-test-suite.cc is an example of this kind of
6.334 +test. It uses NSC TCP to test the ns-3 TCP implementation. Often there
6.335 +will be test vectors required for this kind of test, and they are stored in
6.336 +the directory where the test lives. For example,
6.337 +ns3tcp-interop-response-vectors.pcap is a file consisting of a number of TCP
6.338 +headers that are used as the expected responses of the ns-3 TCP under test
6.339 +to a stimulus generated by the NSC TCP which is used as a ``known good''
6.340 +implementation.
6.341 +
6.342 +@node Examples
6.343 +@subsection Examples
6.344 +
6.345 +The examples are tested by the framework to make sure they built and will
6.346 +run. Nothing is checked, and currently the pcap files are just written off
6.347 +into /tmp to be discarded. If the examples run (don't crash) they pass this
6.348 +smoke test.
6.349 +
6.350 +@node PerformanceTests
6.351 +@subsection Performance Tests
6.352 +
6.353 +Performance tests are those which exercise a particular part of the system
6.354 +and determine if the tests have executed to completion in a reasonable time.
6.355 +
6.356 +@node RunningTests
6.357 +@section Running Tests
6.358 +
6.359 +Tests are typically run using the high level @code{test.py} program. They
6.360 +can also be run ``manually'' using a low level test-runner executable directly
6.361 +from @code{waf}.
6.362 +
6.363 +@node RunningTestsUnderTestRunnerExecutable
6.364 +@section Running Tests Under the Test Runner Executable
6.365 +
6.366 +The test-runner is the bridge from generic Python code to @command{ns-3} code.
6.367 +It is written in C++ and uses the automatic test discovery process in the
6.368 +@command{ns-3} code to find and allow execution of all of the various tests.
6.369 +
6.370 +Although it may not be used directly very often, it is good to understand how
6.371 +@code{test.py} actually runs the various tests.
6.372 +
6.373 +In order to execute the test-runner, you run it like any other ns-3 executable
6.374 +-- using @code{waf}. To get a list of available options, you can type:
6.375 +
6.376 +@verbatim
6.377 + ./waf --run "test-runner --help"
6.378 +@end verbatim
6.379 +
6.380 +You should see something like the following:
6.381 +
6.382 +@verbatim
6.383 + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.384 + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
6.385 + 'build' finished successfully (0.353s)
6.386 + --basedir=dir: Set the base directory (where to find src) to ``dir''
6.387 + --constrain=test-type: Constrain checks to test suites of type ``test-type''
6.388 + --help: Print this message
6.389 + --kinds: List all of the available kinds of tests
6.390 + --list: List all of the test suites (optionally constrained by test-type)
6.391 + --out=file-name: Set the test status output file to ``file-name''
6.392 + --suite=suite-name: Run the test suite named ``suite-name''
6.393 + --verbose: Turn on messages in the run test suites
6.394 +@end verbatim
6.395 +
6.396 +There are a number of things available to you which will be familiar to you if
6.397 +you have looked at @command{test.py}. This should be expected since the test-
6.398 +runner is just an interface between @code{test.py} and @command{ns-3}. You
6.399 +may notice that example-related commands are missing here. That is because
6.400 +the examples are really not @command{ns-3} tests. @command{test.py} runs them
6.401 +as if they were to present a unified testing environment, but they are really
6.402 +completely different and not to be found here.
6.403 +
6.404 +One new option that appears here is the @code{--basedir} option. It turns out
6.405 +that the tests may need to reference the source directory of the @code{ns-3}
6.406 +distribution to find local data, so a base directory is always required to run
6.407 +a test. To run one of the tests directly from the test-runner, you will need
6.408 +to specify the test suite to run along with the base directory. So you could do,
6.409 +
6.410 +@verbatim
6.411 + ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"
6.412 +@end verbatim
6.413 +
6.414 +Note the ``backward'' quotation marks on the @code{pwd} command. This will run
6.415 +the @code{pcap-file-object} test quietly. The only indication that
6.416 +you will get that the test passed is the @emph{absence} of a message from
6.417 +@code{waf} saying that the program returned something other than a zero
6.418 +exit code. To get some output from the test, you need to specify an output
6.419 +file to which the tests will write their XML status using the @code{--out}
6.420 +option. You need to be careful interpreting the results because the test
6.421 +suites will @emph{append} results onto this file. Try,
6.422 +
6.423 +@verbatim
6.424 + ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml''
6.425 +@end verbatim
6.426 +
6.427 +If you look at the file @code{myfile.xml} you should see something like,
6.428 +
6.429 +@verbatim
6.430 +<TestSuite>
6.431 + <SuiteName>pcap-file-object</SuiteName>
6.432 + <TestCase>
6.433 + <CaseName>Check to see that PcapFile::Open with mode ``w'' works</CaseName>
6.434 + <CaseResult>PASS</CaseResult>
6.435 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.436 + </TestCase>
6.437 + <TestCase>
6.438 + <CaseName>Check to see that PcapFile::Open with mode ``r'' works</CaseName>
6.439 + <CaseResult>PASS</CaseResult>
6.440 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.441 + </TestCase>
6.442 + <TestCase>
6.443 + <CaseName>Check to see that PcapFile::Open with mode ``a'' works</CaseName>
6.444 + <CaseResult>PASS</CaseResult>
6.445 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.446 + </TestCase>
6.447 + <TestCase>
6.448 + <CaseName>Check to see that PcapFileHeader is managed correctly</CaseName>
6.449 + <CaseResult>PASS</CaseResult>
6.450 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.451 + </TestCase>
6.452 + <TestCase>
6.453 + <CaseName>Check to see that PcapRecordHeader is managed correctly</CaseName>
6.454 + <CaseResult>PASS</CaseResult>
6.455 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.456 + </TestCase>
6.457 + <TestCase>
6.458 + <CaseName>Check to see that PcapFile can read out a known good pcap file</CaseName>
6.459 + <CaseResult>PASS</CaseResult>
6.460 + <CaseTime>real 0.00 user 0.00 system 0.00</CaseTime>
6.461 + </TestCase>
6.462 + <SuiteResult>PASS</SuiteResult>
6.463 + <SuiteTime>real 0.00 user 0.00 system 0.00</SuiteTime>
6.464 +</TestSuite>
6.465 +@end verbatim
6.466 +
6.467 +If you are familiar with XML this should be fairly self-explanatory. It is
6.468 +also not a complete XML file since test suites are designed to have their
6.469 +output appended to a master XML status file as described in the @command{test.py}
6.470 +section.
6.471 +
6.472 +@node ClassTestRunner
6.473 +@section Class TestRunner
6.474 +
6.475 +The executables that run dedicated test programs use a TestRunner class. This
6.476 +class provides for automatic test registration and listing, as well as a way to
6.477 +exeute the individual tests. Individual test suites use C++ global constructors
6.478 +to add themselves to a collection of test suites managed by the test runner.
6.479 +The test runner is used to list all of the available tests and to select a test
6.480 +to be run. This is a quite simple class that provides three static methods to
6.481 +provide or Adding and Getting test suites to a collection of tests. See the
6.482 +doxygen for class @code{ns3::TestRunner} for details
6.483 +
6.484 +@node TestSuite
6.485 +@section Test Suite
6.486 +
6.487 +All @command{ns-3} tests are classified into Test Suites and Test Cases. A
6.488 +test suite is a collection of test cases that completely exercise a given kind
6.489 +of functionality. As described above, test suites can be classified as,
6.490 +
6.491 +@itemize @bullet
6.492 +@item Build Verification Tests
6.493 +@item Unit Tests
6.494 +@item System Tests
6.495 +@item Examples
6.496 +@item Performance Tests
6.497 +@end itemize
6.498 +
6.499 +This classification is exported from the TestSuite class. This class is quite
6.500 +simple, existing only as a place to export this type and to accumulate test
6.501 +cases. From a user perspective, in order to create a new TestSuite in the
6.502 +system one only has to define a new class that inherits from class @code{TestSuite}
6.503 +and perform these two duties.
6.504 +
6.505 +The following code will define a new class that can be run by @code{test.py}
6.506 +as a ``unit'' test with the display name, ``my-test-suite-name''.
6.507 +
6.508 +@verbatim
6.509 + class MySuite : public TestSuite
6.510 + {
6.511 + public:
6.512 + MyTestSuite ();
6.513 + };
6.514 +
6.515 + MyTestSuite::MyTestSuite ()
6.516 + : TestSuite ("my-test-suite-name", UNIT)
6.517 + {
6.518 + AddTestCase (new MyTestCase);
6.519 + }
6.520 +
6.521 + MyTestSuite myTestSuite;
6.522 +@end verbatim
6.523 +
6.524 +The base class takes care of all of the registration and reporting required to
6.525 +be a good citizen in the test framework.
6.526 +
6.527 +@node TestCase
6.528 +@section Test Case
6.529 +
6.530 +Individual tests are created using a TestCase class. Common models for the use
6.531 +of a test case include "one test case per feature", and "one test case per method."
6.532 +Mixtures of these models may be used.
6.533 +
6.534 +In order to create a new test case in the system, all one has to do is to inherit
6.535 +from the @code{TestCase} base class, override the constructor to give the test
6.536 +case a name and override the @code{DoRun} method to run the test.
6.537 +
6.538 +@verbatim
6.539 +class MyTestCase : public TestCase
6.540 +{
6.541 + MyTestCase ();
6.542 + virtual bool DoRun (void);
6.543 +};
6.544 +
6.545 +MyTestCase::MyTestCase ()
6.546 + : TestCase ("Check some bit of functionality")
6.547 +{
6.548 +}
6.549 +
6.550 +bool
6.551 +MyTestCase::DoRun (void)
6.552 +{
6.553 + NS_TEST_ASSERT_MSG_EQ (true, true, "Some failure message");
6.554 + return GetErrorStatus ();
6.555 +}
6.556 +@end verbatim
6.557 +
6.558 +@node Utilities
6.559 +@section Utilities
6.560 +
6.561 +There are a number of utilities of various kinds that are also part of the
6.562 +testing framework. Examples include a generalized pcap file useful for
6.563 +storing test vectors; a generic container useful for transient storage of
6.564 +test vectors during test execution; and tools for generating presentations
6.565 +based on validation and verification testing results.
6.566 +
6.567 +
6.568 +
6.569 +
6.570 +
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/doc/testing/testing.css Sat Sep 12 19:44:17 2009 -0700
7.3 @@ -0,0 +1,156 @@
7.4 +body {
7.5 + font-family: "Trebuchet MS", "Bitstream Vera Sans", verdana, lucida, arial, helvetica, sans-serif;
7.6 + background: white;
7.7 + color: black;
7.8 + font-size: 11pt;
7.9 +}
7.10 +
7.11 +h1, h2, h3, h4, h5, h6 {
7.12 +# color: #990000;
7.13 + color: #009999;
7.14 +}
7.15 +
7.16 +pre {
7.17 + font-size: 10pt;
7.18 + background: #e0e0e0;
7.19 + color: black;
7.20 +}
7.21 +
7.22 +a:link, a:visited {
7.23 + font-weight: normal;
7.24 + text-decoration: none;
7.25 + color: #0047b9;
7.26 +}
7.27 +
7.28 +a:hover {
7.29 + font-weight: normal;
7.30 + text-decoration: underline;
7.31 + color: #0047b9;
7.32 +}
7.33 +
7.34 +img {
7.35 + border: 0px;
7.36 +}
7.37 +
7.38 +#main th {
7.39 + font-size: 12pt;
7.40 + background: #b0b0b0;
7.41 +}
7.42 +
7.43 +.odd {
7.44 + font-size: 12pt;
7.45 + background: white;
7.46 +}
7.47 +
7.48 +.even {
7.49 + font-size: 12pt;
7.50 + background: #e0e0e0;
7.51 +}
7.52 +
7.53 +.answer {
7.54 + font-size: large;
7.55 + font-weight: bold;
7.56 +}
7.57 +
7.58 +.answer p {
7.59 + font-size: 12pt;
7.60 + font-weight: normal;
7.61 +}
7.62 +
7.63 +.answer ul {
7.64 + font-size: 12pt;
7.65 + font-weight: normal;
7.66 +}
7.67 +
7.68 +#container {
7.69 + position: absolute;
7.70 + width: 100%;
7.71 + height: 100%;
7.72 + top: 0px;
7.73 +}
7.74 +
7.75 +#feedback {
7.76 + color: #b0b0b0;
7.77 + font-size: 9pt;
7.78 + font-style: italic;
7.79 +}
7.80 +
7.81 +#header {
7.82 + position: absolute;
7.83 + margin: 0px;
7.84 + top: 10px;
7.85 + height:96px;
7.86 + left: 175px;
7.87 + right: 10em;
7.88 + bottom: auto;
7.89 + background: white;
7.90 + clear: both;
7.91 +}
7.92 +
7.93 +#middle {
7.94 + position: absolute;
7.95 + left: 0;
7.96 + height: auto;
7.97 + width: 100%;
7.98 +}
7.99 +
7.100 +#main {
7.101 + position: absolute;
7.102 + top: 50px;
7.103 + left: 175px;
7.104 + right: 100px;
7.105 + background: white;
7.106 + padding: 0em 0em 0em 0em;
7.107 +}
7.108 +
7.109 +#navbar {
7.110 + position: absolute;
7.111 + top: 75px;
7.112 + left: 0em;
7.113 + width: 146px;
7.114 + padding: 0px;
7.115 + margin: 0px;
7.116 + font-size: 10pt;
7.117 +}
7.118 +
7.119 +#navbar a:link, #navbar a:visited {
7.120 + font-weight: normal;
7.121 + text-decoration: none;
7.122 + color: #0047b9;
7.123 +}
7.124 +
7.125 +#navbar a:hover {
7.126 + font-weight: normal;
7.127 + text-decoration: underline;
7.128 + color: #0047b9;
7.129 +}
7.130 +
7.131 +#navbar dl {
7.132 + width: 146px;
7.133 + padding: 0;
7.134 + margin: 0 0 10px 0px;
7.135 + background: #99ffff url(images/box_bottom2.gif) no-repeat bottom left;
7.136 +}
7.137 +
7.138 +#navbar dt {
7.139 + padding: 6px 10px;
7.140 + font-size: 100%;
7.141 + font-weight: bold;
7.142 + background: #009999;
7.143 + margin: 0px;
7.144 + border-bottom: 1px solid #fff;
7.145 + color: white;
7.146 + background: #009999 url(images/box_top2.gif) no-repeat top left;
7.147 +}
7.148 +
7.149 +#navbar dd {
7.150 + font-size: 100%;
7.151 + margin: 0 0 0 0px;
7.152 + padding: 6px 10px;
7.153 + color: #0047b9;
7.154 +}
7.155 +
7.156 +dd#selected {
7.157 + background: #99ffff url(images/arrow.gif) no-repeat;
7.158 + background-position: 4px 10px;
7.159 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/doc/testing/testing.texi Sat Sep 12 19:44:17 2009 -0700
8.3 @@ -0,0 +1,98 @@
8.4 +\input texinfo @c -*-texinfo-*-
8.5 +@c %**start of header
8.6 +@setfilename ns-3.info
8.7 +@settitle ns-3 manual
8.8 +@c @setchapternewpage odd
8.9 +@c %**end of header
8.10 +
8.11 +@ifinfo
8.12 +Documentation for the @command{ns-3} project is available in
8.13 +several documents and the wiki:
8.14 +@itemize @bullet
8.15 +@item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen/Manual}: Documentation of the public APIs of the simulator
8.16 +@item @uref{http://www.nsnam.org/tutorial/index.html,,ns-3 Tutorial}
8.17 +@item @uref{http://www.nsnam.org/doc//index.html,,ns-3 Tutorial}
8.18 +@item Reference Manual
8.19 +@item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki}
8.20 +@end itemize
8.21 +
8.22 +This document is written in GNU Texinfo and is to be maintained in
8.23 +revision control on the @command{ns-3} code server. Both PDF and HTML versions
8.24 +should be available on the server. Changes to
8.25 +the document should be discussed on the ns-developers@@isi.edu mailing list.
8.26 +@end ifinfo
8.27 +
8.28 +@copying
8.29 +
8.30 +This is an @command{ns-3} reference manual.
8.31 +Primary documentation for the @command{ns-3} project is available in
8.32 +four forms:
8.33 +@itemize @bullet
8.34 +@item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen}: Documentation of the public APIs of the simulator
8.35 +@item @uref{http://www.nsnam.org/docs/tutorial/index.html,,ns-3 Tutorial}
8.36 +@item @uref{http://www.nsnam.org/docs/manual/index.html,,ns-3 Manual}
8.37 +@item Testing and Validation (this document)
8.38 +@item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki}
8.39 +@end itemize
8.40 +
8.41 +This document is written in GNU Texinfo and is to be maintained in
8.42 +revision control on the @command{ns-3} code server. Both PDF and HTML
8.43 +versions should be available on the server. Changes to
8.44 +the document should be discussed on the ns-developers@@isi.edu mailing list.
8.45 +
8.46 +This software is free software; you can redistribute it and/or modify
8.47 +it under the terms of the GNU General Public License as published by
8.48 +the Free Software Foundation; either version 2 of the License, or
8.49 +(at your option) any later version.
8.50 +
8.51 +This software is distributed in the hope that it will be useful,
8.52 +but WITHOUT ANY WARRANTY; without even the implied warranty of
8.53 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.54 +GNU General Public License for more details.
8.55 +
8.56 +You should have received a copy of the GNU General Public License
8.57 +along with this program. If not, see @uref{http://www.gnu.org/licenses/}.
8.58 +@end copying
8.59 +
8.60 +@titlepage
8.61 +@title ns-3 Testing and Validation
8.62 +@author ns-3 project
8.63 +@author feedback: ns-developers@@isi.edu
8.64 +@today{}
8.65 +
8.66 +@c @page
8.67 +@vskip 0pt plus 1filll
8.68 +@insertcopying
8.69 +@end titlepage
8.70 +
8.71 +@c So the toc is printed at the start.
8.72 +@anchor{Full Table of Contents}
8.73 +@contents
8.74 +
8.75 +@ifnottex
8.76 +@node Top, Overview, Full Table of Contents
8.77 +@top ns-3 Manual (html version)
8.78 +
8.79 +For a pdf version of this document,
8.80 +see @uref{http://www.nsnam.org/docs/testing.pdf}.
8.81 +
8.82 +@insertcopying
8.83 +@end ifnottex
8.84 +
8.85 +@menu
8.86 +* Overview::
8.87 +* Background::
8.88 +* Testing framework::
8.89 +* How to write tests::
8.90 +* Propagation Loss Models::
8.91 +@end menu
8.92 +
8.93 +@include overview.texi
8.94 +@include background.texi
8.95 +@include testing-framework.texi
8.96 +@include how-to-write-tests.texi
8.97 +@include propagation-loss.texi
8.98 +
8.99 +@printindex cp
8.100 +
8.101 +@bye
9.1 Binary file src/common/known.pcap has changed
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/src/common/pcap-file-test-suite.cc Sat Sep 12 19:44:17 2009 -0700
10.3 @@ -0,0 +1,965 @@
10.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
10.5 +/*
10.6 + * This program is free software; you can redistribute it and/or modify
10.7 + * it under the terms of the GNU General Public License version 2 as
10.8 + * published by the Free Software Foundation;
10.9 + *
10.10 + * This program is distributed in the hope that it will be useful,
10.11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
10.12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10.13 + * GNU General Public License for more details.
10.14 + *
10.15 + * You should have received a copy of the GNU General Public License
10.16 + * along with this program; if not, write to the Free Software
10.17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
10.18 + */
10.19 +
10.20 +#include <iostream>
10.21 +#include <stdio.h>
10.22 +#include <stdlib.h>
10.23 +#include <sstream>
10.24 +
10.25 +#include "ns3/test.h"
10.26 +#include "ns3/pcap-file.h"
10.27 +
10.28 +using namespace ns3;
10.29 +
10.30 +// ===========================================================================
10.31 +// Some utility functions for the tests.
10.32 +// ===========================================================================
10.33 +
10.34 +uint16_t
10.35 +Swap (uint16_t val)
10.36 +{
10.37 + return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
10.38 +}
10.39 +
10.40 +uint32_t
10.41 +Swap (uint32_t val)
10.42 +{
10.43 + return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
10.44 +}
10.45 +
10.46 +bool
10.47 +CheckFileExists (std::string filename)
10.48 +{
10.49 + FILE * p = fopen (filename.c_str (), "rb");
10.50 + if (p == 0)
10.51 + {
10.52 + return false;
10.53 + }
10.54 +
10.55 + fclose (p);
10.56 + return true;
10.57 +}
10.58 +
10.59 +
10.60 +bool
10.61 +CheckFileLength (std::string filename, uint64_t sizeExpected)
10.62 +{
10.63 + FILE * p = fopen (filename.c_str (), "rb");
10.64 + if (p == 0)
10.65 + {
10.66 + return false;
10.67 + }
10.68 +
10.69 + fseek (p, 0, SEEK_END);
10.70 +
10.71 + uint64_t sizeActual = ftell (p);
10.72 + fclose (p);
10.73 +
10.74 + return sizeActual == sizeExpected;
10.75 +}
10.76 +
10.77 +// ===========================================================================
10.78 +// Test case to make sure that the Pcap File Object can do its most basic job
10.79 +// and create an empty pcap file.
10.80 +// ===========================================================================
10.81 +class WriteModeCreateTestCase : public TestCase
10.82 +{
10.83 +public:
10.84 + WriteModeCreateTestCase ();
10.85 + virtual ~WriteModeCreateTestCase ();
10.86 +
10.87 +private:
10.88 + virtual void DoSetup (void);
10.89 + virtual bool DoRun (void);
10.90 + virtual void DoTeardown (void);
10.91 +
10.92 + std::string m_testFilename;
10.93 +};
10.94 +
10.95 +WriteModeCreateTestCase::WriteModeCreateTestCase ()
10.96 + : TestCase ("Check to see that PcapFile::Open with mode \"w\" works")
10.97 +{
10.98 +}
10.99 +
10.100 +WriteModeCreateTestCase::~WriteModeCreateTestCase ()
10.101 +{
10.102 +}
10.103 +
10.104 +void
10.105 +WriteModeCreateTestCase::DoSetup (void)
10.106 +{
10.107 + std::stringstream filename;
10.108 + uint32_t n = rand ();
10.109 + filename << n;
10.110 + m_testFilename = "/tmp/" + filename.str () + ".pcap";
10.111 +}
10.112 +
10.113 +void
10.114 +WriteModeCreateTestCase::DoTeardown (void)
10.115 +{
10.116 + remove (m_testFilename.c_str ());
10.117 +}
10.118 +
10.119 +bool
10.120 +WriteModeCreateTestCase::DoRun (void)
10.121 +{
10.122 + PcapFile f;
10.123 +
10.124 + //
10.125 + // Opening a new file in write mode should result in an empty file of the
10.126 + // given name.
10.127 + //
10.128 + bool err = f.Open (m_testFilename, "w");
10.129 +
10.130 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.131 + f.Close ();
10.132 +
10.133 + NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), true,
10.134 + "Open (" << m_testFilename << ", \"w\") does not create file");
10.135 + NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 0), true,
10.136 + "Open (" << m_testFilename << ", \"w\") does not result in an empty file");
10.137 +
10.138 + //
10.139 + // Calling Init() on a file created with "w" should result in a file just
10.140 + // long enough to contain the pcap file header.
10.141 + //
10.142 + err = f.Open (m_testFilename, "w");
10.143 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.144 +
10.145 + err = f.Init (1234, 5678, 7);
10.146 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
10.147 +
10.148 + f.Close ();
10.149 +
10.150 + NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 24), true,
10.151 + "Init () does not result in a file with a pcap file header");
10.152 +
10.153 + //
10.154 + // Opening an existing file in write mode should result in that file being
10.155 + // emptied.
10.156 + //
10.157 + err = f.Open (m_testFilename, "w");
10.158 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.159 +
10.160 + f.Close ();
10.161 +
10.162 + NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 0), true,
10.163 + "Open (" << m_testFilename << ", \"w\") does not result in an empty file");
10.164 +
10.165 + //
10.166 + // Initialize the file again.
10.167 + //
10.168 + err = f.Open (m_testFilename, "w");
10.169 + NS_TEST_ASSERT_MSG_EQ (err, false,
10.170 + "Open (" << m_testFilename << ", \"w\") returns error");
10.171 +
10.172 + err = f.Init (1234, 5678, 7);
10.173 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
10.174 +
10.175 + //
10.176 + // Now we should be able to write to it since it was opened in "w" mode.
10.177 + // This is just a permissions check so we don't actually look at the
10.178 + // data.
10.179 + //
10.180 + uint8_t buffer[128];
10.181 + err = f.Write (0, 0, buffer, 128);
10.182 + NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
10.183 +
10.184 + return false;
10.185 +}
10.186 +
10.187 +// ===========================================================================
10.188 +// Test case to make sure that the Pcap File Object can open an existing pcap
10.189 +// file.
10.190 +// ===========================================================================
10.191 +class ReadModeCreateTestCase : public TestCase
10.192 +{
10.193 +public:
10.194 + ReadModeCreateTestCase ();
10.195 + virtual ~ReadModeCreateTestCase ();
10.196 +
10.197 +private:
10.198 + virtual void DoSetup (void);
10.199 + virtual bool DoRun (void);
10.200 + virtual void DoTeardown (void);
10.201 +
10.202 + std::string m_testFilename;
10.203 +};
10.204 +
10.205 +ReadModeCreateTestCase::ReadModeCreateTestCase ()
10.206 + : TestCase ("Check to see that PcapFile::Open with mode \"r\" works")
10.207 +{
10.208 +}
10.209 +
10.210 +ReadModeCreateTestCase::~ReadModeCreateTestCase ()
10.211 +{
10.212 +}
10.213 +
10.214 +void
10.215 +ReadModeCreateTestCase::DoSetup (void)
10.216 +{
10.217 + std::stringstream filename;
10.218 + uint32_t n = rand ();
10.219 + filename << n;
10.220 + m_testFilename = "/tmp/" + filename.str () + ".pcap";
10.221 +}
10.222 +
10.223 +void
10.224 +ReadModeCreateTestCase::DoTeardown (void)
10.225 +{
10.226 + remove (m_testFilename.c_str ());
10.227 +}
10.228 +
10.229 +bool
10.230 +ReadModeCreateTestCase::DoRun (void)
10.231 +{
10.232 + PcapFile f;
10.233 +
10.234 + //
10.235 + // Opening a non-existing file in read mode should result in an error.
10.236 + //
10.237 + bool err = f.Open (m_testFilename, "r");
10.238 + NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-existing-filename " << m_testFilename << ", \"r\") does not return error");
10.239 +
10.240 + NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), false,
10.241 + "Open (" << m_testFilename << ", \"r\") unexpectedly created a file");
10.242 +
10.243 + //
10.244 + // Okay, now create an uninitialized file using previously tested operations
10.245 + //
10.246 + err = f.Open (m_testFilename, "w");
10.247 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (filename, \"w\") returns error");
10.248 + f.Close ();
10.249 +
10.250 + //
10.251 + // Opening this file should result in an error since it has no pcap file header.
10.252 + //
10.253 + err = f.Open (m_testFilename, "r");
10.254 + NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-initialized-filename " << m_testFilename << ", \"r\") does not return error");
10.255 +
10.256 + //
10.257 + // Okay, now open that non-initialized file in write mode and initialize it
10.258 + // Note that we open it in write mode to initialize it.
10.259 + //
10.260 + err = f.Open (m_testFilename, "w");
10.261 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.262 +
10.263 + err = f.Init (1234, 5678, 7);
10.264 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
10.265 + f.Close ();
10.266 +
10.267 + //
10.268 + // Opening this file should now work since it has a pcap file header.
10.269 + //
10.270 + err = f.Open (m_testFilename, "r");
10.271 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (initialized-filename " << m_testFilename << ", \"r\") returns error");
10.272 +
10.273 + //
10.274 + // Now we should not be able to write to it since it was opened in "r" mode
10.275 + // even if it has been initialized..
10.276 + //
10.277 + uint8_t buffer[128];
10.278 + err = f.Write (0, 0, buffer, 128);
10.279 + NS_TEST_ASSERT_MSG_EQ (err, true, "Write (read-only-file " << m_testFilename << ") does not return error");
10.280 +
10.281 + f.Close ();
10.282 +
10.283 + return false;
10.284 +}
10.285 +
10.286 +// ===========================================================================
10.287 +// Test case to make sure that the Pcap File Object can open an existing pcap
10.288 +// file for appending.
10.289 +// ===========================================================================
10.290 +class AppendModeCreateTestCase : public TestCase
10.291 +{
10.292 +public:
10.293 + AppendModeCreateTestCase ();
10.294 + virtual ~AppendModeCreateTestCase ();
10.295 +
10.296 +private:
10.297 + virtual void DoSetup (void);
10.298 + virtual bool DoRun (void);
10.299 + virtual void DoTeardown (void);
10.300 +
10.301 + std::string m_testFilename;
10.302 +};
10.303 +
10.304 +AppendModeCreateTestCase::AppendModeCreateTestCase ()
10.305 + : TestCase ("Check to see that PcapFile::Open with mode \"a\" works")
10.306 +{
10.307 +}
10.308 +
10.309 +AppendModeCreateTestCase::~AppendModeCreateTestCase ()
10.310 +{
10.311 +}
10.312 +
10.313 +void
10.314 +AppendModeCreateTestCase::DoSetup (void)
10.315 +{
10.316 + std::stringstream filename;
10.317 + uint32_t n = rand ();
10.318 + filename << n;
10.319 + m_testFilename = "/tmp/" + filename.str () + ".pcap";
10.320 +}
10.321 +
10.322 +void
10.323 +AppendModeCreateTestCase::DoTeardown (void)
10.324 +{
10.325 + remove (m_testFilename.c_str ());
10.326 +}
10.327 +
10.328 +bool
10.329 +AppendModeCreateTestCase::DoRun (void)
10.330 +{
10.331 + PcapFile f;
10.332 +
10.333 + //
10.334 + // Opening a non-existing file in append mode should result in an error.
10.335 + //
10.336 + bool err = f.Open (m_testFilename, "a");
10.337 + NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-existing-filename " << m_testFilename << ", \"a\") does not return error");
10.338 + f.Close ();
10.339 +
10.340 + NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), false,
10.341 + "Open (" << m_testFilename << ", \"a\") unexpectedly created a file");
10.342 +
10.343 + //
10.344 + // Okay, now create an uninitialized file using previously tested operations
10.345 + //
10.346 + err = f.Open (m_testFilename, "w");
10.347 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.348 + f.Close ();
10.349 +
10.350 + //
10.351 + // Opening this file should result in an error since it has no pcap file header.
10.352 + //
10.353 + err = f.Open (m_testFilename, "a");
10.354 + NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-initialized-filename " << m_testFilename << ", \"a\") does not return error");
10.355 +
10.356 + //
10.357 + // Okay, now open that non-initialized file in write mode and initialize it.
10.358 + //
10.359 + err = f.Open (m_testFilename, "w");
10.360 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (non-initialized-filename " << m_testFilename << ", \"w\") returns error");
10.361 +
10.362 + err = f.Init (1234, 5678, 7);
10.363 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
10.364 + f.Close ();
10.365 +
10.366 + //
10.367 + // Opening this file should now work since it has a pcap file header.
10.368 + //
10.369 + err = f.Open (m_testFilename, "a");
10.370 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (initialized-filename " << m_testFilename << ", \"r\") returns error");
10.371 +
10.372 + //
10.373 + // We should be able to write to it since it was opened in "a" mode.
10.374 + //
10.375 + uint8_t buffer[128];
10.376 + err = f.Write (0, 0, buffer, 128);
10.377 + NS_TEST_ASSERT_MSG_EQ (err, false, "Write (append-mode-file " << m_testFilename << ") returns error");
10.378 +
10.379 + f.Close ();
10.380 +
10.381 + return false;
10.382 +}
10.383 +
10.384 +// ===========================================================================
10.385 +// Test case to make sure that the Pcap File Object can write out correct pcap
10.386 +// file headers in both endian cases, and then read them in correctly.
10.387 +// ===========================================================================
10.388 +class FileHeaderTestCase : public TestCase
10.389 +{
10.390 +public:
10.391 + FileHeaderTestCase ();
10.392 + virtual ~FileHeaderTestCase ();
10.393 +
10.394 +private:
10.395 + virtual void DoSetup (void);
10.396 + virtual bool DoRun (void);
10.397 + virtual void DoTeardown (void);
10.398 +
10.399 + std::string m_testFilename;
10.400 +};
10.401 +
10.402 +FileHeaderTestCase::FileHeaderTestCase ()
10.403 + : TestCase ("Check to see that PcapFileHeader is managed correctly")
10.404 +{
10.405 +}
10.406 +
10.407 +FileHeaderTestCase::~FileHeaderTestCase ()
10.408 +{
10.409 +}
10.410 +
10.411 +void
10.412 +FileHeaderTestCase::DoSetup (void)
10.413 +{
10.414 + std::stringstream filename;
10.415 + uint32_t n = rand ();
10.416 + filename << n;
10.417 + m_testFilename = "/tmp/" + filename.str () + ".pcap";
10.418 +}
10.419 +
10.420 +void
10.421 +FileHeaderTestCase::DoTeardown (void)
10.422 +{
10.423 + remove (m_testFilename.c_str ());
10.424 +}
10.425 +
10.426 +bool
10.427 +FileHeaderTestCase::DoRun (void)
10.428 +{
10.429 + PcapFile f;
10.430 +
10.431 + //
10.432 + // Create an uninitialized file using previously tested operations
10.433 + //
10.434 + bool err = f.Open (m_testFilename, "w");
10.435 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.436 +
10.437 + //
10.438 + // Initialize the pcap file header.
10.439 + //
10.440 + err = f.Init (1234, 5678, 7);
10.441 + NS_TEST_ASSERT_MSG_EQ (err, false,
10.442 + "Init (1234, 5678, 7) returns error");
10.443 + f.Close ();
10.444 +
10.445 + //
10.446 + // Take a look and see what was done to the file
10.447 + //
10.448 + FILE *p = fopen (m_testFilename.c_str (), "r+b");
10.449 + NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
10.450 +
10.451 + uint32_t val32;
10.452 + uint16_t val16;
10.453 +
10.454 + size_t result = fread (&val32, sizeof(val32), 1, p);
10.455 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
10.456 + NS_TEST_ASSERT_MSG_EQ (val32, 0xa1b2c3d4, "Magic number written incorrectly");
10.457 +
10.458 + result = fread (&val16, sizeof(val16), 1, p);
10.459 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
10.460 + NS_TEST_ASSERT_MSG_EQ (val16, 2, "Version major written incorrectly");
10.461 +
10.462 + result = fread (&val16, sizeof(val16), 1, p);
10.463 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
10.464 + NS_TEST_ASSERT_MSG_EQ (val16, 4, "Version minor written incorrectly");
10.465 +
10.466 + result = fread (&val32, sizeof(val32), 1, p);
10.467 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
10.468 + NS_TEST_ASSERT_MSG_EQ (val32, 7, "Version minor written incorrectly");
10.469 +
10.470 + result = fread (&val32, sizeof(val32), 1, p);
10.471 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
10.472 + NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
10.473 +
10.474 + result = fread (&val32, sizeof(val32), 1, p);
10.475 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
10.476 + NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Snap length written incorrectly");
10.477 +
10.478 + result = fread (&val32, sizeof(val32), 1, p);
10.479 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
10.480 + NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Data length type written incorrectly");
10.481 +
10.482 + fclose (p);
10.483 + p = 0;
10.484 +
10.485 + //
10.486 + // We wrote a native-endian file out correctly, now let's see if we can read
10.487 + // it back in correctly.
10.488 + //
10.489 + err = f.Open (m_testFilename, "r");
10.490 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (existing-initialized-file " << m_testFilename << ", \"r\") returns error");
10.491 +
10.492 + NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
10.493 + NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
10.494 + NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
10.495 + NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
10.496 + NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
10.497 + NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
10.498 + NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
10.499 +
10.500 + //
10.501 + // Re-open the file to erase its contents.
10.502 + //
10.503 + err = f.Open (m_testFilename, "w");
10.504 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.505 +
10.506 + //
10.507 + // Initialize the pcap file header, turning on swap mode manually to force
10.508 + // the pcap file header to be written out in foreign-endian form, whichever
10.509 + // endian-ness that might be.
10.510 + //
10.511 + err = f.Init (1234, 5678, 7, true);
10.512 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
10.513 + f.Close ();
10.514 +
10.515 + //
10.516 + // Take a look and see what was done to the file. Everything should now
10.517 + // appear byte-swapped.
10.518 + //
10.519 + p = fopen (m_testFilename.c_str (), "r+b");
10.520 + NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
10.521 +
10.522 + result = fread (&val32, sizeof(val32), 1, p);
10.523 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
10.524 + NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (0xa1b2c3d4)), "Magic number written incorrectly");
10.525 +
10.526 + result = fread (&val16, sizeof(val16), 1, p);
10.527 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
10.528 + NS_TEST_ASSERT_MSG_EQ (val16, Swap(uint16_t (2)), "Version major written incorrectly");
10.529 +
10.530 + result = fread (&val16, sizeof(val16), 1, p);
10.531 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
10.532 + NS_TEST_ASSERT_MSG_EQ (val16, Swap(uint16_t (4)), "Version minor written incorrectly");
10.533 +
10.534 + result = fread (&val32, sizeof(val32), 1, p);
10.535 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
10.536 + NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (7)), "Version minor written incorrectly");
10.537 +
10.538 + result = fread (&val32, sizeof(val32), 1, p);
10.539 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
10.540 + NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
10.541 +
10.542 + result = fread (&val32, sizeof(val32), 1, p);
10.543 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
10.544 + NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (5678)), "Snap length written incorrectly");
10.545 +
10.546 + result = fread (&val32, sizeof(val32), 1, p);
10.547 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
10.548 + NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (1234)), "Data length type written incorrectly");
10.549 +
10.550 + fclose (p);
10.551 + p = 0;
10.552 +
10.553 + //
10.554 + // We wrote an opposite-endian file out correctly, now let's see if we can read
10.555 + // it back in correctly.
10.556 + //
10.557 + err = f.Open (m_testFilename, "r");
10.558 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (existing-initialized-file " << m_testFilename << ", \"r\") returns error");
10.559 +
10.560 + NS_TEST_ASSERT_MSG_EQ (f.GetSwapMode (), true, "Byte-swapped file not correctly indicated");
10.561 +
10.562 + NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
10.563 + NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
10.564 + NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
10.565 + NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
10.566 + NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
10.567 + NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
10.568 + NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
10.569 +
10.570 + f.Close ();
10.571 +
10.572 + return false;
10.573 +}
10.574 +
10.575 +// ===========================================================================
10.576 +// Test case to make sure that the Pcap File Object can write pcap packet
10.577 +// records in both endian cases, and then read them in correctly.
10.578 +// ===========================================================================
10.579 +class RecordHeaderTestCase : public TestCase
10.580 +{
10.581 +public:
10.582 + RecordHeaderTestCase ();
10.583 + virtual ~RecordHeaderTestCase ();
10.584 +
10.585 +private:
10.586 + virtual void DoSetup (void);
10.587 + virtual bool DoRun (void);
10.588 + virtual void DoTeardown (void);
10.589 +
10.590 + std::string m_testFilename;
10.591 +};
10.592 +
10.593 +RecordHeaderTestCase::RecordHeaderTestCase ()
10.594 + : TestCase ("Check to see that PcapRecordHeader is managed correctly")
10.595 +{
10.596 +}
10.597 +
10.598 +RecordHeaderTestCase::~RecordHeaderTestCase ()
10.599 +{
10.600 +}
10.601 +
10.602 +void
10.603 +RecordHeaderTestCase::DoSetup (void)
10.604 +{
10.605 + std::stringstream filename;
10.606 + uint32_t n = rand ();
10.607 + filename << n;
10.608 + m_testFilename = "/tmp/" + filename.str () + ".pcap";
10.609 +}
10.610 +
10.611 +void
10.612 +RecordHeaderTestCase::DoTeardown (void)
10.613 +{
10.614 + remove (m_testFilename.c_str ());
10.615 +}
10.616 +
10.617 +bool
10.618 +RecordHeaderTestCase::DoRun (void)
10.619 +{
10.620 + PcapFile f;
10.621 +
10.622 + //
10.623 + // Create an uninitialized file using previously tested operations
10.624 + //
10.625 + bool err = f.Open (m_testFilename, "w");
10.626 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.627 +
10.628 + //
10.629 + // Initialize the pcap file header.
10.630 + //
10.631 + err = f.Init (37, 43, -7);
10.632 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (37, 43, -7) returns error");
10.633 +
10.634 + //
10.635 + // Initialize a buffer with a counting pattern to check the data later.
10.636 + //
10.637 + uint8_t bufferOut[128];
10.638 + for (uint32_t i = 0; i < 128; ++i)
10.639 + {
10.640 + bufferOut[i] = i;
10.641 + }
10.642 +
10.643 + //
10.644 + // Now we should be able to write a packet to it since it was opened in "w"
10.645 + // mode. The packet data written should be limited to 43 bytes in length
10.646 + // by the Init() call above.
10.647 + //
10.648 + err = f.Write (1234, 5678, bufferOut, 128);
10.649 + NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
10.650 + f.Close ();
10.651 +
10.652 + //
10.653 + // Let's peek into the file and see what actually went out for that
10.654 + // packet.
10.655 + //
10.656 + FILE *p = fopen (m_testFilename.c_str (), "r+b");
10.657 + NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
10.658 +
10.659 + //
10.660 + // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
10.661 + // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
10.662 + // double check that this is exactly what happened.
10.663 + //
10.664 + fseek (p, 0, SEEK_END);
10.665 + uint64_t size = ftell (p);
10.666 + NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
10.667 +
10.668 + //
10.669 + // A pcap file header takes up 24 bytes, so we should see a pcap record header
10.670 + // starting there in the file. We've tested this all before so we just assume
10.671 + // it's all right and just seek to just past that point..
10.672 + //
10.673 + fseek (p, 24, SEEK_SET);
10.674 +
10.675 + uint32_t val32;
10.676 +
10.677 + size_t result = fread (&val32, sizeof(val32), 1, p);
10.678 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
10.679 + NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Seconds timestamp written incorrectly");
10.680 +
10.681 + result = fread (&val32, sizeof(val32), 1, p);
10.682 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
10.683 + NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Microseconds timestamp written incorrectly");
10.684 +
10.685 + result = fread (&val32, sizeof(val32), 1, p);
10.686 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
10.687 + NS_TEST_ASSERT_MSG_EQ (val32, 43, "Included length written incorrectly");
10.688 +
10.689 + result = fread (&val32, sizeof(val32), 1, p);
10.690 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
10.691 + NS_TEST_ASSERT_MSG_EQ (val32, 128, "Actual length written incorrectly");
10.692 +
10.693 + //
10.694 + // Take a look and see what went out into the file. The packet data
10.695 + // should be unchanged (unswapped).
10.696 + //
10.697 + uint8_t bufferIn[128];
10.698 +
10.699 + result = fread (bufferIn, 1, 43, p);
10.700 + NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
10.701 +
10.702 + for (uint32_t i = 0; i < 43; ++i)
10.703 + {
10.704 + NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
10.705 + }
10.706 +
10.707 + fclose (p);
10.708 + p = 0;
10.709 +
10.710 + //
10.711 + // Let's see if the PcapFile object can figure out how to do the same thing
10.712 + // correctly read in a packet.
10.713 + //
10.714 + err = f.Open (m_testFilename, "r");
10.715 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"r\") of existing good file returns error");
10.716 +
10.717 + uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
10.718 +
10.719 + err = f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
10.720 + NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good packet returns error");
10.721 + NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
10.722 + NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
10.723 + NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
10.724 + NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
10.725 + NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
10.726 +
10.727 + //
10.728 + // Did the data come back correctly?
10.729 + //
10.730 + for (uint32_t i = 0; i < 43; ++i)
10.731 + {
10.732 + NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
10.733 + }
10.734 +
10.735 + //
10.736 + // We have to check to make sure that the pcap record header is swapped
10.737 + // correctly. Open the file in write mode to clear the data.
10.738 + //
10.739 + err = f.Open (m_testFilename, "w");
10.740 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
10.741 +
10.742 + //
10.743 + // Initialize the pcap file header, forcing the object into swap mode.
10.744 + //
10.745 + err = f.Init (37, 43, -7, true);
10.746 + NS_TEST_ASSERT_MSG_EQ (err, false, "Init (37, 43, -7) returns error");
10.747 +
10.748 + //
10.749 + // Now we should be able to write a packet to it since it was opened in "w"
10.750 + // mode. The packet data written should be limited to 43 bytes in length
10.751 + // by the Init() call above.
10.752 + //
10.753 + err = f.Write (1234, 5678, bufferOut, 128);
10.754 + NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
10.755 + f.Close ();
10.756 +
10.757 + //
10.758 + // Let's peek into the file and see what actually went out for that
10.759 + // packet.
10.760 + //
10.761 + p = fopen (m_testFilename.c_str (), "r+b");
10.762 + NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
10.763 +
10.764 + //
10.765 + // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
10.766 + // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
10.767 + // double check that this is exactly what happened.
10.768 + //
10.769 + fseek (p, 0, SEEK_END);
10.770 + size = ftell (p);
10.771 + NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
10.772 +
10.773 + //
10.774 + // A pcap file header takes up 24 bytes, so we should see a pcap record header
10.775 + // starting there in the file. We've tested this all before so we just assume
10.776 + // it's all right and just seek past it.
10.777 + //
10.778 + fseek (p, 24, SEEK_SET);
10.779 +
10.780 + result = fread (&val32, sizeof(val32), 1, p);
10.781 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
10.782 + NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (1234)), "Swapped seconds timestamp written incorrectly");
10.783 +
10.784 + result = fread (&val32, sizeof(val32), 1, p);
10.785 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
10.786 + NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (5678)), "Swapped microseconds timestamp written incorrectly");
10.787 +
10.788 + result = fread (&val32, sizeof(val32), 1, p);
10.789 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
10.790 + NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (43)), "Swapped included length written incorrectly");
10.791 +
10.792 + result = fread (&val32, sizeof(val32), 1, p);
10.793 + NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
10.794 + NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (128)), "Swapped Actual length written incorrectly");
10.795 +
10.796 + //
10.797 + // Take a look and see what went out into the file. The packet data
10.798 + // should be unchanged (unswapped).
10.799 + //
10.800 + result = fread (bufferIn, 1, 43, p);
10.801 + NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
10.802 +
10.803 + for (uint32_t i = 0; i < 43; ++i)
10.804 + {
10.805 + NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
10.806 + }
10.807 +
10.808 + fclose (p);
10.809 + p = 0;
10.810 +
10.811 + //
10.812 + // Let's see if the PcapFile object can figure out how to do the same thing and
10.813 + // correctly read in a packet. The record header info should come back to us
10.814 + // swapped back into correct form.
10.815 + //
10.816 + err = f.Open (m_testFilename, "r");
10.817 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"r\") of existing good file returns error");
10.818 +
10.819 + err = f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
10.820 + NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good packet returns error");
10.821 + NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
10.822 + NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
10.823 + NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
10.824 + NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
10.825 + NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
10.826 +
10.827 + //
10.828 + // Did the data come back correctly (unchanged / unswapped)?
10.829 + //
10.830 + for (uint32_t i = 0; i < 43; ++i)
10.831 + {
10.832 + NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
10.833 + }
10.834 +
10.835 + f.Close ();
10.836 +
10.837 + return false;
10.838 +}
10.839 +
10.840 +// ===========================================================================
10.841 +// Test case to make sure that the Pcap File Object can read out the contents
10.842 +// of a known good pcap file.
10.843 +// ===========================================================================
10.844 +class ReadFileTestCase : public TestCase
10.845 +{
10.846 +public:
10.847 + ReadFileTestCase ();
10.848 + virtual ~ReadFileTestCase ();
10.849 +
10.850 +private:
10.851 + virtual void DoSetup (void);
10.852 + virtual bool DoRun (void);
10.853 + virtual void DoTeardown (void);
10.854 +
10.855 + std::string m_testFilename;
10.856 +};
10.857 +
10.858 +ReadFileTestCase::ReadFileTestCase ()
10.859 + : TestCase ("Check to see that PcapFile can read out a known good pcap file")
10.860 +{
10.861 +}
10.862 +
10.863 +ReadFileTestCase::~ReadFileTestCase ()
10.864 +{
10.865 +}
10.866 +
10.867 +void
10.868 +ReadFileTestCase::DoSetup (void)
10.869 +{
10.870 +}
10.871 +
10.872 +void
10.873 +ReadFileTestCase::DoTeardown (void)
10.874 +{
10.875 +}
10.876 +
10.877 +const uint32_t N_KNOWN_PACKETS = 6;
10.878 +const uint32_t N_PACKET_BYTES = 16;
10.879 +
10.880 +typedef struct PACKET_ENTRY {
10.881 + uint32_t tsSec;
10.882 + uint32_t tsUsec;
10.883 + uint32_t inclLen;
10.884 + uint32_t origLen;
10.885 + uint16_t data[N_PACKET_BYTES];
10.886 +} PacketEntry;
10.887 +
10.888 +PacketEntry knownPackets[] = {
10.889 + {2, 3696, 46, 46, {0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0003, 0x0a01,
10.890 + 0x0201, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0204, 0x0000, 0x0000}},
10.891 + {2, 3707, 46, 46, {0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0006, 0x0a01,
10.892 + 0x0204, 0x0000, 0x0000, 0x0003, 0x0a01, 0x0201, 0x0000, 0x0000}},
10.893 + {2, 3801, 1070, 1070, {0x4500, 0x041c, 0x0000, 0x0000, 0x3f11, 0x0000, 0x0a01, 0x0101,
10.894 + 0x0a01, 0x0204, 0xc001, 0x0009, 0x0408, 0x0000, 0x0000, 0x0000}},
10.895 + {2, 3811, 46, 46, {0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0006, 0x0a01,
10.896 + 0x0204, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0201, 0x0000, 0x0000}},
10.897 + {2, 3822, 46, 46, {0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0003, 0x0a01,
10.898 + 0x0201, 0x0000, 0x0000, 0x0006, 0x0a01, 0x0204, 0x0000, 0x0000}},
10.899 + {2, 3915, 1070, 1070, {0x4500, 0x041c, 0x0000, 0x0000, 0x4011, 0x0000, 0x0a01, 0x0204,
10.900 + 0x0a01, 0x0101, 0x0009, 0xc001, 0x0408, 0x0000, 0x0000, 0x0000}}
10.901 +};
10.902 +
10.903 +
10.904 +bool
10.905 +ReadFileTestCase::DoRun (void)
10.906 +{
10.907 + PcapFile f;
10.908 +
10.909 + //
10.910 + //
10.911 + std::string filename = NS_TEST_SOURCEDIR + "known.pcap";
10.912 + bool err = f.Open (filename, "r");
10.913 + NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << filename << ", \"w\") returns error");
10.914 +
10.915 + //
10.916 + // We are going to read out the file header and all of the packets to make
10.917 + // sure that we read what we know, a priori, to be there.
10.918 + //
10.919 + // The packet data was gotten using "tcpdump -nn -tt -r known.pcap -x"
10.920 + // and the timestamp and first 32 bytes of the resulting dump were
10.921 + // duplicated in the structure above.
10.922 + //
10.923 + uint8_t data[N_PACKET_BYTES];
10.924 + uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
10.925 +
10.926 + PacketEntry *p = knownPackets;
10.927 +
10.928 + for (uint32_t i = 0; i < N_KNOWN_PACKETS; ++i, ++p)
10.929 + {
10.930 + err = f.Read (data, sizeof(data), tsSec, tsUsec, inclLen, origLen, readLen);
10.931 + NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good pcap file returns error");
10.932 + NS_TEST_ASSERT_MSG_EQ (tsSec, p->tsSec, "Incorrectly read seconds timestap from known good pcap file");
10.933 + NS_TEST_ASSERT_MSG_EQ (tsUsec, p->tsUsec, "Incorrectly read microseconds timestap from known good pcap file");
10.934 + NS_TEST_ASSERT_MSG_EQ (inclLen, p->inclLen, "Incorrectly read included length from known good packet");
10.935 + NS_TEST_ASSERT_MSG_EQ (origLen, p->origLen, "Incorrectly read original length from known good packet");
10.936 + NS_TEST_ASSERT_MSG_EQ (readLen, N_PACKET_BYTES, "Incorrect actual read length from known good packet given buffer size");
10.937 + }
10.938 +
10.939 + //
10.940 + // The file should now be at EOF since we've read all of the packets.
10.941 + // Another packet read should return an error.
10.942 + //
10.943 + err = f.Read (data, 1, tsSec, tsUsec, inclLen, origLen, readLen);
10.944 + NS_TEST_ASSERT_MSG_EQ (err, true, "Read() of known good pcap file at EOF does not return error");
10.945 +
10.946 + f.Close ();
10.947 +
10.948 + return false;
10.949 +}
10.950 +
10.951 +class PcapFileTestSuite : public TestSuite
10.952 +{
10.953 +public:
10.954 + PcapFileTestSuite ();
10.955 +};
10.956 +
10.957 +PcapFileTestSuite::PcapFileTestSuite ()
10.958 + : TestSuite ("pcap-file-object", UNIT)
10.959 +{
10.960 + AddTestCase (new WriteModeCreateTestCase);
10.961 + AddTestCase (new ReadModeCreateTestCase);
10.962 + AddTestCase (new AppendModeCreateTestCase);
10.963 + AddTestCase (new FileHeaderTestCase);
10.964 + AddTestCase (new RecordHeaderTestCase);
10.965 + AddTestCase (new ReadFileTestCase);
10.966 +}
10.967 +
10.968 +PcapFileTestSuite pcapFileTestSuite;
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/src/common/pcap-file.cc Sat Sep 12 19:44:17 2009 -0700
11.3 @@ -0,0 +1,519 @@
11.4 +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
11.5 +/*
11.6 + * Copyright (c) 2009 University of Washington
11.7 + *
11.8 + * This program is free software; you can redistribute it and/or modify
11.9 + * it under the terms of the GNU General Public License version 2 as
11.10 + * published by the Free Software Foundation;
11.11 + *
11.12 + * This program is distributed in the hope that it will be useful,
11.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
11.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11.15 + * GNU General Public License for more details.
11.16 + *
11.17 + * You should have received a copy of the GNU General Public License
11.18 + * along with this program; if not, write to the Free Software
11.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
11.20 + */
11.21 +
11.22 +#include <iostream>
11.23 +#include <stdio.h>
11.24 +#include <stdlib.h>
11.25 +
11.26 +#include "pcap-file.h"
11.27 +
11.28 +//
11.29 +// This file is used as part of the ns-3 test framework, so please refrain from
11.30 +// adding any ns-3 specific constructs such as Packet to this file.
11.31 +//
11.32 +namespace ns3 {
11.33 +
11.34 +const uint32_t MAGIC = 0xa1b2c3d4; /**< Magic number identifying standard pcap file format */
11.35 +const uint32_t SWAPPED_MAGIC = 0xd4c3b2a1; /**< Looks this way if byte swapping is required */
11.36 +
11.37 +const uint32_t NS_MAGIC = 0xa1b23cd4; /**< Magic number identifying nanosec resolution pcap file format */
11.38 +const uint32_t NS_SWAPPED_MAGIC = 0xd43cb2a1; /**< Looks this way if byte swapping is required */
11.39 +
11.40 +const uint16_t VERSION_MAJOR = 2; /**< Major version of supported pcap file format */
11.41 +const uint16_t VERSION_MINOR = 4; /**< Minor version of supported pcap file format */
11.42 +const int32_t SIGFIGS_DEFAULT = 0; /**< Significant figures for timestamps (libpcap doesn't even bother) */
11.43 +
11.44 +PcapFile::PcapFile ()
11.45 + : m_filename (""),
11.46 + m_filePtr (0),
11.47 + m_haveFileHeader (false),
11.48 + m_swapMode (false)
11.49 +{
11.50 +}
11.51 +
11.52 +PcapFile::~PcapFile ()
11.53 +{
11.54 + Close ();
11.55 +}
11.56 +
11.57 +void
11.58 +PcapFile::Close (void)
11.59 +{
11.60 + if (m_filePtr)
11.61 + {
11.62 + fclose (m_filePtr);
11.63 + }
11.64 + m_filePtr = 0;
11.65 + m_filename = "";
11.66 + m_haveFileHeader = false;
11.67 +}
11.68 +
11.69 +uint32_t
11.70 +PcapFile::GetMagic (void)
11.71 +{
11.72 + return m_fileHeader.m_magicNumber;
11.73 +}
11.74 +
11.75 +uint16_t
11.76 +PcapFile::GetVersionMajor (void)
11.77 +{
11.78 + return m_fileHeader.m_versionMajor;
11.79 +}
11.80 +
11.81 +uint16_t
11.82 +PcapFile::GetVersionMinor (void)
11.83 +{
11.84 + return m_fileHeader.m_versionMinor;
11.85 +}
11.86 +
11.87 +int32_t
11.88 +PcapFile::GetTimeZoneOffset (void)
11.89 +{
11.90 + return m_fileHeader.m_zone;
11.91 +}
11.92 +
11.93 +uint32_t
11.94 +PcapFile::GetSigFigs (void)
11.95 +{
11.96 + return m_fileHeader.m_sigFigs;
11.97 +}
11.98 +
11.99 +uint32_t
11.100 +PcapFile::GetSnapLen (void)
11.101 +{
11.102 + return m_fileHeader.m_snapLen;
11.103 +}
11.104 +
11.105 +uint32_t
11.106 +PcapFile::GetDataLinkType (void)
11.107 +{
11.108 + return m_fileHeader.m_type;
11.109 +}
11.110 +
11.111 +bool
11.112 +PcapFile::GetSwapMode (void)
11.113 +{
11.114 + return m_swapMode;
11.115 +}
11.116 +
11.117 +uint8_t
11.118 +PcapFile::Swap (uint8_t val)
11.119 +{
11.120 + return val;
11.121 +}
11.122 +
11.123 +uint16_t
11.124 +PcapFile::Swap (uint16_t val)
11.125 +{
11.126 + return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
11.127 +}
11.128 +
11.129 +uint32_t
11.130 +PcapFile::Swap (uint32_t val)
11.131 +{
11.132 + return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
11.133 +}
11.134 +
11.135 +void
11.136 +PcapFile::Swap (PcapFileHeader *from, PcapFileHeader *to)
11.137 +{
11.138 + to->m_magicNumber = Swap (from->m_magicNumber);
11.139 + to->m_versionMajor = Swap (from->m_versionMajor);
11.140 + to->m_versionMinor = Swap (from->m_versionMinor);
11.141 + to->m_zone = Swap (uint32_t(from->m_zone));
11.142 + to->m_sigFigs = Swap (from->m_sigFigs);
11.143 + to->m_snapLen = Swap (from->m_snapLen);
11.144 + to->m_type = Swap (from->m_type);
11.145 +}
11.146 +
11.147 +void
11.148 +PcapFile::Swap (PcapRecordHeader *from, PcapRecordHeader *to)
11.149 +{
11.150 + to->m_tsSec = Swap (from->m_tsSec);
11.151 + to->m_tsUsec = Swap (from->m_tsUsec);
11.152 + to->m_inclLen = Swap (from->m_inclLen);
11.153 + to->m_origLen = Swap (from->m_origLen);
11.154 +}
11.155 +
11.156 +bool
11.157 +PcapFile::WriteFileHeader (void)
11.158 +{
11.159 + //
11.160 + // If we're initializing the file, we need to write the pcap file header
11.161 + // at the start of the file.
11.162 + //
11.163 + int result = fseek (m_filePtr, 0, SEEK_SET);
11.164 + if (result)
11.165 + {
11.166 + return true;
11.167 + }
11.168 +
11.169 + //
11.170 + // We have the ability to write out the pcap file header in a foreign endian
11.171 + // format, so we need a temp place to swap on the way out.
11.172 + //
11.173 + PcapFileHeader header;
11.174 +
11.175 + //
11.176 + // the pointer headerOut selects either the swapped or non-swapped version of
11.177 + // the pcap file header.
11.178 + //
11.179 + PcapFileHeader *headerOut = 0;
11.180 +
11.181 + if (m_swapMode == false)
11.182 + {
11.183 + headerOut = &m_fileHeader;
11.184 + }
11.185 + else
11.186 + {
11.187 + Swap (&m_fileHeader, &header);
11.188 + headerOut = &header;
11.189 + }
11.190 +
11.191 + //
11.192 + // Watch out for memory alignment differences between machines, so write
11.193 + // them all individually.
11.194 + //
11.195 + result = 0;
11.196 +
11.197 + result |= (fwrite (&headerOut->m_magicNumber, sizeof(headerOut->m_magicNumber), 1, m_filePtr) != 1);
11.198 + result |= (fwrite (&headerOut->m_versionMajor, sizeof(headerOut->m_versionMajor), 1, m_filePtr) != 1);
11.199 + result |= (fwrite (&headerOut->m_versionMinor, sizeof(headerOut->m_versionMinor), 1, m_filePtr) != 1);
11.200 + result |= (fwrite (&headerOut->m_zone, sizeof(headerOut->m_zone), 1, m_filePtr) != 1);
11.201 + result |= (fwrite (&headerOut->m_sigFigs, sizeof(headerOut->m_sigFigs), 1, m_filePtr) != 1);
11.202 + result |= (fwrite (&headerOut->m_snapLen, sizeof(headerOut->m_snapLen), 1, m_filePtr) != 1);
11.203 + result |= (fwrite (&headerOut->m_type, sizeof(headerOut->m_type), 1, m_filePtr) != 1);
11.204 +
11.205 + //
11.206 + // If any of the fwrites above did not succeed in writinging the correct
11.207 + // number of objects, result will be nonzero and will indicate an error.
11.208 + //
11.209 + return result != 0;
11.210 +}
11.211 +
11.212 +bool
11.213 +PcapFile::ReadAndVerifyFileHeader (void)
11.214 +{
11.215 + //
11.216 + // Pcap file header is always at the start of the file
11.217 + //
11.218 + int result = fseek (m_filePtr, 0, SEEK_SET);
11.219 + if (result)
11.220 + {
11.221 + return true;
11.222 + }
11.223 +
11.224 + //
11.225 + // Watch out for memory alignment differences between machines, so read
11.226 + // them all individually.
11.227 + //
11.228 + result = 0;
11.229 +
11.230 + result |= (fread (&m_fileHeader.m_magicNumber, sizeof(m_fileHeader.m_magicNumber), 1, m_filePtr) != 1);
11.231 + result |= (fread (&m_fileHeader.m_versionMajor, sizeof(m_fileHeader.m_versionMajor), 1, m_filePtr) != 1);
11.232 + result |= (fread (&m_fileHeader.m_versionMinor, sizeof(m_fileHeader.m_versionMinor), 1, m_filePtr) != 1);
11.233 + result |= (fread (&m_fileHeader.m_zone, sizeof(m_fileHeader.m_zone), 1, m_filePtr) != 1);
11.234 + result |= (fread (&m_fileHeader.m_sigFigs, sizeof(m_fileHeader.m_sigFigs), 1, m_filePtr) != 1);
11.235 + result |= (fread (&m_fileHeader.m_snapLen, sizeof(m_fileHeader.m_snapLen), 1, m_filePtr) != 1);
11.236 + result |= (fread (&m_fileHeader.m_type, sizeof(m_fileHeader.m_type), 1, m_filePtr) != 1);
11.237 +
11.238 + //
11.239 + // If any of the freads above did not succeed in reading the correct number of
11.240 + // objects, result will be nonzero.
11.241 + //
11.242 + if (result)
11.243 + {
11.244 + return true;
11.245 + }
11.246 +
11.247 + //
11.248 + // There are four possible magic numbers that can be there. Normal and byte
11.249 + // swapped versions of the standard magic number, and normal and byte swapped
11.250 + // versions of the magic number indicating nanosecond resolution timestamps.
11.251 + //
11.252 + if (m_fileHeader.m_magicNumber != MAGIC && m_fileHeader.m_magicNumber != SWAPPED_MAGIC &&
11.253 + m_fileHeader.m_magicNumber != NS_MAGIC && m_fileHeader.m_magicNumber != NS_SWAPPED_MAGIC)
11.254 + {
11.255 + return true;
11.256 + }
11.257 +
11.258 + //
11.259 + // If the magic number is swapped, then we can assume that everything else we read
11.260 + // is swapped.
11.261 + //
11.262 + m_swapMode = (m_fileHeader.m_magicNumber == SWAPPED_MAGIC || m_fileHeader.m_magicNumber == NS_SWAPPED_MAGIC) ? true : false;
11.263 +
11.264 + if (m_swapMode)
11.265 + {
11.266 + Swap (&m_fileHeader, &m_fileHeader);
11.267 + }
11.268 +
11.269 + //
11.270 + // We only deal with one version of the pcap file format.
11.271 + //
11.272 + if (m_fileHeader.m_versionMajor != VERSION_MAJOR || m_fileHeader.m_versionMinor != VERSION_MINOR)
11.273 + {
11.274 + return true;
11.275 + }
11.276 +
11.277 + //
11.278 + // A quick test of reasonablness for the time zone offset corresponding to
11.279 + // a real place on the planet.
11.280 + //
11.281 + if (m_fileHeader.m_zone < -12 || m_fileHeader.m_zone > 12)
11.282 + {
11.283 + return true;
11.284 + }
11.285 +
11.286 + m_haveFileHeader = true;
11.287 + return false;
11.288 +}
11.289 +
11.290 +bool
11.291 +PcapFile::Open (std::string const &filename, std::string const &mode)
11.292 +{
11.293 + //
11.294 + // If opening a new file, implicit close of any existing file required.
11.295 + //
11.296 + Close ();
11.297 +
11.298 + //
11.299 + // All pcap files are binary files, so we just do this automatically.
11.300 + //
11.301 + std::string realMode = mode + "b";
11.302 +
11.303 + //
11.304 + // Our modes may be subtly different from the standard fopen semantics since
11.305 + // we need to have a pcap file header to succeed in some cases; so we need
11.306 + // to process different modes according to our own definitions of the modes.
11.307 + //
11.308 + // In the case of read modes, we must read, check and save the pcap file
11.309 + // header as well as just opening the file.
11.310 + //
11.311 + // In the case of write modes, we just pass the call on through to the
11.312 + // library.
11.313 + //
11.314 + // In the case of append modes, we change the semantics to require the
11.315 + // given file to exist. We can't just create a file since we can't make up
11.316 + // a pcap file header on our own.
11.317 + //
11.318 + if (realMode == "rb" || realMode == "r+b")
11.319 + {
11.320 + m_filePtr = fopen (filename.c_str (), realMode.c_str ());
11.321 + if (m_filePtr == 0)
11.322 + {
11.323 + return true;
11.324 + }
11.325 + m_filename = filename;
11.326 + return ReadAndVerifyFileHeader ();
11.327 + }
11.328 + else if (realMode == "wb" || realMode == "w+b")
11.329 + {
11.330 + m_filePtr = fopen (filename.c_str (), realMode.c_str ());
11.331 + if (m_filePtr)
11.332 + {
11.333 + m_filename = filename;
11.334 + return false;
11.335 + }
11.336 + else
11.337 + {
11.338 + return true;
11.339 + }
11.340 + }
11.341 + else if (realMode == "ab" || realMode == "a+b")
11.342 + {
11.343 + //
11.344 + // Remember that semantics for append are different here. We never create
11.345 + // a file since we can't make up a pcap file header. We first have to
11.346 + // open the file in read-only mode and check to see that it exists and
11.347 + // read the file header. If this all works out, then we can go ahead and
11.348 + // open the file in append mode and seek to the end (imlicitly).
11.349 + //
11.350 + m_filePtr = fopen (filename.c_str (), "rb");
11.351 + if (m_filePtr == 0)
11.352 + {
11.353 + return true;
11.354 + }
11.355 +
11.356 + bool result = ReadAndVerifyFileHeader ();
11.357 + if (result == true)
11.358 + {
11.359 + Close ();
11.360 + return true;
11.361 + }
11.362 +
11.363 + //
11.364 + // We have a properly initialized file and have the pcap file header
11.365 + // loaded and checked. This means that the file meets all of the
11.366 + // critera for opening in append mode, but the file is in read-only mode
11.367 + // now -- we must close it and open it in the correct mode.
11.368 + //
11.369 + fclose (m_filePtr);
11.370 + m_filePtr = 0;
11.371 +
11.372 + m_filePtr = fopen (filename.c_str (), realMode.c_str ());
11.373 + if (m_filePtr == 0)
11.374 + {
11.375 + return true;
11.376 + }
11.377 +
11.378 + m_filename = filename;
11.379 + return false;
11.380 + }
11.381 + else
11.382 + {
11.383 + return true;
11.384 + }
11.385 +}
11.386 +
11.387 +bool
11.388 +PcapFile::Init (uint32_t dataLinkType, uint32_t snapLen, int32_t timeZoneCorrection, bool swapMode)
11.389 +{
11.390 + //
11.391 + // Initialize the in-memory file header.
11.392 + //
11.393 + m_fileHeader.m_magicNumber = MAGIC;
11.394 + m_fileHeader.m_versionMajor = VERSION_MAJOR;
11.395 + m_fileHeader.m_versionMinor = VERSION_MINOR;
11.396 + m_fileHeader.m_zone = timeZoneCorrection;
11.397 + m_fileHeader.m_sigFigs = 0;
11.398 + m_fileHeader.m_snapLen = snapLen;
11.399 + m_fileHeader.m_type = dataLinkType;
11.400 +
11.401 + m_haveFileHeader = true;
11.402 + m_swapMode = swapMode;
11.403 +
11.404 + return WriteFileHeader ();
11.405 +}
11.406 +
11.407 +bool
11.408 +PcapFile::Write (uint32_t tsSec, uint32_t tsUsec, uint8_t const * const data, uint32_t totalLen)
11.409 +{
11.410 + if (m_haveFileHeader == false)
11.411 + {
11.412 + return true;
11.413 + }
11.414 +
11.415 + uint32_t inclLen = totalLen > m_fileHeader.m_snapLen ? m_fileHeader.m_snapLen : totalLen;
11.416 +
11.417 + PcapRecordHeader header;
11.418 + header.m_tsSec = tsSec;
11.419 + header.m_tsUsec = tsUsec;
11.420 + header.m_inclLen = inclLen;
11.421 + header.m_origLen = totalLen;
11.422 +
11.423 + if (m_swapMode)
11.424 + {
11.425 + Swap (&header, &header);
11.426 + }
11.427 +
11.428 + //
11.429 + // Watch out for memory alignment differences between machines, so write
11.430 + // them all individually.
11.431 + //
11.432 + uint32_t result = 0;
11.433 +
11.434 + result |= (fwrite (&header.m_tsSec, sizeof(header.m_tsSec), 1, m_filePtr) != 1);
11.435 + result |= (fwrite (&header.m_tsUsec, sizeof(header.m_tsUsec), 1, m_filePtr) != 1);
11.436 + result |= (fwrite (&header.m_inclLen, sizeof(header.m_inclLen), 1, m_filePtr) != 1);
11.437 + result |= (fwrite (&header.m_origLen, sizeof(header.m_origLen), 1, m_filePtr) != 1);
11.438 +
11.439 + result |= fwrite (data, 1, inclLen, m_filePtr) != inclLen;
11.440 +
11.441 + return result != 0;
11.442 +}
11.443 +
11.444 +bool
11.445 +PcapFile::Read (
11.446 + uint8_t * const data,
11.447 + uint32_t maxBytes,
11.448 + uint32_t &tsSec,
11.449 + uint32_t &tsUsec,
11.450 + uint32_t &inclLen,
11.451 + uint32_t &origLen,
11.452 + uint32_t &readLen)
11.453 +{
11.454 + if (m_haveFileHeader == false)
11.455 + {
11.456 + return true;
11.457 + }
11.458 +
11.459 + PcapRecordHeader header;
11.460 +
11.461 + //
11.462 + // Watch out for memory alignment differences between machines, so read
11.463 + // them all individually.
11.464 + //
11.465 + uint32_t result = 0;
11.466 +
11.467 + result |= (fread (&header.m_tsSec, sizeof(header.m_tsSec), 1, m_filePtr) != 1);
11.468 + result |= (fread (&header.m_tsUsec, sizeof(header.m_tsUsec), 1, m_filePtr) != 1);
11.469 + result |= (fread (&header.m_inclLen, sizeof(header.m_inclLen), 1, m_filePtr) != 1);
11.470 + result |= (fread (&header.m_origLen, sizeof(header.m_origLen), 1, m_filePtr) != 1);
11.471 +
11.472 + //
11.473 + // If any of the freads above did not succeed in reading the correct number of
11.474 + // objects, result will be nonzero.
11.475 + //
11.476 + if (result)
11.477 + {
11.478 + return true;
11.479 + }
11.480 +
11.481 + if (m_swapMode)
11.482 + {
11.483 + Swap (&header, &header);
11.484 + }
11.485 +
11.486 + tsSec = header.m_tsSec;
11.487 + tsUsec = header.m_tsUsec;
11.488 + inclLen = header.m_inclLen;
11.489 + origLen = header.m_origLen;
11.490 +
11.491 + //
11.492 + // We don't always want to force the client to keep a maximum length buffer
11.493 + // around so we allow her to specify a minimum number of bytes to read.
11.494 + // Usually 64 bytes is enough information to print all of the headers, so
11.495 + // it isn't typically necessary to read all thousand bytes of an echo packet,
11.496 + // for example, to figure out what is going on.
11.497 + //
11.498 + readLen = maxBytes < header.m_inclLen ? maxBytes : header.m_inclLen;
11.499 + result = fread (data, 1, readLen, m_filePtr) != readLen;
11.500 + if (result)
11.501 + {
11.502 + return result;
11.503 + }
11.504 +
11.505 + //
11.506 + // To keep the file pointer pointed in the right place, however, we always
11.507 + // need to account for the entire packet as stored originally.
11.508 + //
11.509 + if (readLen < header.m_inclLen)
11.510 + {
11.511 + uint64_t pos = ftell (m_filePtr);
11.512 + int result = fseek (m_filePtr, pos + header.m_inclLen - readLen, SEEK_SET);
11.513 + if (result)
11.514 + {
11.515 + return true;
11.516 + }
11.517 + }
11.518 +
11.519 + return false;
11.520 +}
11.521 +
11.522 +} //namespace ns3
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/src/common/pcap-file.h Sat Sep 12 19:44:17 2009 -0700
12.3 @@ -0,0 +1,193 @@
12.4 +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
12.5 +/*
12.6 + * Copyright (c) 2009 University of Washington
12.7 + *
12.8 + * This program is free software; you can redistribute it and/or modify
12.9 + * it under the terms of the GNU General Public License version 2 as
12.10 + * published by the Free Software Foundation;
12.11 + *
12.12 + * This program is distributed in the hope that it will be useful,
12.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
12.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.15 + * GNU General Public License for more details.
12.16 + *
12.17 + * You should have received a copy of the GNU General Public License
12.18 + * along with this program; if not, write to the Free Software
12.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
12.20 + */
12.21 +
12.22 +#ifndef PCAP_FILE_H
12.23 +#define PCAP_FILE_H
12.24 +
12.25 +#include <string>
12.26 +
12.27 +namespace ns3 {
12.28 +
12.29 +/*
12.30 + * A class representing a pcap file. This allows easy creation, writing and
12.31 + * reading of files composed of stored packets; which may be viewed using
12.32 + * standard tools.
12.33 + */
12.34 +
12.35 +class PcapFile
12.36 +{
12.37 +public:
12.38 + static const int32_t ZONE_DEFAULT = 0; /**< Time zone offset for current location */
12.39 + static const uint32_t SNAPLEN_DEFAULT = 65535; /**< Default value for maximum octets to save per packet */
12.40 +
12.41 +public:
12.42 + PcapFile ();
12.43 + ~PcapFile ();
12.44 +
12.45 + /**
12.46 + * Create a new pcap file or open an existing pcap file. Semantics are
12.47 + * similar to the C standard library function \c fopen, but differ in that
12.48 + * positions in the file are based on packets not characters. For example
12.49 + * if the file is opened for reading, the file position indicator (seek
12.50 + * position) points to the beginning of the first packet in the file, not
12.51 + * zero (which would point to the start of the pcap header).
12.52 + *
12.53 + * Possible modes are:
12.54 + *
12.55 + * \verbatim
12.56 + * "r": Open a file for reading. The file must exist. The pcap header
12.57 + * is assumed to exist in the file and will be read and checked.
12.58 + * The file seek position indicator is set to point to the first
12.59 + * packet on exit.
12.60 + *
12.61 + * "w": Create an empty file for writing. If a file with the same name
12.62 + * already exists its content is erased and the file is treated as a
12.63 + * new empty pcap file. The file is assumed not to have a pcap
12.64 + * header and the caller is responsible for calling Init before saving
12.65 + * any packet data. The file seek position indicator is set to point
12.66 + * to the beginning of the file on exit since there will be no pcap
12.67 + * header.
12.68 + *
12.69 + * "a": Append to an existing file. This mode allows for adding packet data
12.70 + * to the end of an existing pcap file. The file must exist and have a
12.71 + * valid pcap header written (N.B. this is different from standard fopen
12.72 + * semantics). The file seek position indicator is set to point
12.73 + * to the end of the file on exit.
12.74 + *
12.75 + * "r+": Open a file for update -- both reading and writing. The file must
12.76 + * exist. The pcap header is assumed to have been written to the
12.77 + * file and will be read and checked. The file seek position indicator
12.78 + * is set to point to the first packet on exit.
12.79 + *
12.80 + * "w+": Create an empty file for both reading and writing. If a file with
12.81 + * the same name already exists, its content is erased and the file is
12.82 + * treated as a new empty pcap file. Since this new file will not have
12.83 + * a pcap header, the caller is responsible for calling Init before
12.84 + * saving any packet data. On exit, the file seek position indicator is
12.85 + * set to point to the beginning of the file.
12.86 + *
12.87 + * "a+" Open a file for reading and appending. The file must exist and have a
12.88 + * valid pcap header written (N.B. this is different from standard fopen
12.89 + * semantics). The file seek position indicator is set to point
12.90 + * to the end of the file on exit. Existing content is preserved.
12.91 + * \endverbatim
12.92 + *
12.93 + * Since a pcap file is always a binary file, the file type is automatically
12.94 + * selected as a binary file. For example, providing a mode string "a+"
12.95 + * results in the underlying OS file being opened in "a+b" mode.
12.96 + *
12.97 + * \param filename String containing the name of the file.
12.98 + *
12.99 + * \param mode String containing the access mode for the file.
12.100 + *
12.101 + * \returns Error indication that should be interpreted as, "did an error
12.102 + * happen"? That is, the method returns false if the open succeeds, true
12.103 + * otherwise. The errno variable will be set by the OS to to provide a
12.104 + * more descriptive failure indication.
12.105 + */
12.106 + bool Open (std::string const &filename, std::string const &mode);
12.107 +
12.108 + void Close (void);
12.109 +
12.110 + /**
12.111 + * Initialize the pcap file associated with this object. This file must have
12.112 + * been previously opened with write permissions.
12.113 + *
12.114 + * \param dataLinkType A data link type as defined in the pcap library. If
12.115 + * you want to make resulting pcap files visible in existing tools, the
12.116 + * data link type must match existing definitions, such as PCAP_ETHERNET,
12.117 + * PCAP_PPP, PCAP_80211, etc. If you are storing different kinds of packet
12.118 + * data, such as naked TCP headers, you are at liberty to locally define your
12.119 + * own data link types. According to the pcap-linktype man page, "well-known"
12.120 + * pcap linktypes range from 0 to 177. If you use a large random number for
12.121 + * your type, chances are small for a collision.
12.122 + *
12.123 + * \param snapLen An optional maximum size for packets written to the file.
12.124 + * Defaults to 65535. If packets exceed this length they are truncated.
12.125 + *
12.126 + * \param timeZoneCorrection An integer describing the offset of your local
12.127 + * time zone from UTC/GMT. For example, Pacific Standard Time in the US is
12.128 + * GMT-8, so one would enter -8 for that correction. Defaults to 0 (UTC).
12.129 + *
12.130 + * \returns false if the open succeeds, true otherwise.
12.131 + *
12.132 + * \warning Calling this method on an existing file will result in the loss
12.133 + * any existing data.
12.134 + */
12.135 + bool Init (uint32_t dataLinkType,
12.136 + uint32_t snapLen = SNAPLEN_DEFAULT,
12.137 + int32_t timeZoneCorrection = ZONE_DEFAULT,
12.138 + bool swapMode = false);
12.139 +
12.140 + bool Write (uint32_t tsSec, uint32_t tsUsec, uint8_t const * const data, uint32_t totalLen);
12.141 +
12.142 + bool Read (uint8_t * const data,
12.143 + uint32_t maxBytes,
12.144 + uint32_t &tsSec,
12.145 + uint32_t &tsUsec,
12.146 + uint32_t &inclLen,
12.147 + uint32_t &origLen,
12.148 + uint32_t &readLen);
12.149 +
12.150 + bool GetSwapMode (void);
12.151 +
12.152 + uint32_t GetMagic (void);
12.153 + uint16_t GetVersionMajor (void);
12.154 + uint16_t GetVersionMinor (void);
12.155 + int32_t GetTimeZoneOffset (void);
12.156 + uint32_t GetSigFigs (void);
12.157 + uint32_t GetSnapLen (void);
12.158 + uint32_t GetDataLinkType (void);
12.159 +
12.160 +private:
12.161 + typedef struct {
12.162 + uint32_t m_magicNumber; /**< Magic number identifying this as a pcap file */
12.163 + uint16_t m_versionMajor; /**< Major version identifying the version of pcap used in this file */
12.164 + uint16_t m_versionMinor; /**< Minor version identifying the version of pcap used in this file */
12.165 + int32_t m_zone; /**< Time zone correction to be applied to timestamps of packets */
12.166 + uint32_t m_sigFigs; /**< Unused by pretty much everybody */
12.167 + uint32_t m_snapLen; /**< Maximum length of packet data stored in records */
12.168 + uint32_t m_type; /**< Data link type of packet data */
12.169 + } PcapFileHeader;
12.170 +
12.171 + typedef struct {
12.172 + uint32_t m_tsSec; /**< seconds part of timestamp */
12.173 + uint32_t m_tsUsec; /**< microseconds part of timestamp (nsecs for PCAP_NSEC_MAGIC) */
12.174 + uint32_t m_inclLen; /**< number of octets of packet saved in file */
12.175 + uint32_t m_origLen; /**< actual length of original packet */
12.176 + } PcapRecordHeader;
12.177 +
12.178 + uint8_t Swap (uint8_t val);
12.179 + uint16_t Swap (uint16_t val);
12.180 + uint32_t Swap (uint32_t val);
12.181 + void Swap (PcapFileHeader *from, PcapFileHeader *to);
12.182 + void Swap (PcapRecordHeader *from, PcapRecordHeader *to);
12.183 +
12.184 + bool WriteFileHeader (void);
12.185 + bool ReadAndVerifyFileHeader (void);
12.186 +
12.187 + std::string m_filename;
12.188 + FILE *m_filePtr;
12.189 + PcapFileHeader m_fileHeader;
12.190 + bool m_haveFileHeader;
12.191 + bool m_swapMode;
12.192 +};
12.193 +
12.194 +}//namespace ns3
12.195 +
12.196 +#endif /* PCAP_FILE_H */
13.1 --- a/src/common/wscript Thu Sep 10 11:41:33 2009 +0100
13.2 +++ b/src/common/wscript Sat Sep 12 19:44:17 2009 -0700
13.3 @@ -18,6 +18,8 @@
13.4 'tag-buffer.cc',
13.5 'packet-tag-list.cc',
13.6 'ascii-writer.cc',
13.7 + 'pcap-file.cc',
13.8 + 'pcap-file-test-suite.cc',
13.9 ]
13.10
13.11 headers = bld.new_task_gen('ns3header')
13.12 @@ -38,4 +40,5 @@
13.13 'packet-tag-list.h',
13.14 'ascii-writer.h',
13.15 'sgi-hashmap.h',
13.16 + 'pcap-file.h',
13.17 ]
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/src/core/names-test-suite.cc Sat Sep 12 19:44:17 2009 -0700
14.3 @@ -0,0 +1,975 @@
14.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
14.5 +/*
14.6 + * This program is free software; you can redistribute it and/or modify
14.7 + * it under the terms of the GNU General Public License version 2 as
14.8 + * published by the Free Software Foundation;
14.9 + *
14.10 + * This program is distributed in the hope that it will be useful,
14.11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
14.12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14.13 + * GNU General Public License for more details.
14.14 + *
14.15 + * You should have received a copy of the GNU General Public License
14.16 + * along with this program; if not, write to the Free Software
14.17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14.18 + */
14.19 +
14.20 +#include "test.h"
14.21 +#include "names.h"
14.22 +
14.23 +using namespace ns3;
14.24 +
14.25 +// ===========================================================================
14.26 +// Cook up a couple of simple object class that we can use in the object
14.27 +// naming tests. They do nothing but be of the right type.
14.28 +// ===========================================================================
14.29 +class TestObject : public Object
14.30 +{
14.31 +public:
14.32 + static TypeId GetTypeId (void)
14.33 + {
14.34 + static TypeId tid = TypeId ("TestObject")
14.35 + .SetParent (Object::GetTypeId ())
14.36 + .HideFromDocumentation ()
14.37 + .AddConstructor<TestObject> ();
14.38 + return tid;
14.39 + }
14.40 + TestObject () {}
14.41 + virtual void Dispose (void) {}
14.42 +};
14.43 +
14.44 +class AlternateTestObject : public Object
14.45 +{
14.46 +public:
14.47 + static TypeId GetTypeId (void)
14.48 + {
14.49 + static TypeId tid = TypeId ("AlternateTestObject")
14.50 + .SetParent (Object::GetTypeId ())
14.51 + .HideFromDocumentation ()
14.52 + .AddConstructor<AlternateTestObject> ();
14.53 + return tid;
14.54 + }
14.55 + AlternateTestObject () {}
14.56 + virtual void Dispose (void) {}
14.57 +};
14.58 +
14.59 +// ===========================================================================
14.60 +// Test case to make sure that the Object Name Service can do its most basic
14.61 +// job and add associations between Objects using the lowest level add
14.62 +// function, which is:
14.63 +//
14.64 +// Add (Ptr<Object> context, std::string name, Ptr<Object> object);
14.65 +//
14.66 +// All other add functions will just translate into this form, so this is the
14.67 +// most basic Add functionality.
14.68 +// ===========================================================================
14.69 +class BasicAddTestCase : public TestCase
14.70 +{
14.71 +public:
14.72 + BasicAddTestCase ();
14.73 + virtual ~BasicAddTestCase ();
14.74 +
14.75 +private:
14.76 + virtual bool DoRun (void);
14.77 + virtual void DoTeardown (void);
14.78 +};
14.79 +
14.80 +BasicAddTestCase::BasicAddTestCase ()
14.81 + : TestCase ("Check low level Names::Add and Names::FindName functionality")
14.82 +{
14.83 +}
14.84 +
14.85 +BasicAddTestCase::~BasicAddTestCase ()
14.86 +{
14.87 +}
14.88 +
14.89 +void
14.90 +BasicAddTestCase::DoTeardown (void)
14.91 +{
14.92 + Names::Delete ();
14.93 +}
14.94 +
14.95 +bool
14.96 +BasicAddTestCase::DoRun (void)
14.97 +{
14.98 + std::string found;
14.99 +
14.100 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.101 + Names::Add (Ptr<Object> (0, false), "Name One", objectOne);
14.102 +
14.103 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.104 + Names::Add (Ptr<Object> (0, false), "Name Two", objectTwo);
14.105 +
14.106 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.107 + Names::Add (objectOne, "Child", childOfObjectOne);
14.108 +
14.109 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.110 + Names::Add (objectTwo, "Child", childOfObjectTwo);
14.111 +
14.112 + found = Names::FindName (objectOne);
14.113 + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object");
14.114 +
14.115 + found = Names::FindName (objectTwo);
14.116 + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object");
14.117 +
14.118 + found = Names::FindName (childOfObjectOne);
14.119 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.120 +
14.121 + found = Names::FindName (childOfObjectTwo);
14.122 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.123 +
14.124 + return false;
14.125 +}
14.126 +
14.127 +// ===========================================================================
14.128 +// Test case to make sure that the Object Name Service can correctly use a
14.129 +// string context in the most basic ways
14.130 +//
14.131 +// Add (std::string context, std::string name, Ptr<Object> object);
14.132 +//
14.133 +// High level path-based functions will translate into this form, so this is
14.134 +// the second most basic Add functionality.
14.135 +// ===========================================================================
14.136 +class StringContextAddTestCase : public TestCase
14.137 +{
14.138 +public:
14.139 + StringContextAddTestCase ();
14.140 + virtual ~StringContextAddTestCase ();
14.141 +
14.142 +private:
14.143 + virtual bool DoRun (void);
14.144 + virtual void DoTeardown (void);
14.145 +};
14.146 +
14.147 +StringContextAddTestCase::StringContextAddTestCase ()
14.148 + : TestCase ("Check string context Names::Add and Names::FindName functionality")
14.149 +
14.150 +{
14.151 +}
14.152 +
14.153 +StringContextAddTestCase::~StringContextAddTestCase ()
14.154 +{
14.155 +}
14.156 +
14.157 +void
14.158 +StringContextAddTestCase::DoTeardown (void)
14.159 +{
14.160 + Names::Delete ();
14.161 +}
14.162 +
14.163 +bool
14.164 +StringContextAddTestCase::DoRun (void)
14.165 +{
14.166 + std::string found;
14.167 +
14.168 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.169 + Names::Add ("/Names", "Name One", objectOne);
14.170 +
14.171 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.172 + Names::Add ("/Names", "Name Two", objectTwo);
14.173 +
14.174 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.175 + Names::Add ("/Names/Name One", "Child", childOfObjectOne);
14.176 +
14.177 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.178 + Names::Add ("/Names/Name Two", "Child", childOfObjectTwo);
14.179 +
14.180 + found = Names::FindName (objectOne);
14.181 + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object");
14.182 +
14.183 + found = Names::FindName (objectTwo);
14.184 + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object");
14.185 +
14.186 + found = Names::FindName (childOfObjectOne);
14.187 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.188 +
14.189 + found = Names::FindName (childOfObjectTwo);
14.190 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.191 +
14.192 + return false;
14.193 +}
14.194 +
14.195 +// ===========================================================================
14.196 +// Test case to make sure that the Object Name Service can correctly use a
14.197 +// fully qualified path to add assocations
14.198 +//
14.199 +// Add (std::string name, Ptr<Object> object);
14.200 +// ===========================================================================
14.201 +class FullyQualifiedAddTestCase : public TestCase
14.202 +{
14.203 +public:
14.204 + FullyQualifiedAddTestCase ();
14.205 + virtual ~FullyQualifiedAddTestCase ();
14.206 +
14.207 +private:
14.208 + virtual bool DoRun (void);
14.209 + virtual void DoTeardown (void);
14.210 +};
14.211 +
14.212 +FullyQualifiedAddTestCase::FullyQualifiedAddTestCase ()
14.213 + : TestCase ("Check fully qualified path Names::Add and Names::FindName functionality")
14.214 +
14.215 +{
14.216 +}
14.217 +
14.218 +FullyQualifiedAddTestCase::~FullyQualifiedAddTestCase ()
14.219 +{
14.220 +}
14.221 +
14.222 +void
14.223 +FullyQualifiedAddTestCase::DoTeardown (void)
14.224 +{
14.225 + Names::Delete ();
14.226 +}
14.227 +
14.228 +bool
14.229 +FullyQualifiedAddTestCase::DoRun (void)
14.230 +{
14.231 + std::string found;
14.232 +
14.233 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.234 + Names::Add ("/Names/Name One", objectOne);
14.235 +
14.236 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.237 + Names::Add ("/Names/Name Two", objectTwo);
14.238 +
14.239 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.240 + Names::Add ("/Names/Name One/Child", childOfObjectOne);
14.241 +
14.242 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.243 + Names::Add ("/Names/Name Two/Child", childOfObjectTwo);
14.244 +
14.245 + found = Names::FindName (objectOne);
14.246 + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object");
14.247 +
14.248 + found = Names::FindName (objectTwo);
14.249 + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object");
14.250 +
14.251 + found = Names::FindName (childOfObjectOne);
14.252 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.253 +
14.254 + found = Names::FindName (childOfObjectTwo);
14.255 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.256 +
14.257 + return false;
14.258 +}
14.259 +
14.260 +// ===========================================================================
14.261 +// Test case to make sure that the Object Name Service can correctly use a
14.262 +// relative path to add assocations. This functionality is provided as a
14.263 +// convenience so clients don't always have to provide the name service
14.264 +// namespace name in all of their strings.
14.265 +//
14.266 +//
14.267 +// Add (std::string name, Ptr<Object> object);
14.268 +// ===========================================================================
14.269 +class RelativeAddTestCase : public TestCase
14.270 +{
14.271 +public:
14.272 + RelativeAddTestCase ();
14.273 + virtual ~RelativeAddTestCase ();
14.274 +
14.275 +private:
14.276 + virtual bool DoRun (void);
14.277 + virtual void DoTeardown (void);
14.278 +};
14.279 +
14.280 +RelativeAddTestCase::RelativeAddTestCase ()
14.281 + : TestCase ("Check relative path Names::Add and Names::FindName functionality")
14.282 +
14.283 +{
14.284 +}
14.285 +
14.286 +RelativeAddTestCase::~RelativeAddTestCase ()
14.287 +{
14.288 +}
14.289 +
14.290 +void
14.291 +RelativeAddTestCase::DoTeardown (void)
14.292 +{
14.293 + Names::Delete ();
14.294 +}
14.295 +
14.296 +bool
14.297 +RelativeAddTestCase::DoRun (void)
14.298 +{
14.299 + std::string found;
14.300 +
14.301 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.302 + Names::Add ("Name One", objectOne);
14.303 +
14.304 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.305 + Names::Add ("Name Two", objectTwo);
14.306 +
14.307 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.308 + Names::Add ("Name One/Child", childOfObjectOne);
14.309 +
14.310 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.311 + Names::Add ("Name Two/Child", childOfObjectTwo);
14.312 +
14.313 + found = Names::FindName (objectOne);
14.314 + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object");
14.315 +
14.316 + found = Names::FindName (objectTwo);
14.317 + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object");
14.318 +
14.319 + found = Names::FindName (childOfObjectOne);
14.320 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.321 +
14.322 + found = Names::FindName (childOfObjectTwo);
14.323 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.324 +
14.325 + return false;
14.326 +}
14.327 +
14.328 +// ===========================================================================
14.329 +// Test case to make sure that the Object Name Service can rename objects in
14.330 +// its most basic way, which is
14.331 +//
14.332 +// Rename (Ptr<Object> context, std::string oldname, std::string newname);
14.333 +//
14.334 +// All other rename functions will just translate into this form, so this is the
14.335 +// most basic rename functionality.
14.336 +// ===========================================================================
14.337 +class BasicRenameTestCase : public TestCase
14.338 +{
14.339 +public:
14.340 + BasicRenameTestCase ();
14.341 + virtual ~BasicRenameTestCase ();
14.342 +
14.343 +private:
14.344 + virtual bool DoRun (void);
14.345 + virtual void DoTeardown (void);
14.346 +};
14.347 +
14.348 +BasicRenameTestCase::BasicRenameTestCase ()
14.349 + : TestCase ("Check low level Names::Rename functionality")
14.350 +{
14.351 +}
14.352 +
14.353 +BasicRenameTestCase::~BasicRenameTestCase ()
14.354 +{
14.355 +}
14.356 +
14.357 +void
14.358 +BasicRenameTestCase::DoTeardown (void)
14.359 +{
14.360 + Names::Delete ();
14.361 +}
14.362 +
14.363 +bool
14.364 +BasicRenameTestCase::DoRun (void)
14.365 +{
14.366 + std::string found;
14.367 +
14.368 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.369 + Names::Add (Ptr<Object> (0, false), "Name", objectOne);
14.370 +
14.371 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.372 + Names::Add (objectOne, "Child", childOfObjectOne);
14.373 +
14.374 + found = Names::FindName (objectOne);
14.375 + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object");
14.376 +
14.377 + Names::Rename (Ptr<Object> (0, false), "Name", "New Name");
14.378 +
14.379 + found = Names::FindName (objectOne);
14.380 + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object");
14.381 +
14.382 + found = Names::FindName (childOfObjectOne);
14.383 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.384 +
14.385 + Names::Rename (objectOne, "Child", "New Child");
14.386 +
14.387 + found = Names::FindName (childOfObjectOne);
14.388 + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object");
14.389 +
14.390 + return false;
14.391 +}
14.392 +
14.393 +// ===========================================================================
14.394 +// Test case to make sure that the Object Name Service can rename objects
14.395 +// using a string context
14.396 +//
14.397 +// Rename (std::string context, std::string oldname, std::string newname);
14.398 +// ===========================================================================
14.399 +class StringContextRenameTestCase : public TestCase
14.400 +{
14.401 +public:
14.402 + StringContextRenameTestCase ();
14.403 + virtual ~StringContextRenameTestCase ();
14.404 +
14.405 +private:
14.406 + virtual bool DoRun (void);
14.407 + virtual void DoTeardown (void);
14.408 +};
14.409 +
14.410 +StringContextRenameTestCase::StringContextRenameTestCase ()
14.411 + : TestCase ("Check string context-based Names::Rename functionality")
14.412 +{
14.413 +}
14.414 +
14.415 +StringContextRenameTestCase::~StringContextRenameTestCase ()
14.416 +{
14.417 +}
14.418 +
14.419 +void
14.420 +StringContextRenameTestCase::DoTeardown (void)
14.421 +{
14.422 + Names::Delete ();
14.423 +}
14.424 +
14.425 +bool
14.426 +StringContextRenameTestCase::DoRun (void)
14.427 +{
14.428 + std::string found;
14.429 +
14.430 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.431 + Names::Add ("/Names", "Name", objectOne);
14.432 +
14.433 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.434 + Names::Add ("/Names/Name", "Child", childOfObjectOne);
14.435 +
14.436 + found = Names::FindName (objectOne);
14.437 + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object");
14.438 +
14.439 + Names::Rename ("/Names", "Name", "New Name");
14.440 +
14.441 + found = Names::FindName (objectOne);
14.442 + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object");
14.443 +
14.444 + found = Names::FindName (childOfObjectOne);
14.445 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.446 +
14.447 + Names::Rename ("/Names/New Name", "Child", "New Child");
14.448 +
14.449 + found = Names::FindName (childOfObjectOne);
14.450 + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object");
14.451 +
14.452 + return false;
14.453 +}
14.454 +
14.455 +// ===========================================================================
14.456 +// Test case to make sure that the Object Name Service can rename objects
14.457 +// using a fully qualified path name
14.458 +//
14.459 +// Rename (std::string oldpath, std::string newname);
14.460 +// ===========================================================================
14.461 +class FullyQualifiedRenameTestCase : public TestCase
14.462 +{
14.463 +public:
14.464 + FullyQualifiedRenameTestCase ();
14.465 + virtual ~FullyQualifiedRenameTestCase ();
14.466 +
14.467 +private:
14.468 + virtual bool DoRun (void);
14.469 + virtual void DoTeardown (void);
14.470 +};
14.471 +
14.472 +FullyQualifiedRenameTestCase::FullyQualifiedRenameTestCase ()
14.473 + : TestCase ("Check fully qualified path Names::Rename functionality")
14.474 +{
14.475 +}
14.476 +
14.477 +FullyQualifiedRenameTestCase::~FullyQualifiedRenameTestCase ()
14.478 +{
14.479 +}
14.480 +
14.481 +void
14.482 +FullyQualifiedRenameTestCase::DoTeardown (void)
14.483 +{
14.484 + Names::Delete ();
14.485 +}
14.486 +
14.487 +bool
14.488 +FullyQualifiedRenameTestCase::DoRun (void)
14.489 +{
14.490 + std::string found;
14.491 +
14.492 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.493 + Names::Add ("/Names/Name", objectOne);
14.494 +
14.495 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.496 + Names::Add ("/Names/Name/Child", childOfObjectOne);
14.497 +
14.498 + found = Names::FindName (objectOne);
14.499 + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object");
14.500 +
14.501 + Names::Rename ("/Names/Name", "New Name");
14.502 +
14.503 + found = Names::FindName (objectOne);
14.504 + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object");
14.505 +
14.506 + found = Names::FindName (childOfObjectOne);
14.507 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.508 +
14.509 + Names::Rename ("/Names/New Name/Child", "New Child");
14.510 +
14.511 + found = Names::FindName (childOfObjectOne);
14.512 + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object");
14.513 +
14.514 + return false;
14.515 +}
14.516 +
14.517 +// ===========================================================================
14.518 +// Test case to make sure that the Object Name Service can rename objects
14.519 +// using a relaltive path name
14.520 +//
14.521 +// Rename (std::string oldpath, std::string newname);
14.522 +// ===========================================================================
14.523 +class RelativeRenameTestCase : public TestCase
14.524 +{
14.525 +public:
14.526 + RelativeRenameTestCase ();
14.527 + virtual ~RelativeRenameTestCase ();
14.528 +
14.529 +private:
14.530 + virtual bool DoRun (void);
14.531 + virtual void DoTeardown (void);
14.532 +};
14.533 +
14.534 +RelativeRenameTestCase::RelativeRenameTestCase ()
14.535 + : TestCase ("Check relative path Names::Rename functionality")
14.536 +{
14.537 +}
14.538 +
14.539 +RelativeRenameTestCase::~RelativeRenameTestCase ()
14.540 +{
14.541 +}
14.542 +
14.543 +void
14.544 +RelativeRenameTestCase::DoTeardown (void)
14.545 +{
14.546 + Names::Delete ();
14.547 +}
14.548 +
14.549 +bool
14.550 +RelativeRenameTestCase::DoRun (void)
14.551 +{
14.552 + std::string found;
14.553 +
14.554 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.555 + Names::Add ("Name", objectOne);
14.556 +
14.557 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.558 + Names::Add ("Name/Child", childOfObjectOne);
14.559 +
14.560 + found = Names::FindName (objectOne);
14.561 + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object");
14.562 +
14.563 + Names::Rename ("Name", "New Name");
14.564 +
14.565 + found = Names::FindName (objectOne);
14.566 + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object");
14.567 +
14.568 + found = Names::FindName (childOfObjectOne);
14.569 + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object");
14.570 +
14.571 + Names::Rename ("New Name/Child", "New Child");
14.572 +
14.573 + found = Names::FindName (childOfObjectOne);
14.574 + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object");
14.575 +
14.576 + return false;
14.577 +}
14.578 +
14.579 +// ===========================================================================
14.580 +// Test case to make sure that the Object Name Service can look up an object
14.581 +// and return its fully qualified path name
14.582 +//
14.583 +// FindPath (Ptr<Object> object);
14.584 +// ===========================================================================
14.585 +class FindPathTestCase : public TestCase
14.586 +{
14.587 +public:
14.588 + FindPathTestCase ();
14.589 + virtual ~FindPathTestCase ();
14.590 +
14.591 +private:
14.592 + virtual bool DoRun (void);
14.593 + virtual void DoTeardown (void);
14.594 +};
14.595 +
14.596 +FindPathTestCase::FindPathTestCase ()
14.597 + : TestCase ("Check Names::FindPath functionality")
14.598 +{
14.599 +}
14.600 +
14.601 +FindPathTestCase::~FindPathTestCase ()
14.602 +{
14.603 +}
14.604 +
14.605 +void
14.606 +FindPathTestCase::DoTeardown (void)
14.607 +{
14.608 + Names::Delete ();
14.609 +}
14.610 +
14.611 +bool
14.612 +FindPathTestCase::DoRun (void)
14.613 +{
14.614 + std::string found;
14.615 +
14.616 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.617 + Names::Add ("Name", objectOne);
14.618 +
14.619 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.620 + Names::Add ("/Names/Name/Child", childOfObjectOne);
14.621 +
14.622 + found = Names::FindPath (objectOne);
14.623 + NS_TEST_ASSERT_MSG_EQ (found, "/Names/Name", "Could not Names::Add and Names::FindPath an Object");
14.624 +
14.625 + found = Names::FindPath (childOfObjectOne);
14.626 + NS_TEST_ASSERT_MSG_EQ (found, "/Names/Name/Child", "Could not Names::Add and Names::FindPath a child Object");
14.627 +
14.628 + Ptr<TestObject> objectNotThere = CreateObject<TestObject> ();
14.629 + found = Names::FindPath (objectNotThere);
14.630 + NS_TEST_ASSERT_MSG_EQ (found, "", "Unexpectedly found a non-existent Object");
14.631 +
14.632 + return false;
14.633 +}
14.634 +
14.635 +// ===========================================================================
14.636 +// Test case to make sure that the Object Name Service can find Objects using
14.637 +// the lowest level find function, which is:
14.638 +//
14.639 +// Find (Ptr<Object> context, std::string name);
14.640 +// ===========================================================================
14.641 +class BasicFindTestCase : public TestCase
14.642 +{
14.643 +public:
14.644 + BasicFindTestCase ();
14.645 + virtual ~BasicFindTestCase ();
14.646 +
14.647 +private:
14.648 + virtual bool DoRun (void);
14.649 + virtual void DoTeardown (void);
14.650 +};
14.651 +
14.652 +BasicFindTestCase::BasicFindTestCase ()
14.653 + : TestCase ("Check low level Names::Find functionality")
14.654 +{
14.655 +}
14.656 +
14.657 +BasicFindTestCase::~BasicFindTestCase ()
14.658 +{
14.659 +}
14.660 +
14.661 +void
14.662 +BasicFindTestCase::DoTeardown (void)
14.663 +{
14.664 + Names::Delete ();
14.665 +}
14.666 +
14.667 +bool
14.668 +BasicFindTestCase::DoRun (void)
14.669 +{
14.670 + Ptr<TestObject> found;
14.671 +
14.672 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.673 + Names::Add ("Name One", objectOne);
14.674 +
14.675 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.676 + Names::Add ("Name Two", objectTwo);
14.677 +
14.678 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.679 + Names::Add ("Name One/Child", childOfObjectOne);
14.680 +
14.681 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.682 + Names::Add ("Name Two/Child", childOfObjectTwo);
14.683 +
14.684 + found = Names::Find<TestObject> (Ptr<Object> (0, false), "Name One");
14.685 + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via object context");
14.686 +
14.687 + found = Names::Find<TestObject> (Ptr<Object> (0, false), "Name Two");
14.688 + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via object context");
14.689 +
14.690 + found = Names::Find<TestObject> (objectOne, "Child");
14.691 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via object context");
14.692 +
14.693 + found = Names::Find<TestObject> (objectTwo, "Child");
14.694 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via object context");
14.695 +
14.696 + return false;
14.697 +}
14.698 +
14.699 +// ===========================================================================
14.700 +// Test case to make sure that the Object Name Service can find Objects using
14.701 +// a string context-based find function, which is:
14.702 +//
14.703 +// Find (std::string context, std::string name);
14.704 +// ===========================================================================
14.705 +class StringContextFindTestCase : public TestCase
14.706 +{
14.707 +public:
14.708 + StringContextFindTestCase ();
14.709 + virtual ~StringContextFindTestCase ();
14.710 +
14.711 +private:
14.712 + virtual bool DoRun (void);
14.713 + virtual void DoTeardown (void);
14.714 +};
14.715 +
14.716 +StringContextFindTestCase::StringContextFindTestCase ()
14.717 + : TestCase ("Check string context-based Names::Find functionality")
14.718 +{
14.719 +}
14.720 +
14.721 +StringContextFindTestCase::~StringContextFindTestCase ()
14.722 +{
14.723 +}
14.724 +
14.725 +void
14.726 +StringContextFindTestCase::DoTeardown (void)
14.727 +{
14.728 + Names::Delete ();
14.729 +}
14.730 +
14.731 +bool
14.732 +StringContextFindTestCase::DoRun (void)
14.733 +{
14.734 + Ptr<TestObject> found;
14.735 +
14.736 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.737 + Names::Add ("Name One", objectOne);
14.738 +
14.739 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.740 + Names::Add ("Name Two", objectTwo);
14.741 +
14.742 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.743 + Names::Add ("Name One/Child", childOfObjectOne);
14.744 +
14.745 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.746 + Names::Add ("Name Two/Child", childOfObjectTwo);
14.747 +
14.748 + found = Names::Find<TestObject> ("/Names", "Name One");
14.749 + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context");
14.750 +
14.751 + found = Names::Find<TestObject> ("/Names", "Name Two");
14.752 + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context");
14.753 +
14.754 + found = Names::Find<TestObject> ("/Names/Name One", "Child");
14.755 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context");
14.756 +
14.757 + found = Names::Find<TestObject> ("/Names/Name Two", "Child");
14.758 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via string context");
14.759 +
14.760 + return false;
14.761 +}
14.762 +
14.763 +// ===========================================================================
14.764 +// Test case to make sure that the Object Name Service can find Objects using
14.765 +// a fully qualified path name-based find function, which is:
14.766 +//
14.767 +// Find (std::string name);
14.768 +// ===========================================================================
14.769 +class FullyQualifiedFindTestCase : public TestCase
14.770 +{
14.771 +public:
14.772 + FullyQualifiedFindTestCase ();
14.773 + virtual ~FullyQualifiedFindTestCase ();
14.774 +
14.775 +private:
14.776 + virtual bool DoRun (void);
14.777 + virtual void DoTeardown (void);
14.778 +};
14.779 +
14.780 +FullyQualifiedFindTestCase::FullyQualifiedFindTestCase ()
14.781 + : TestCase ("Check fully qualified path Names::Find functionality")
14.782 +{
14.783 +}
14.784 +
14.785 +FullyQualifiedFindTestCase::~FullyQualifiedFindTestCase ()
14.786 +{
14.787 +}
14.788 +
14.789 +void
14.790 +FullyQualifiedFindTestCase::DoTeardown (void)
14.791 +{
14.792 + Names::Delete ();
14.793 +}
14.794 +
14.795 +bool
14.796 +FullyQualifiedFindTestCase::DoRun (void)
14.797 +{
14.798 + Ptr<TestObject> found;
14.799 +
14.800 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.801 + Names::Add ("/Names/Name One", objectOne);
14.802 +
14.803 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.804 + Names::Add ("/Names/Name Two", objectTwo);
14.805 +
14.806 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.807 + Names::Add ("/Names/Name One/Child", childOfObjectOne);
14.808 +
14.809 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.810 + Names::Add ("/Names/Name Two/Child", childOfObjectTwo);
14.811 +
14.812 + found = Names::Find<TestObject> ("/Names/Name One");
14.813 + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context");
14.814 +
14.815 + found = Names::Find<TestObject> ("/Names/Name Two");
14.816 + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context");
14.817 +
14.818 + found = Names::Find<TestObject> ("/Names/Name One/Child");
14.819 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context");
14.820 +
14.821 + found = Names::Find<TestObject> ("/Names/Name Two/Child");
14.822 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via string context");
14.823 +
14.824 + return false;
14.825 +}
14.826 +
14.827 +// ===========================================================================
14.828 +// Test case to make sure that the Object Name Service can find Objects using
14.829 +// a relative path name-based find function, which is:
14.830 +//
14.831 +// Find (std::string name);
14.832 +// ===========================================================================
14.833 +class RelativeFindTestCase : public TestCase
14.834 +{
14.835 +public:
14.836 + RelativeFindTestCase ();
14.837 + virtual ~RelativeFindTestCase ();
14.838 +
14.839 +private:
14.840 + virtual bool DoRun (void);
14.841 + virtual void DoTeardown (void);
14.842 +};
14.843 +
14.844 +RelativeFindTestCase::RelativeFindTestCase ()
14.845 + : TestCase ("Check relative path Names::Find functionality")
14.846 +{
14.847 +}
14.848 +
14.849 +RelativeFindTestCase::~RelativeFindTestCase ()
14.850 +{
14.851 +}
14.852 +
14.853 +void
14.854 +RelativeFindTestCase::DoTeardown (void)
14.855 +{
14.856 + Names::Delete ();
14.857 +}
14.858 +
14.859 +bool
14.860 +RelativeFindTestCase::DoRun (void)
14.861 +{
14.862 + Ptr<TestObject> found;
14.863 +
14.864 + Ptr<TestObject> objectOne = CreateObject<TestObject> ();
14.865 + Names::Add ("Name One", objectOne);
14.866 +
14.867 + Ptr<TestObject> objectTwo = CreateObject<TestObject> ();
14.868 + Names::Add ("Name Two", objectTwo);
14.869 +
14.870 + Ptr<TestObject> childOfObjectOne = CreateObject<TestObject> ();
14.871 + Names::Add ("Name One/Child", childOfObjectOne);
14.872 +
14.873 + Ptr<TestObject> childOfObjectTwo = CreateObject<TestObject> ();
14.874 + Names::Add ("Name Two/Child", childOfObjectTwo);
14.875 +
14.876 + found = Names::Find<TestObject> ("Name One");
14.877 + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context");
14.878 +
14.879 + found = Names::Find<TestObject> ("Name Two");
14.880 + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context");
14.881 +
14.882 + found = Names::Find<TestObject> ("Name One/Child");
14.883 + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context");
14.884 +
14.885 + found = Names::Find<TestObject> ("Name Two/Child");