1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_REG_TEST_H
25 #define PXR_BASE_TF_REG_TEST_H
26 
27 /// \file tf/regTest.h
28 /// \ingroup group_tf_Internal
29 /// Support for simple regression tests.
30 
31 #include "pxr/pxr.h"
32 
33 #include "pxr/base/tf/api.h"
34 #include "pxr/base/tf/singleton.h"
35 #include "pxr/base/tf/hash.h"
36 #include "pxr/base/tf/hashmap.h"
37 #include <string>
38 
39 PXR_NAMESPACE_OPEN_SCOPE
40 
41 /// \class TfRegTest
42 /// \ingroup group_tf_Internal
43 ///
44 /// \c TfRegTest is a singleton class, which is used to register functions
45 /// with either type \c bool \c (*)(int, char*[]), or functions returning type
46 /// \c bool and taking no arguments.
47 ///
48 /// Here is how \c TfRegTest is used to create tests in separate files, which
49 /// are then compiled into a single standalone executable (and not part of any
50 /// library):
51 ///
52 /// // file: main.cpp
53 /// \include test/main.cpp
54 ///
55 /// // file: hammer.cpp
56 /// \code
57 /// #include "pxr/base/tf/regTest.h"
58 ///
59 /// static bool
60 /// Test_PtHammer()
61 /// {
62 ///     bool success;
63 ///     ...
64 ///     return success;
65 /// }
66 /// TF_ADD_REGTEST(PtHammer);
67 /// \endcode
68 ///
69 /// \code
70 /// // file: drill.cpp
71 /// static bool
72 /// Test_PtDrill(int argc, char *argv[])
73 /// {
74 ///     bool success;
75 ///     ...
76 ///     return success;
77 /// }
78 /// TF_ADD_REGTEST(PtDrill);
79 /// \endcode
80 ///
81 /// When \c main.cpp, \c drill.cpp and \c hammer.cpp are compiled into
82 /// an executable, then a test can be run by invoking the executable
83 /// with a first argument of either \p PtHammer or \p PtDrill.
84 /// Since \p PtHammer is a function without arguments, supplying additional
85 /// arguments is an error; but \p PtDrill takes arguments, so additional
86 /// command-line arguments specified are passed to the function.
87 /// (Most library test functions shouldn't need any arguments.)
88 ///
89 class TfRegTest {
90 public:
91     /// Run a single regression test function, returning 0 if the function
92     /// succeeded and 1 otherwise.
93     ///
94     /// This function is intended to be called as follows:
95     /// \code
96     ///     int main(int argc, char *argv[]) {
97     ///         return TfRegTest::Main(argc, argv);
98     ///     }
99     /// \endcode
100     ///
101     /// The first argument is the name of the test to be run.  If the
102     /// registered test function run takes no arguments, then no arguments
103     /// other than the test name should be supplied.  Otherwise, the \c Main()
104     /// passes \c argc-1 and \c argv+1 to the test function, and the test
105     /// function is responsible for argument checking.
Main(int argc,char * argv[])106     static int Main(int argc, char *argv[]) {
107         return GetInstance()._Main(argc, argv);
108     }
109 
110     TF_API
111     static TfRegTest& GetInstance();
112 
113     /// Type of a function with no arguments.
114     typedef bool (*RegFunc)();
115 
116     /// Type of a function with arguments.
117     ///
118     /// When \c Main(argc,argv) is requested to run a function of type
119     /// \c RegFuncWithArgs, it invokes the function with arguments \c argc-1
120     /// and \c argv+1.
121     typedef bool (*RegFuncWithArgs)(int argc, char *argv[]);
122 
123     TF_API
124     bool Register(const char* name, RegFunc);
125     TF_API
126     bool Register(const char* name, RegFuncWithArgs);
127 
128 private:
129     friend class TfSingleton<TfRegTest>;
130     TF_API
131     int _Main(int argc, char *argv[]);
132 
133     void _PrintTestNames();
134 
135     typedef TfHashMap<std::string, RegFunc, TfHash> _Hash;
136     typedef TfHashMap<std::string, RegFuncWithArgs, TfHash> _HashWithArgs;
137     _Hash _functionTable;
138     _HashWithArgs _functionTableWithArgs;
139 };
140 
141 TF_API_TEMPLATE_CLASS(TfSingleton<TfRegTest>);
142 
143 /// Adds the function Test_\p name, under name \p name, as a runnable
144 /// regression test. Test_\p name must be of type \c RegFunc or
145 /// \c RegFuncWithArgs.
146 ///
147 /// \ingroup group_tf_Internal
148 /// \hideinitializer
149 #define TF_ADD_REGTEST(name)    \
150     bool Tf_RegTst##name = TfRegTest::GetInstance().Register(#name, Test_##name)
151 
152 PXR_NAMESPACE_CLOSE_SCOPE
153 
154 #endif
155