add test and validation framework
authorCraig Dowell <craigdo@ee.washington.edu>
Sat Sep 12 19:44:17 2009 -0700 (4 months ago)
changeset 47727b6ae6bf0055
parent 4771 9e018570c659
child 4773 904c1803d5dc
add test and validation framework
doc/testing/Makefile
doc/testing/background.texi
doc/testing/how-to-write-tests.texi
doc/testing/overview.texi
doc/testing/propagation-loss.texi
doc/testing/testing-framework.texi
doc/testing/testing.css
doc/testing/testing.texi
src/common/known.pcap
src/common/pcap-file-test-suite.cc
src/common/pcap-file.cc
src/common/pcap-file.h
src/common/wscript
src/core/names-test-suite.cc
src/core/rng-test-suite.cc
src/core/test.cc
src/core/test.h
src/core/wscript
src/helper/internet-stack-helper.h
src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc
src/test/ns3tcp/ns3tcp-interop-response-vectors.pcap
src/test/ns3tcp/ns3tcp-interop-test-suite.cc
src/test/ns3tcp/ns3tcp.h
src/test/ns3tcp/waf
src/test/ns3tcp/wscript
src/test/ns3wifi/ns3wifi.h
src/test/ns3wifi/propagation-loss-models-test-suite.cc
src/test/ns3wifi/waf
src/test/ns3wifi/wscript
src/wscript
test.py
utils/test-runner.cc
utils/wscript
     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");