|
Using CeeFIT
Introduction
Java Fit works so nicely because it is so simple. A large part of the
design goals of CeeFIT was to enable developers and business users to have a
similarly simple system for C++ program testing. On the business user
side, CeeFIT is almost identical in function to Java Fit, so from that standpoint
CeeFIT wins there. On the developer side, CeeFIT provides two mechanisms
to develop and register Fixture classes with the system: the macro registration
method and the manual registration method. Which method you pick depends
upon a couple things:
If you ...
- don't need to put anything custom in your constructor or destructor because
your members' constructors/destructors can manage themselves
- don't mind calling macros that do crazy C++ things under-the-hood
- don't need a header file to present your class declaration to other classes
(you will likely need a header declaration for your Fixture if you plan to
create other Fixtures that extend from it.)
then the macro method is for you, life is good!
Otherwise, you will need to manually write and register
your fixture classes (which is no big deal, but not nearly as flashy or helpful
as macros :-).
Macro Registration of Fixtures
CeeFIT macro registration will take your Fixture class fields and methods and
register them automatically with the CeeFIT engine. For the most part,
macro registered Fixtures will only be useful for simple Fixture types like
ColumnFixture.
Luckily, ColumnFixture covers many of the test cases you will likely encounter.
IMPORTANT NOTE: the CeeFIT macros described below only work when
called from .cpp files; do not try to put your Fixtures created with macros
in header files, that will likely yield compiler or linker errors.
Here is an example of a working ColumnFixture class file in the global namespace
that uses macros (if your test code will reside in namespaces, use namespace-aware
versions of the macros):
1
|
#include "ceefit.h"
|
2 |
|
|
3
|
declare_fit_module(ExampleMultiply);
|
4 |
|
5 |
begin_fit_fixture(MULTIPLY,
CEEFIT::COLUMNFIXTURE, fitexample.Multiply) |
6 |
fit_var(int,
x); |
7 |
|
8 |
fit_var(int,
y); |
9 |
|
10 |
fit_test(multiply,
int) |
11 |
{ |
12 |
return
(x * y); |
13 |
} |
14 |
end_fit_fixture(MULTIPLY) |
|
Figure 1 - multiply.cpp: A CeeFIT ColumnFixture
that has x, y, and multiply() columns.
|
Line descriptions:
1 - You must include the ceefit.h header to get all of the CeeFIT declarations,
ceefit.h should be in the root of your CeeFIT installation and that folder should
be added to your compiler's include path.
3 - declare_fit_module() is an optional macro you can add to your CeeFIT
Fixture modules. (The word module being used here refers to .cpp files.)
Many linkers will selectively exclude modules if other modules do not reference
them, declare_fit_module() macro provides an anchor by which your Fixture's
module can be referenced by another module. Use the force_link_fit_module()
macro in the module containing your main() function to create that reference
and force the linker to link in your Fixture module. Many linkers will
NOT selectively exclude modules that cannot be directly referenced back to the
main() module. Your C++ environment may not require the use of
these macros to link and execute CeeFIT fixtures.
| Macro |
Prototype
|
declare_fit_module(symbolicName) |
| Description |
Creates a variable synthesized from symbolicName
that is used as an anchor point from which the linker can tie Fixture
modules to the module containing your main() method. declare_fit_module()
should always appear in the global namespace. A semi-colon is required
after the declare_fit_module() macro. |
| Parameters |
- symbolicName : Can be any globally unique symbolic name.
Follows naming rules for any C++ symbol.
|
| Macro |
Prototype
|
force_link_fit_module(symbolicName) |
| Description |
Links a module containing the symbolicName to the
current module. force_link_fit_module() should always appear
in the global namespace. A semi-colon is required after the force_link_fit_module()
macro. |
| Parameters |
- symbolicName : Can be any globally unique symbolic name.
Follows naming rules for any C++ symbol.
|
5 - The begin_fit_fixture() macro denotes the start of a CeeFIT Fixture
class. begin_fit_fixture() also defines a constructor and destructor for
the Fixture class. In this macro you specify what the name of your Fixture
class will be, which base class your Fixture class will extend, and an alias
for your classname. This Fixture class is a simple multiplication test,
and the base class for the multiply fixutre is CEEFIT::COLUMNFIXTURE.
The aliasName is saved as a string and allows for the CeeFIT engine to
match HTML tables written for another language. This Fixture class will
match to a Fit HTML table named "MULTIPLY" and the alias allows it
to match to a table named "fitexample.Multiply", which is the name
of a corresponding Java Fixture class.
| Macro |
Prototype
|
begin_fit_fixture(className, extendsFrom, aliasName) |
| Description |
Begins a CeeFIT Fixture class. begin_fit_fixture()
must appear only in the global namespace. A semi-colon is optional
after the begin_fit_fixture() macro. The default access rights
established by begin_fit_fixture() is public. |
| Parameters |
- className : The name of your Fixture class.
- extendsFrom : The fully qualified name of the Fixture class your
class will extend from.
- aliasName : An alias that your Fixture class can match to.
|
6, 8 - The fit_var() macro defines a named variable that is automatically
registered with the CeeFIT engine to match against columns in the input HTML
files. These lines register int variables with the names x
and y.
| Macro |
Prototype
|
fit_var(varType,
varName) |
| Description |
Defines and registers a CeeFIT Fixture variable for the
current Fixture class. A semi-colon is required after the fit_var()
macro. |
| Parameters |
- varType : The type of the member variable for your CeeFIT
Fixture.
- varName : The name of the member variable for your CeeFIT
Fixture. varName is case-sensitive and its spelling must
match exactly to expected items in the input HTML tables.
|
10-13 - The fit_test() macro defines a named no-arg test method that
is automatically registered with the CeeFIT engine to match against columns
in the input HTML files. This line registers a test method called multiply
that returns an int that is compared against expected result data in
the input HTML files.
| Macro |
Prototype
|
fit_test(testName,
returnType) |
| Description |
Defines and registers a CeeFIT Fixture test method for
the current Fixture class. This macro must be followed by a C++
code block that returns an object of type returnType. The
function defined is a virtual function. |
| Parameters |
- testName : The name of the CeeFIT Fixture test method.
testName is case-sensitive and its spelling must match exactly
to expected items in the input HTML tables.
- returnType : The type of the object returned from your CeeFIT
Fixture test method.
|
14 - The end_fit_fixture() macro closes off the end of your CeeFIT Fixture
class.
| Macro |
Prototype
|
end_fit_fixture(className) |
| Description |
Finishes a CeeFIT Fixture class. end_fit_fixture()
must appear only in the global namespace. A semi-colon is optional
after the end_fit_fixture() macro. |
| Parameters |
- className : The name of your Fixture class, must match the
name used in the corresponding begin_fit_fixture().
|
Namespace-Aware Macro Registrations
If you use namespaces in your C++ programs and wish for your Fixture classes
to be namespaced as well, you may do so using the namespace-aware macros provided
with CeeFIT. Here is an example of the namespace-aware macros:
1 |
#include "ceefit.h" |
|
2
|
|
|
3
|
declare_fit_module(FatDivide);
|
4 |
|
5 |
using namespace CEEFIT; |
6 |
|
7 |
namespace CEEFAT |
8 |
{ |
9 |
begin_namespaced_fit_fixture(CEEFAT,
DIVIDE, COLUMNFIXTURE, fat.Divide) |
10 |
fit_var(int,
x); |
11 |
|
12 |
fit_var(int,
y); |
13 |
|
14 |
fit_test(divide,
int) |
15 |
{ |
16 |
if(y
== 0) |
17 |
{
|
18 |
STRING
message; |
19 |
message
+= x; |
20 |
message
+= "/"; |
21 |
message
+= y; |
22 |
throw
new DIVIDEBYZEROEXCEPTION(message); |
23 |
}
|
24 |
return
x/y; |
25 |
} |
26 |
end_namespaced_fit_fixture(CEEFAT, DIVIDE)
|
27 |
}; |
|
Figure 2 - divide.cpp: A CeeFIT ColumnFixture
in the CEEFAT namespace that has x, y, and divide()
columns.
|
Line descriptions:
5 - You may use the using keyword to import all symbols
in the CEEFIT namespace. We do this here, and as a result, COLUMNFIXTURE
does not need to be prefaced by "CEEFIT::" later on in the code.
7 - This is opens the CEEFAT namespace. In Java Fit, the "fat"
package contains some additional classes used in certifying Fit complies with
the Fit Specification 1.1. CeeFIT ships with a port of the fat package,
and that code resides in the "CEEFAT" namespace.
9 - begin_namespaced_fit_fixture() macro is like begin_fit_fixture()
except that the first parameter is the name of the namespace that the current
fixture will be added to. If the namespace the Fixture is being added
to is more than one level deep, separate your namespace names with the scope
resolution "::" operator. (eg. MYGAME::FITTESTS).
The name of the class is combined with the namespace and that forms the complete
name which the CeeFIT engine can use to match the class with HTML input tables.
In the case of this Fixture, it will match to the strings "CEEFAT::DIVIDE"
and the alias "fat.Divide".
| Macro |
Prototype
|
begin_namespaced_fit_fixture(namespaceName,
className, extendsFrom, aliasName) |
| Description |
Begins a namespaced CeeFIT Fixture class. begin_namespaced_fit_fixture()
must appear in a namespace (other than global). A semi-colon is
optional after the begin_namespaced_fit_fixture() macro. |
| Parameters |
- namespaceName : The namespace your Fixture class is in.
Do not end this name with a scope resolution operator "::".
- className : The name of your Fixture class.
- extendsFrom : The fully qualified name of the Fixture class
your class will extend from.
- aliasName : An alias that your Fixture class can match
to.
|
16-24 - This CeeFIT test is all about testing integer division.
In order to avoid a processor fault due to div/0, we compare the y
variable with 0 first. If it is zero, we build an exception reason STRING
variable and throw a DIVIDEBYZEROEXCEPTION back to the CeeFIT engine.
Otherwise, we return x/y.
26 - end_namespaced_fit_fixture() macro terminates your Fixture class
just like the end_fit_fixture() does. You must specify the namespace
name and Fixture class name in this macro.
| Macro |
Prototype
|
end_namespaced_fit_fixture(namespaceName,
className) |
| Description |
Finishes a namespaced CeeFIT Fixture class. end_namespaced_fit_fixture()
must appear in a namespace (other than global). A semi-colon is
optional after the end_namespaced_fit_fixture() macro. |
| Parameters |
- namespaceName : The namespace your Fixture class is in.
Do not end this name with a scope resolution operator "::".
- className : The name of your Fixture class, must match
the name used in the corresponding begin_namespaced_fit_fixture().
|
27 - This brace closes off the CEEFAT namespace body.
Manual Registration of Fixtures
The manual registration of Fixtures achieves the same definition/registration
of your Fixture classes similarly to the macro registration style. On
the plus side, you have more control over the features your Fixture class can
sport and things generally look more C++-ish than with the macros. On
the minus side, it's generally takes more effort for you to get things working
correctly.
Here is an example of a PrimitiveFixture-based Fixture class that
could only be pulled off with the manual Fixture registration technique.
PrimitiveFixtures do not use reflection to match columns with member methods
and variables. Rather, with a class that extends PrimitiveFixture, you
manually inspect the PARSE object passed to you and process it however you like.
PARSE is a value class that contains the parsed HTML tables which you can navigate
to read and process elements.
In the case of the Table Fixture here, it is used as a tool.
It is very common when writing Fit tests to nest the construction and processing
of Fixture classes. The Table Fixture is a very simple demonstration of
this type of containment: the rows of the table below the title row are parsed
and run through the CeeFIT engine as another Fixture.
1 |
using namespace CEEFIT;
|
2 |
|
3 |
namespace CEEFAT |
4 |
{ |
5 |
class TABLE : public
PRIMITIVEFIXTURE |
6 |
{ |
7 |
public:
|
8 |
static
PTR<PARSE> Table; |
9 |
|
10 |
inline
ceefit_init_spec TABLE(void) {} |
11 |
inline
virtual ceefit_dtor_spec
~TABLE(void) {} |
12 |
|
13 |
virtual
void ceefit_call_spec DoRows(PTR<PARSE>& rows);
|
14 |
static
VALUE<PARSE> ceefit_call_spec Copy(PTR<PARSE>&
tree); |
15 |
|
16 |
virtual
void ceefit_call_spec ReleaseStatics(void); |
17 |
|
18 |
private:
|
19 |
ceefit_init_spec
TABLE(const TABLE&); |
20 |
TABLE&
ceefit_init_spec operator=(const TABLE&); |
21 |
}; |
22 |
}; |
|
Figure 3 - table.h: A CeeFIT PrimitiveFixture
in the CEEFAT namespace.
|
Line descriptions:
5 - This is the class declaration, where class TABLE extends from
CEEFIT::PRIMITIVEFIXTURE. Always extend from any Fixture base class with
the "public" scope.
8- This is a static variable used by the TABLE class that keeps track of a copy
of the rows following the title row. This static variable is interesting
in that it references a variable that was dynamically allocated from the CeeFIT
heap. In order to ensure that this field is deallocated before the
program terminates, we add code later on that manages the memory for those statics.
10-11- It is your responsibility to define the default constructor and destructor
for any FIXTURE's you manually register. This is in contrast to the macro
registration technique where the constructor and destructor are provided for
you.
| Calling
Specification |
Name
|
ceefit_init_spec |
| Description |
Your default constructor and copy constructor,
destructor must be declared and defined with the ceefit_init_spec
call spec, (if you want it to be cross-platform enough to compile with
Borland C++ Builder or MSVC6.) |
| Calling
Specification |
Name
|
ceefit_dtor_spec |
| Description |
Your destructor must be declared and defined
with the ceefit_dtor_spec call spec, (if you want it to be cross-platform
enough to compile with Borland C++ Builder or MSVC6.) |
13 - DoRows() is a Fixture base class function. Overriding this
method allows you to define the action taken when the CeeFIT engine goes to
process all rows of the Fixture.
| Calling
Specification |
Name
|
ceefit_call_spec |
| Description |
operator=() and any other methods in
your class must be declared and defined with the ceefit_call_spec
call spec, (if you want it to be cross-platform enough to compile with
Borland C++ Builder or MSVC6.) This rule holds for any functions
in manual or macro-registered Fixtures. |
14 - The static utiltiy Copy() method does a deep copy of the
PARSE object passed, returning a pointer to the PARSE object in a VALUE<PARSE>
object.
16 - ReleaseStatics() is a method you can override in your Fixture classes
if you have static variables that need to have their memory released before
the CeeFIT engine completes a call to CEEFIT::Run(). If you implement
ReleaseStatics(), you must make sure to call the base class' ReleaseStatics()
method in the body of your method so that its statics get released as well.
In your ReleaseStatics() method, make sure to set any pointers or PTR
objects to null to eliminate the potential for double-frees.
19-20 - declare (but do not define) any standard functions that you do not need
with private access rights.
The following code is the corresponding table.cpp file for table.h:
1
|
#include "ceefit.h"
|
2 |
#include "fat/Table.h" |
3 |
|
4 |
declare_fit_module(FatTable);
|
5 |
|
6 |
using namespace CEEFIT; |
7 |
|
8 |
namespace CEEFAT |
9 |
{ |
10 |
PTR<PARSE>
TABLE::Table(null); |
11 |
|
12 |
void ceefit_call_spec
TABLE::DoRows(PTR<PARSE>& rows) |
13 |
{ |
14 |
PTR<PARSE>
nullParse(null); |
15 |
STRING
emptyString; |
16 |
|
17 |
PTR<PARSE>
rowCopy(Copy(rows)); |
18 |
TABLE::Table
= new PARSE(STRING("table"), emptyString, rowCopy, nullParse);
|
19 |
|
20 |
// evaluate
the rest of the table like a runner |
21 |
PTR<FIXTURE>
temp(new FIXTURE()); |
22 |
temp->DoTables(TABLE::Table);
|
23 |
} |
24 |
|
25 |
VALUE<PARSE>
ceefit_call_spec TABLE::Copy(PTR<PARSE>& tree) |
26 |
{ |
27 |
if(tree
== null) |
28 |
{ |
29 |
return(VALUE<PARSE>(null));
|
30 |
} |
31 |
else
|
32 |
{ |
33 |
PTR<PARSE>
treeParts(Copy(tree->Parts)); |
34 |
PTR<PARSE>
treeMore(Copy(tree->More)); |
35 |
|
36 |
return(VALUE<PARSE>(new
PARSE(tree->Tag, tree->Body, treeParts, treeMore))); |
37 |
} |
38 |
} |
39 |
|
40 |
void ceefit_call_spec
TABLE::ReleaseStatics(void) |
41 |
{ |
42 |
TABLE::Table
= null; |
43 |
|
44 |
this->PRIMITIVEFIXTURE::ReleaseStatics();
|
45 |
} |
46 |
|
47 |
static REGISTERFIXTURECLASS<
TABLE > FatTableFixtureRegistration("CEEFAT::TABLE",
|
48 |
"fat.Table");
|
49 |
}; |
|
Figure 4 - table.cpp: A CeeFIT PrimitiveFixture
definition in the CEEFAT namespace.
|
Line descriptions:
4 - We did not forget the declare_fit_module()/force_link_fit_module()
in case the compiler/linker isn't clever enough to link in table.cpp as is.
10 - The definition of the static Table variable.
12-23 - The DoRows() method. Builds a PARSE object by copying
the remaining rows object and then performs a DoTables() on it so that it
gets acted on as if it were a table.
25-38 - static Copy() method. Recursively traverses
and builds a copy of the passed PARSE object.
40-45 - The ReleaseStatics() method. Just before CEEFIT::Run()
returns, a temporary instance of every registered Fixture is created and ReleaseStatics()
is invoked. This ReleaseStatics() method null's the static Table
variable and then calls the super class' ReleaseStatics() method.
By implementing ReleaseStatics(), we ensure that the memory for TABLE::Table
is released before the end of the CeeFIT run.
47 - The static instance of the REGISTERFIXTURECLASS<T> class
registers the class with the CeeFIT engine. This line pulls the whole
system together. To register your Fixture class, make sure you create
a static instance of REGISTERFIXTURECLASS<T> where T equals your Fixture
class.
|
Manual
Fixture Registration
|
Constructor
|
REGISTERFIXTURECLASS<T>(const
char* fullClassName,
const
char* aliasName) |
| Description |
Registers a Fixture class with the CeeFIT engine.
You should call this constructor from a static instance of REGISTERFIXTURECLASS<T>.
|
Template
Parameters |
- T : The Fixture class you wish to register with the CeeFIT
engine.
|
| Parameters |
- fullClassName : The fully qualified name of the class (including
namespace) as it will show up in the input HTML tables.
- aliasName : An alias name for your Fixture.
|
The following example Fixture shows how to manually register member methods
and variables so that base classes like ColumnFixture or ActionFixture
can use reflect on your Fixture's features to set its variables or call its
methods. The actual function of this Fixture is to serve as a data container
for individual pieces of music that flow through the Music Example - one of
the supplemental test suites that show off Fit's abilities. Anywhere reflection
is required in your classes, you will need to ultimately derive from FIXTURE
so that functions like RegisterCeefitField() and RegisterCeefitTest()
can safely be called. Completely understanding this class and what its
purpose is not required for this example - what is important is to see how the
default constructor is used here to register fields and tests (member variables
and methods) with the CeeFIT engine for this Fixture.
1
|
#include "tools/alloc.h"
|
2 |
#include "ceefit.h" |
3 |
#include "eg/eg.h" |
4 |
|
5 |
declare_fit_module(MusicClass);
|
6 |
using namespace CEEFIT; |
7 |
|
8 |
namespace EG_MUSIC |
9 |
{ |
10 |
const char* MUSIC::Status
= "ready"; |
11 |
|
12 |
ceefit_init_spec
MUSIC::MUSIC() |
13 |
{ |
14 |
Size
= 0L; |
15 |
Seconds
= 0; |
16 |
TrackNumber
= 0; |
17 |
TrackCount
= 0; |
18 |
Year
= 0; |
19 |
Selected
= false; |
20 |
|
21 |
RegisterCeefitField(this,
"title", Title); |
22 |
RegisterCeefitField(this,
"artist", Artist); |
23 |
RegisterCeefitField(this,
"album", Album); |
24 |
RegisterCeefitField(this,
"genre", Genre); |
25 |
RegisterCeefitField(this,
"size", Size); |
26 |
RegisterCeefitField(this,
"seconds", Seconds); |
27 |
RegisterCeefitField(this,
"trackNumber", TrackNumber); |
28 |
RegisterCeefitField(this,
"trackCount", TrackCount); |
29 |
RegisterCeefitField(this,
"year", Year); |
30 |
RegisterCeefitField(this,
"date", Date); |
31 |
RegisterCeefitField(this,
"selected", Selected); |
32 |
|
33 |
RegisterCeefitTest(this,
"track", &MUSIC::Track); |
34 |
RegisterCeefitTest(this,
"time", &MUSIC::Time); |
35 |
} |
36 |
|
37 |
ceefit_dtor_spec
MUSIC::~MUSIC() |
38 |
{ |
39 |
} |
40 |
|
41 |
MUSIC& ceefit_call_spec
MUSIC::operator=(const MUSIC& aMusic) |
42 |
{ |
43 |
Title
= aMusic.Title; |
44 |
Artist
= aMusic.Artist; |
45 |
Album
= aMusic.Album; |
46 |
Genre
= aMusic.Genre; |
47 |
Size
= aMusic.Size; |
48 |
Seconds
= aMusic.Seconds; |
49 |
TrackNumber
= aMusic.TrackNumber; |
50 |
TrackCount
= aMusic.TrackCount; |
51 |
Year
= aMusic.Year; |
52 |
Date
= aMusic.Date; |
53 |
Selected
= aMusic.Selected; |
54 |
|
55 |
return(*this);
|
56 |
} |
57 |
|
58 |
STRING ceefit_call_spec
MUSIC::Track() |
59 |
{ |
60 |
return(STRING()
+ TrackNumber + " of " + TrackCount); |
61 |
} |
62 |
|
63 |
double ceefit_call_spec
MUSIC::Time() |
64 |
{ |
65 |
double
result = (Seconds / 0.6); |
66 |
if(Seconds
>= 0) |
67 |
{ |
68 |
return(floor(result
+ 0.5) / 100.0); |
69 |
} |
70 |
else
|
71 |
{ |
72 |
return(ceil(result
+ 0.5) / 100.0); |
73 |
} |
74 |
} |
75 |
|
76 |
int ceefit_call_spec
MUSIC::GetHashCode(void) const |
77 |
{ |
78 |
return(Title.GetHashCode());
|
79 |
} |
80 |
|
81 |
bool ceefit_call_spec
MUSIC::IsEqual(const MUSIC& aMusic) const |
82 |
{ |
83 |
return(Title.IsEqual(aMusic.Title)
|
84 |
&&
Artist.IsEqual(aMusic.Artist) |
85 |
&&
Album.IsEqual(aMusic.Album) |
86 |
&&
Genre.IsEqual(aMusic.Genre) |
87 |
&&
Size == aMusic.Size |
88 |
&&
Seconds == aMusic.Seconds |
89 |
&&
TrackNumber == aMusic.TrackNumber |
90 |
&&
TrackCount == aMusic.TrackCount |
91 |
&&
Year == aMusic.Year |
92 |
&&
Date.IsEqual(aMusic.Date) |
93 |
&&
Selected == aMusic.Selected); |
94 |
} |
95 |
|
96 |
STRING ceefit_call_spec
MUSIC::ToString() const |
97 |
{ |
98 |
if(Title.Length()
== 0) |
99 |
{ |
100 |
return
Title; |
101 |
} |
102 |
else
|
103 |
{ |
104 |
return
this->OBJECT::ToString(); |
105 |
} |
106 |
} |
107 |
|
108 |
|
109 |
// Factory //////////////////////////////////
|
110 |
|
111 |
VALUE ceefit_call_spec
MUSIC::Parse(const STRING& string) |
112 |
{ |
113 |
MUSIC*
m = new MUSIC(); |
114 |
|
115 |
DYNARRAY
parsedTokens; |
116 |
Tokenize(parsedTokens,
string, STRING("\t")); |
117 |
int
i = 0; |
118 |
m->Title
= parsedTokens[i++]; |
119 |
m->Artist
= parsedTokens[i++]; |
120 |
m->Album
= parsedTokens[i++]; |
121 |
m->Genre
= parsedTokens[i++]; |
122 |
if(!swscanf(parsedTokens[i++].GetBuffer(),
L"%li", &m->Size) || |
123 |
!swscanf(parsedTokens[i++].GetBuffer(),
L"%i", &m->Seconds) || |
124 |
!swscanf(parsedTokens[i++].GetBuffer(),
L"%i", &m->TrackNumber) || |
125 |
!swscanf(parsedTokens[i++].GetBuffer(),
L"%i", &m->TrackCount) || |
126 |
!swscanf(parsedTokens[i++].GetBuffer(),
L"%i", &m->Year)) |
127 |
{ |
128 |
throw
new PARSEEXCEPTION("Parse exception in MUSIC::Parse"); |
129 |
} |
130 |
m->Date.Parse(parsedTokens[i++]);
|
131 |
|
132 |
return
VALUE(m); |
133 |
} |
134 |
|
135 |
STRING ceefit_call_spec
MUSIC::Dump() const |
136 |
{ |
137 |
STRING
out; |
138 |
STRING
numberParts; |
139 |
STRING
dateString(Date.ToString()); |
140 |
|
141 |
SafeSprintf(numberParts,
L"%li\t%i\t%i\t%i\t%i", |
142 |
Size,
Seconds, TrackNumber, TrackCount, Year); |
143 |
|
144 |
out
+= Title + "\t" + Artist + "\t" + Album + "\t" + Genre +
|
145 |
"\t"
+ numberParts + "\t" + dateString; |
146 |
|
147 |
return(out);
|
148 |
} |
149 |
|
150 |
static REGISTERFIXTURECLASS<
MUSIC > MusicRegistration("EG_MUSIC::MUSIC", |
151 |
"eg.music.Music");
|
152 |
}; |
|
Figure 5 - music.cpp: The Music class
from the MusicExample definition in the EG_MUSIC namespace.
|
Line descriptions:
12-35 - In manually registered fixtures, the constructor is responsible for
initializing member variables and then registering with the CeeFIT engine any
variables and tests that will be discovered via reflection later on. Two
methods are used to achieve this: RegisterCeefitField() and RegisterCeefitTest().
|
Manual
Fixture Registration
|
Function
|
template<class
FIXTURETYPE, class FIELDTYPE>
void RegisterCeefitField(FIXTURETYPE* fixture,
const
char* fieldName,
FIELDTYPE&
actualField) |
| Description |
Registers a member variable by name and reference with
the CeeFIT engine for a given Fixture. You should only call this
method from the constructor of your Fixture class. Invoking this
method on a given member variable allows that variable to be accessed
via the pseudo-reflection API's provided by the CeeFIT engine. |
Template
Parameters |
- FIXTURETYPE : The Fixture class your member variable being
registered belongs to.
- FIELDTYPE : The type of the member variable being registered.
This type must have a template specialization for FITFIELD<T>
class where T equals FIELDTYPE. That specialization
must be defined in a header that is included in any modules invoking
RegisterCeefitField() or the REGISTERFIXTURECLASS<T>
constructor for the associated Fixture class.
|
| Parameters |
- fixture : The Fixture containing member variable to be registered.
- fieldName : A character string with the exact, case sensitive
name of the field being registered as it should match to items in
the input HTML tables. Note, this string need not match the
case nor spelling of actualField (but it should match exactly
if you care about stuff like that.)
- actualField : A reference to the actual member variable being
registered within fixture.
|
|
Manual
Fixture Registration
|
Function
|
template<class
FIXTURETYPE, class RETURNTYPE>
void RegisterCeefitTest(FIXTURETYPE* fixture,
const char* testName,
RETURNTYPE
(ceefit_call_spec FIXTURETYPE::*testFunc)(void)) |
| Description |
Registers a no-arg pointer-to-method by name with the CeeFIT
engine for a given Fixture. You should only call this method from
the constructor of your Fixture class. Invoking this method on a
given method allows that method to be called via the pseudo-reflection
API's provided by the CeeFIT engine. |
Template
Parameters |
- FIXTURETYPE : The Fixture class your method being registered
belongs to.
- RETURNTYPE : The type of the variable returned by testFunc.
This type must have a template specialization for FITFIELD<T>
class where T equals RETURNTYPE. That specialization
must be defined in a header that is included in any modules invoking
RegisterCeefitTest() or the REGISTERFIXTURECLASS<T>
constructor for the associated Fixture class.
|
| Parameters |
- fixture : The Fixture containing the method to be registered.
- testName : A character string with the exact, case sensitive
name of the method being registered as it should match to items in
the input HTML tables. Note, this string need not match the
case nor spelling of testFunc (but it should match exactly
if you care about stuff like that.)
- testFunc : A pointer to the no-arg method being registered
within fixture.
|
|
Manual Fixture
Registration
|
Function
|
template<class
FIXTURETYPE, class RETURNTYPE, class ARGTYPE>
void RegisterCeefitTest(FIXTURETYPE* fixture,
const char* testName,
RETURNTYPE
(ceefit_call_spec FIXTURETYPE::*testFunc)(ARGTYPE)) |
| Description |
Registers a one arg pointer-to-method by name with the
CeeFIT engine for a given Fixture. You should only call this method
from the constructor of your Fixture class. Invoking this method
on a given method allows that method to be called via the pseudo-reflection
API's provided by the CeeFIT engine. One arg methods are used only
by advanced Fixture types like RowFixture.
There is no macro equivalent for one arg method registration. |
Template
Parameters |
- FIXTURETYPE : The Fixture class your method being registered
belongs to.
- RETURNTYPE : The type of the variable returned by testFunc.
This type must have a template specialization for FITFIELD<T>
class where T equals RETURNTYPE. That specialization
must be defined in a header that is included in any modules invoking
RegisterCeefitTest() or the REGISTERFIXTURECLASS<T>
constructor for the associated Fixture class.
- ARGTYPE : The type of the argument to testFunc.
|
| Parameters |
- fixture : The Fixture containing the method to be registered.
- testName : A character string with the exact, case sensitive
name of the method being registered as it should match to items in
the input HTML tables. Note, this string need not match the
case nor spelling of testFunc (but it should match exactly
if you care about stuff like that.)
- testFunc : A pointer to the one arg method being registered
within fixture.
|
|