Skip to content

Commit

Permalink
A temporary directory utility function for file I/O tests. (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
samcunliffe authored Nov 18, 2022
1 parent 97b4686 commit 3d0fe2c
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 65 deletions.
119 changes: 75 additions & 44 deletions tdms/tests/include/unit_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
#pragma once

#include <complex>
#include <filesystem>
#include <random>
#include <string>

namespace tdms_tests {

inline double TOLERANCE = 1e-16; //< Floating-point comparison tolerance
inline double TOLERANCE = 1e-16;//< Floating-point comparison tolerance

/**
/**
* @brief Determines if two numerical values are close by relative comparison.
*
* Checks the truth value of the condition
Expand All @@ -21,54 +24,82 @@ namespace tdms_tests {
* @param tol Relative comparison tolerance
* @param close_to_zero_tol Cutoff value for the "close to zero" criterion
*/
template<typename T>
inline bool is_close(T a, T b, double tol = 1E-10, double close_to_zero_tol = 1E-30) {

auto max_norm = std::max(std::abs(a), std::abs(b));
template<typename T>
inline bool is_close(T a, T b, double tol = 1E-10, double close_to_zero_tol = 1E-30) {

if (max_norm < close_to_zero_tol) {// Prevent dividing by zero
return true;
}
auto max_norm = std::max(std::abs(a), std::abs(b));

return std::abs(a - b) / std::max(std::abs(a), std::abs(b)) < tol;
if (max_norm < close_to_zero_tol) {// Prevent dividing by zero
return true;
}

/**
* @brief Determines whether an error value is better than a benchmark, or sufficiently close to be insignificant.
*
* If the error to check is superior (IE, closer to zero in absolute value) than the benchmark, return true.
* Otherwise, use relative comparison to determine if the errors are sufficiently similar.
*
* @param to_check Numerical error to evaluate suitability of
* @param to_beat Benchmark error
* @param tol Relative comparison tolerance
* @param close_to_zero_tol Cutoff value for the "close to zero" criterion
* @return true to_check is a superior or equivalent error to to_beat
* @return false to_check is an inferior error
*/
template<typename T>
inline bool is_close_or_better(T to_check, T to_beat, double tol = 1E-10, double close_to_zero_tol = 1E-30) {
if (std::abs(to_check) < std::abs(to_beat)) {
// return true if the value to_check is better (closer to 0) than to_beat
return true;
} else {
// determine if the numerical values are close by relative comparison
return is_close(to_check, to_beat, tol, close_to_zero_tol);
}
}
return std::abs(a - b) / std::max(std::abs(a), std::abs(b)) < tol;
}

/**
* @brief Computes the Euclidean norm of the vector provided
/**
* @brief Determines whether an error value is better than a benchmark, or sufficiently close to be insignificant.
*
* If the error to check is superior (IE, closer to zero in absolute value) than the benchmark, return true.
* Otherwise, use relative comparison to determine if the errors are sufficiently similar.
*
* @param v Vector or array
* @param end (Inclusive) end of buffer to read vector from
* @param start (Inclusive) start of buffer to read vector from
* @return double Euclidean norm
* @param to_check Numerical error to evaluate suitability of
* @param to_beat Benchmark error
* @param tol Relative comparison tolerance
* @param close_to_zero_tol Cutoff value for the "close to zero" criterion
* @return true to_check is a superior or equivalent error to to_beat
* @return false to_check is an inferior error
*/
template<typename T>
inline double euclidean(T *v, int end, int start = 0) {
double norm_val = 0.;
for (int i = start; i < end; i++) { norm_val += std::norm(v[i]); }
return std::sqrt(norm_val);
template<typename T>
inline bool is_close_or_better(T to_check, T to_beat, double tol = 1E-10,
double close_to_zero_tol = 1E-30) {
if (std::abs(to_check) < std::abs(to_beat)) {
// return true if the value to_check is better (closer to 0) than to_beat
return true;
} else {
// determine if the numerical values are close by relative comparison
return is_close(to_check, to_beat, tol, close_to_zero_tol);
}
}

/**
* @brief Computes the Euclidean norm of the vector provided
*
* @param v Vector or array
* @param end (Inclusive) end of buffer to read vector from
* @param start (Inclusive) start of buffer to read vector from
* @return double Euclidean norm
*/
template<typename T>
inline double euclidean(T *v, int end, int start = 0) {
double norm_val = 0.;
for (int i = start; i < end; i++) { norm_val += std::norm(v[i]); }
return std::sqrt(norm_val);
}

/**
* @brief Create a temporary directory for writing files.
*
* Creates a subdirectory (with randomised name) in the system tmp. This can be
* used for writing files when testing file I/O and field exporters and similar.
*
* @warning You should probably clean up after yourself: call
* `std::filesystem::remove_all` when you're done.
*
* @return std::filesystem::path Path to the temporary directory.
*/
inline std::filesystem::path create_tmp_dir() {
// random number setup
std::random_device device_seed;
std::mt19937 random_number_generator(device_seed());

// get system tmp directory (OS-dependent), add a uniquely named subdirectory
auto tmp = std::filesystem::temp_directory_path();
std::string subdir = "tdms_unit_tests_" + std::to_string(random_number_generator());
auto path = tmp / subdir;

// mkdir and return the path to the directory we've just created
std::filesystem::create_directory(path);
return path;
}

}// namespace tdms_tests
53 changes: 32 additions & 21 deletions tdms/tests/unit/field_tests/test_TDFieldExporter2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,39 @@

#include <catch2/catch_test_macros.hpp>

#include "unit_test_utils.h"

TEST_CASE("TDFieldExporter2D") {
// create a directory to write to
auto temporary_directory = tdms_tests::create_tmp_dir();

// create a 0-field for us to export
const int I_tot = 8, J_tot = 1, K_tot = 8;
ElectricSplitField Es(I_tot, J_tot, K_tot);
Es.allocate_and_zero();

// create our field exporter
TDFieldExporter2D tdfe2d;
tdfe2d.folder_name = ".";
int stride;

SECTION("Stride too small") {
int nI = 4, nK = 4;
stride = 1;
REQUIRE_NOTHROW(tdfe2d.allocate(nI, nK));
REQUIRE_THROWS_AS(tdfe2d.export_field(Es, stride, 0), std::runtime_error);
}
SECTION("Stride able to write out") {
int nI = 8, nK = 8;
stride = 2;
REQUIRE_NOTHROW(tdfe2d.allocate(nI, nK));
REQUIRE_NOTHROW(tdfe2d.export_field(Es, stride, 0));
// block to ensure everything is out of scope before we remove the directory
{
// create a 0-field for us to export
const int I_tot = 8, J_tot = 1, K_tot = 8;
ElectricSplitField Es(I_tot, J_tot, K_tot);
Es.allocate_and_zero();


// create our field exporter
TDFieldExporter2D tdfe2d;
tdfe2d.folder_name = temporary_directory.c_str();
int stride;

SECTION("Stride too small") {
int nI = 4, nK = 4;
stride = 1;
REQUIRE_NOTHROW(tdfe2d.allocate(nI, nK));
REQUIRE_THROWS_AS(tdfe2d.export_field(Es, stride, 0), std::runtime_error);
}
SECTION("Stride able to write out") {
int nI = 8, nK = 8;
stride = 2;
REQUIRE_NOTHROW(tdfe2d.allocate(nI, nK));
REQUIRE_NOTHROW(tdfe2d.export_field(Es, stride, 0));
}
}

// tear down - remove the exported files
std::filesystem::remove_all(temporary_directory);
}

0 comments on commit 3d0fe2c

Please sign in to comment.