@@ -91,3 +91,134 @@ release. Once this is done perform a final round of updates:
9191* Update BlueBrain Spack recipe to use the archive and not the Git commit.
9292* Update the upstream Spack recipe.
9393
94+ ## Writing Tests
95+ ### Generate Multi-Dimensional Test Data
96+ Input array of any dimension and type can be generated using the template class
97+ ` DataGenerator ` . For example:
98+ ```
99+ auto dims = std::vector<size_t>{4, 2};
100+ auto values = testing::DataGenerator<std::vector<std::array<double, 2>>::create(dims);
101+ ```
102+ Generates an ` std::vector<std::array<double, 2>> ` initialized with suitable
103+ values.
104+
105+ If "suitable" isn't specific enough, one can specify a callback:
106+ ```
107+ auto callback = [](const std::vector<size_t>& indices) {
108+ return 42.0;
109+ }
110+
111+ auto values = testing::DataGenerator<std::vector<double>>::create(dims, callback);
112+ ```
113+
114+ The ` dims ` can be generated via ` testing::DataGenerator::default_dims ` or by
115+ using ` testing::DataGenerator::sanitize_dims ` . Remember, that certain
116+ containers are fixed size and that we often compute the number of elements by
117+ multiplying the dims.
118+
119+ ### Generate Scalar Test Data
120+ To generate a single "suitable" element use template class ` DefaultValues ` , e.g.
121+ ```
122+ auto default_values = testing::DefaultValues<double>();
123+ auto x = testing::DefaultValues<double>(indices);
124+ ```
125+
126+ ### Accessing Elements
127+ To access a particular element from an unknown container use the following trait:
128+ ```
129+ using trait = testing::ContainerTraits<std::vector<std::array<int, 2>>;
130+ // auto x = values[1][0];
131+ auto x = trait::get(values, {1, 0});
132+
133+ // values[1][0] = 42.0;
134+ trait::set(values, {1, 0}, 42.0);
135+ ```
136+
137+ ### Utilities For Multi-Dimensional Arrays
138+ Use ` testing::DataGenerator::allocate ` to allocate an array (without filling
139+ it) and ` testing::copy ` to copy an array from one type to another. There's
140+ ` testing::ravel ` , ` testing::unravel ` and ` testing::flat_size ` to compute the
141+ position in a flat array from a multi-dimensional index, the reverse and the
142+ number of element in the multi-dimensional array.
143+
144+ ### Deduplicating DataSet and Attribute
145+ Due to how HighFive is written testing ` DataSet ` and ` Attribute ` often requires
146+ duplicating the entire test code because somewhere a ` createDataSet ` must be
147+ replaced with ` createAttribute ` . Use ` testing::AttributeCreateTraits ` and
148+ ` testing::DataSetCreateTraits ` . For example,
149+ ```
150+ template<class CreateTraits>
151+ void check_write(...) {
152+ // Same as one of:
153+ // file.createDataSet(name, values);
154+ // file.createAttribute(name, values);
155+ CreateTraits::create(file, name, values);
156+ }
157+ ```
158+
159+ ### Test Organization
160+ #### Multi-Dimensional Arrays
161+ All tests for reading/writing whole multi-dimensional arrays to datasets or
162+ attributes belong in ` tests/unit/tests_high_five_multi_dimensional.cpp ` . This
163+ includes write/read cycles; checking all the generic edges cases, e.g. empty
164+ arrays and mismatching sizes; and checking non-reallocation.
165+
166+ Read/Write cycles are implemented in two distinct checks. One for writing and
167+ another for reading. When checking writing we read with a "trusted"
168+ multi-dimensional array (a nested ` std::vector ` ), and vice-versa when checking
169+ reading. This matters because certain bugs, like writing a column major array
170+ as if it were row-major can't be caught if one reads it back into a
171+ column-major array.
172+
173+ Remember, ` std::vector<bool> ` is very different from all other ` std::vector ` s.
174+
175+ Every container ` template<class T> C; ` should at least be checked with all of
176+ the following ` T ` s that are supported by the container: ` bool ` , ` double ` ,
177+ ` std::string ` , ` std::vector ` , ` std::array ` . The reason is ` bool ` and
178+ ` std::string ` are special, ` double ` is just a POD, ` std::vector ` requires
179+ dynamic memory allocation and ` std::array ` is statically allocated.
180+
181+ Similarly, each container should be put inside an ` std::vector ` and an
182+ ` std::array ` .
183+
184+ #### Scalar Data Set
185+ Write-read cycles for scalar values should be implemented in
186+ ` tests/unit/tests_high_five_scalar.cpp ` .
187+
188+ #### Data Types
189+ Unit-tests related to checking that ` DataType ` API, go in
190+ ` tests/unit/tests_high_data_type.cpp ` .
191+
192+ #### Selections
193+ Anything selection related goes in ` tests/unit/test_high_five_selection.cpp ` .
194+ This includes things like ` ElementSet ` and ` HyperSlab ` .
195+
196+ #### Strings
197+ Regular write-read cycles for strings are performed along with the other types,
198+ see above. This should cover compatibility of ` std::string ` with all
199+ containers. However, additional testing is required, e.g. character set,
200+ padding, fixed vs. variable length. These all go in
201+ ` tests/unit/test_high_five_string.cpp ` .
202+
203+ #### Specific Tests For Optional Containers
204+ If containers, e.g. ` Eigen::Matrix ` require special checks those go in files
205+ called ` tests/unit/test_high_five_*.cpp ` where ` * ` is ` eigen ` for Eigen.
206+
207+ #### Memory Layout Assumptions
208+ In HighFive we make assumptions about the memory layout of certain types. For
209+ example, we assume that
210+ ```
211+ auto array = std::vector<std::array<double, 2>>(n);
212+ doube * ptr = (double*) array.data();
213+ ```
214+ is a sensible thing to do. We assume similar about ` bool ` and
215+ ` details::Boolean ` . These types of tests go into
216+ ` tests/unit/tests_high_five_memory_layout.cpp ` .
217+
218+ #### H5Easy
219+ Anything ` H5Easy ` related goes in files with the appropriate name.
220+
221+ #### Everything Else
222+ What's left goes in ` tests/unit/test_high_five_base.cpp ` . This covers opening
223+ files, groups, dataset or attributes; checking certain pathological edge cases;
224+ etc.
0 commit comments