unit-tests-common.h
Go to the documentation of this file.
1 // License: Apache 2.0. See LICENSE file in root directory.
2 // Copyright(c) 2015 Intel Corporation. All Rights Reserved.
3 #pragma once
4 #ifndef LIBREALSENSE_UNITTESTS_COMMON_H
5 #define LIBREALSENSE_UNITTESTS_COMMON_H
6 
7 #include "catch/catch.hpp"
8 #include <librealsense/rs.h>
9 #include <limits> // For std::numeric_limits
10 #include <cmath> // For std::sqrt
11 #include <cassert> // For assert
12 #include <thread> // For std::this_thread::sleep_for
13 #include <map>
14 #include <mutex>
15 #include <condition_variable>
16 #include <atomic>
17 
18 // noexcept is not accepted by Visual Studio 2013 yet, but noexcept(false) is require on throwing destructors on gcc and clang
19 // It is normally advisable not to throw in a destructor, however, this usage is safe for require_error/require_no_error because
20 // they will only ever be created as temporaries immediately before being passed to a C ABI function. All parameters and return
21 // types are vanilla C types, and thus nothrow-copyable, and the function itself cannot throw because it is a C ABI function.
22 // Therefore, when a temporary require_error/require_no_error is destructed immediately following one of these C ABI function
23 // calls, we should not have any exceptions in flight, and can freely throw (perhaps indirectly by calling Catch's REQUIRE()
24 // macro) to indicate postcondition violations.
25 #ifdef WIN32
26 #define NOEXCEPT_FALSE
27 #else
28 #define NOEXCEPT_FALSE noexcept(false)
29 #endif
30 
31 // Can be passed to rs_error ** parameters, requires that an error is indicated with the specific provided message
33 {
35  bool validate_error_message; // Messages content may vary , subject to backend selection
37 public:
38  require_error(std::string message,bool message_validation=true) : message(move(message)), validate_error_message(message_validation), err() {}
39  require_error(const require_error &) = delete;
41  {
42  assert(!std::uncaught_exception());
43  REQUIRE(err != nullptr);
44  if (validate_error_message)
45  {
46  REQUIRE(rs_get_error_message(err) == std::string(message));
47  }
48  }
49  require_error & operator = (const require_error &) = delete;
50  operator rs_error ** () { return &err; }
51 };
52 
53 // Can be passed to rs_error ** parameters, requires that no error is indicated
55 {
57 public:
58  require_no_error() : err() {}
59  require_no_error(const require_error &) = delete;
61  {
62  assert(!std::uncaught_exception());
63  REQUIRE(rs_get_error_message(err) == rs_get_error_message(nullptr)); // Perform this check first. If an error WAS indicated, Catch will display it, making our debugging easier.
64  REQUIRE(err == nullptr);
65  }
66  require_no_error & operator = (const require_no_error &) = delete;
67  operator rs_error ** () { return &err; }
68 };
69 
70 // RAII wrapper to ensure that contexts are always cleaned up. rs_context has singleton
71 // semantics, and creation will fail if an undeleted previous context still exists.
73 {
75  safe_context(int) : context() {}
76 public:
78  {
80  REQUIRE(context != nullptr);
81  }
82 
84  {
85  if(context)
86  {
87  rs_delete_context(context, nullptr);
88  }
89  }
90 
91  bool operator == (const safe_context& other) const { return context == other.context; }
92 
93  operator rs_context * () const { return context; }
94 };
95 
96 
97 // Compute dot product of a and b
98 inline float dot_product(const float (& a)[3], const float (& b)[3])
99 {
100  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
101 }
102 
103 // Compute length of v
104 inline float vector_length(const float (& v)[3])
105 {
106  return std::sqrt(dot_product(v, v));
107 }
108 
109 // Require that r = cross(a, b)
110 inline void require_cross_product(const float (& r)[3], const float (& a)[3], const float (& b)[3])
111 {
112  REQUIRE( r[0] == Approx(a[1]*b[2] - a[2]*b[1]) );
113  REQUIRE( r[1] == Approx(a[2]*b[0] - a[0]*b[2]) );
114  REQUIRE( r[2] == Approx(a[0]*b[1] - a[1]*b[0]) );
115 }
116 
117 // Require that vector is exactly the zero vector
118 inline void require_zero_vector(const float (& vector)[3])
119 {
120  for(int i=1; i<3; ++i) REQUIRE( vector[i] == 0.0f );
121 }
122 
123 // Require that a == transpose(b)
124 inline void require_transposed(const float (& a)[9], const float (& b)[9])
125 {
126  REQUIRE( a[0] == Approx(b[0]) );
127  REQUIRE( a[1] == Approx(b[3]) );
128  REQUIRE( a[2] == Approx(b[6]) );
129  REQUIRE( a[3] == Approx(b[1]) );
130  REQUIRE( a[4] == Approx(b[4]) );
131  REQUIRE( a[5] == Approx(b[7]) );
132  REQUIRE( a[6] == Approx(b[2]) );
133  REQUIRE( a[7] == Approx(b[5]) );
134  REQUIRE( a[8] == Approx(b[8]) );
135 }
136 
137 // Require that matrix is an orthonormal 3x3 matrix
138 inline void require_rotation_matrix(const float (& matrix)[9])
139 {
140  const float row0[] = {matrix[0], matrix[3], matrix[6]};
141  const float row1[] = {matrix[1], matrix[4], matrix[7]};
142  const float row2[] = {matrix[2], matrix[5], matrix[8]};
143  REQUIRE( dot_product(row0, row0) == Approx(1) );
144  REQUIRE( dot_product(row1, row1) == Approx(1) );
145  REQUIRE( dot_product(row2, row2) == Approx(1) );
146  REQUIRE( dot_product(row0, row1) == Approx(0) );
147  REQUIRE( dot_product(row1, row2) == Approx(0) );
148  REQUIRE( dot_product(row2, row0) == Approx(0) );
149  require_cross_product(row0, row1, row2);
150  require_cross_product(row0, row1, row2);
151  require_cross_product(row0, row1, row2);
152 }
153 
154 // Require that matrix is exactly the identity matrix
155 inline void require_identity_matrix(const float (& matrix)[9])
156 {
157  static const float identity_matrix_3x3[] = {1,0,0, 0,1,0, 0,0,1};
158  for(int i=0; i<9; ++i) REQUIRE( matrix[i] == identity_matrix_3x3[i] );
159 }
160 
164  std::chrono::high_resolution_clock::time_point start_time , end_time;
168 };
169 
170 inline void check_fps(double actual_fps, double configured_fps)
171 {
172  REQUIRE(actual_fps >= configured_fps * 0.9); // allow threshold of 10 percent
173 }
174 
175 struct stream_mode { rs_stream stream; int width, height; rs_format format; int framerate; };
176 
177 // All streaming tests are bounded by time or number of frames, which comes first
178 const int max_capture_time_sec = 3; // Each streaming test configuration shall not exceed this threshold
179 const uint32_t max_frames_to_receive = 50; // Max frames to capture per streaming tests
180 
181 inline void test_wait_for_frames(rs_device * device, std::initializer_list<stream_mode>& modes, std::map<rs_stream, test_duration>& duration_per_stream)
182 {
185 
187 
188  int lowest_fps_mode = std::numeric_limits<int>::max();
189  for(auto & mode : modes)
190  {
191  REQUIRE( rs_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
192  REQUIRE( rs_get_frame_data(device, mode.stream, require_no_error()) != nullptr );
193  REQUIRE( rs_get_frame_timestamp(device, mode.stream, require_no_error()) >= 0 );
194  if (mode.framerate < lowest_fps_mode) lowest_fps_mode = mode.framerate;
195  }
196 
197  std::vector<unsigned long long> last_frame_number;
198  std::vector<unsigned long long> number_of_frames;
199  last_frame_number.resize(RS_STREAM_COUNT);
200  number_of_frames.resize(RS_STREAM_COUNT);
201 
202  for (auto& elem : modes)
203  {
204  number_of_frames[elem.stream] = 0;
205  last_frame_number[elem.stream] = 0;
206  }
207 
208  const auto actual_frames_to_receive = std::min(max_frames_to_receive, (uint32_t)(max_capture_time_sec* lowest_fps_mode));
209  const auto first_frame_to_capture = (actual_frames_to_receive*0.1); // Skip the first 10% of frames
210  const auto frames_to_capture = (actual_frames_to_receive - first_frame_to_capture);
211 
212  for (auto i = 0u; i< actual_frames_to_receive; ++i)
213  {
215 
216  if (i < first_frame_to_capture) continue; // Skip some frames at the beginning to stabilize the stream output
217 
218  for (auto & mode : modes)
219  {
220  if (rs_get_frame_timestamp(device, mode.stream, require_no_error()) > 0)
221  {
222  REQUIRE( rs_is_stream_enabled(device, mode.stream, require_no_error()) == 1);
223  REQUIRE( rs_get_frame_data(device, mode.stream, require_no_error()) != nullptr);
224  REQUIRE( rs_get_frame_timestamp(device, mode.stream, require_no_error()) >= 0);
225 
226  auto frame_number = rs_get_frame_number(device, mode.stream, require_no_error());
227  if (!duration_per_stream[mode.stream].is_end_time_initialized && last_frame_number[mode.stream] != frame_number)
228  {
229  last_frame_number[mode.stream] = frame_number;
230  ++number_of_frames[mode.stream];
231  }
232 
233  if (!duration_per_stream[mode.stream].is_start_time_initialized && number_of_frames[mode.stream] >= 1)
234  {
235  duration_per_stream[mode.stream].start_time = std::chrono::high_resolution_clock::now();
236  duration_per_stream[mode.stream].is_start_time_initialized = true;
237  }
238 
239  if (!duration_per_stream[mode.stream].is_end_time_initialized && (number_of_frames[mode.stream] > (0.9 * frames_to_capture))) // Requires additional work for streams with different fps
240  {
241  duration_per_stream[mode.stream].end_time = std::chrono::high_resolution_clock::now();
242  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(duration_per_stream[mode.stream].end_time - duration_per_stream[mode.stream].start_time).count();
243  auto fps = ((double)number_of_frames[mode.stream] / duration) * 1000.;
244  //check_fps(fps, mode.framerate); // requires additional work to configure UVC controls in order to achieve the required fps
245  duration_per_stream[mode.stream].is_end_time_initialized = true;
246  }
247  }
248  }
249  }
250 
251  rs_stop_device(device, require_no_error());
253 }
254 
255 
256 static std::mutex m;
257 static std::mutex cb_mtx;
258 static std::condition_variable cv;
259 static std::atomic<bool> stop_streaming;
260 static int done;
261 struct user_data{
262  std::map<rs_stream, test_duration> duration_per_stream;
263  std::map<rs_stream, unsigned> number_of_frames_per_stream;
264 };
265 
266 
267 inline void frame_callback(rs_device * dev, rs_frame_ref * frame, void * user)
268 {
269  std::lock_guard<std::mutex> lock(cb_mtx);
270 
271  if (stop_streaming || (rs_get_detached_frame_timestamp(frame, require_no_error()) == 0))
272  {
273  rs_release_frame(dev, frame, require_no_error());
274  return;
275  }
276 
277  auto data = (user_data*)user;
278  bool stop = true;
279 
280  auto stream_type = rs_get_detached_frame_stream_type(frame, require_no_error());
281 
282  for (auto& elem : data->number_of_frames_per_stream)
283  {
284  if (elem.second < data->duration_per_stream[stream_type].actual_frames_to_receive)
285  {
286  stop = false;
287  break;
288  }
289  }
290 
291 
292  if (stop)
293  {
294  stop_streaming = true;
295  rs_release_frame(dev, frame, require_no_error());
296  {
297  std::lock_guard<std::mutex> lk(m);
298  done = true;
299  }
300  cv.notify_one();
301  return;
302  }
303 
304  if (data->duration_per_stream[stream_type].is_end_time_initialized)
305  {
306  rs_release_frame(dev, frame, require_no_error());
307  return;
308  }
309 
310  unsigned num_of_frames = 0;
311  num_of_frames = (++data->number_of_frames_per_stream[stream_type]);
312 
313  if (num_of_frames >= data->duration_per_stream[stream_type].actual_frames_to_receive)
314  {
315  if (!data->duration_per_stream[stream_type].is_end_time_initialized)
316  {
317  data->duration_per_stream[stream_type].end_time = std::chrono::high_resolution_clock::now();
318  data->duration_per_stream[stream_type].is_end_time_initialized = true;
319  }
320 
321  rs_release_frame(dev, frame, require_no_error());
322  return;
323  }
324 
325  if ((num_of_frames == data->duration_per_stream[stream_type].first_frame_to_capture) && (!data->duration_per_stream[stream_type].is_start_time_initialized)) // Skip some frames at the beginning to stabilize the stream output
326  {
327  data->duration_per_stream[stream_type].start_time = std::chrono::high_resolution_clock::now();
328  data->duration_per_stream[stream_type].is_start_time_initialized = true;
329  }
330 
331  REQUIRE( rs_get_detached_frame_data(frame, require_no_error()) != nullptr );
333 
334  rs_release_frame(dev, frame, require_no_error());
335 }
336 
337 inline void test_frame_callback(rs_device * device, std::initializer_list<stream_mode>& modes, std::map<rs_stream, test_duration>& duration_per_stream)
338 {
339  done = false;
340  stop_streaming = false;
341  user_data data;
342  data.duration_per_stream = duration_per_stream;
343  for(auto & mode : modes)
344  {
345  data.number_of_frames_per_stream[mode.stream] = 0;
346  data.duration_per_stream[mode.stream].is_start_time_initialized = false;
347  data.duration_per_stream[mode.stream].is_end_time_initialized = false;
348  data.duration_per_stream[mode.stream].actual_frames_to_receive = std::min(max_frames_to_receive, (uint32_t)(max_capture_time_sec* mode.framerate));
349  data.duration_per_stream[mode.stream].first_frame_to_capture = (uint32_t)(data.duration_per_stream[mode.stream].actual_frames_to_receive*0.1); // Skip the first 10% of frames
350  data.duration_per_stream[mode.stream].frames_to_capture = data.duration_per_stream[mode.stream].actual_frames_to_receive - data.duration_per_stream[mode.stream].first_frame_to_capture;
351  REQUIRE( rs_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
352  rs_set_frame_callback(device, mode.stream, frame_callback, &data, require_no_error());
353  }
354 
357 
358  {
359  std::unique_lock<std::mutex> lk(m);
360  cv.wait(lk, [&]{return done;});
361  lk.unlock();
362  }
363 
364  rs_stop_device(device, require_no_error());
366 
367  for(auto & mode : modes)
368  {
369  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(data.duration_per_stream[mode.stream].end_time - data.duration_per_stream[mode.stream].start_time).count();
370  auto fps = ((float)data.number_of_frames_per_stream[mode.stream] / duration) * 1000.;
371  //check_fps(fps, mode.framerate); / TODO is not suppuorted yet. See above message
372  }
373 }
374 
375 inline void motion_callback(rs_device * , rs_motion_data, void *)
376 {
377 }
378 
380 {
381 }
382 
383 // Provide support for doing basic streaming tests on a set of specified modes
384 inline void test_streaming(rs_device * device, std::initializer_list<stream_mode> modes)
385 {
386  std::map<rs_stream, test_duration> duration_per_stream;
387  for(auto & mode : modes)
388  {
389  duration_per_stream.insert(std::pair<rs_stream, test_duration>(mode.stream, test_duration()));
390  rs_enable_stream(device, mode.stream, mode.width, mode.height, mode.format, mode.framerate, require_no_error());
391  REQUIRE( rs_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
392  }
393 
394  test_wait_for_frames(device, modes, duration_per_stream);
395 
396  test_frame_callback(device, modes, duration_per_stream);
397 
398  for(auto & mode : modes)
399  {
400  rs_disable_stream(device, mode.stream, require_no_error());
401  REQUIRE( rs_is_stream_enabled(device, mode.stream, require_no_error()) == 0 );
402  }
403 }
404 
405 inline void test_option(rs_device * device, rs_option option, std::initializer_list<int> good_values, std::initializer_list<int> bad_values)
406 {
407  // Test reading the current value
408  const auto first_value = rs_get_device_option(device, option, require_no_error());
409 
410  // todo - Check that the first value is something sane?
411 
412  // Test setting good values, and that each value set can be subsequently get
413  for(auto value : good_values)
414  {
415  rs_set_device_option(device, option, value, require_no_error());
416  REQUIRE( rs_get_device_option(device, option, require_no_error()) == value );
417  }
418 
419  // Test setting bad values, and verify that they do not change the value of the option
420  const auto last_good_value = rs_get_device_option(device, option, require_no_error());
421  for(auto value : bad_values)
422  {
423  rs_set_device_option(device, option, value, require_error("foo",false));
424  REQUIRE( rs_get_device_option(device, option, require_no_error()) == last_good_value );
425  }
426 
427  // Test that we can reset the option to its original value
428  rs_set_device_option(device, option, first_value, require_no_error());
429  REQUIRE( rs_get_device_option(device, option, require_no_error()) == first_value );
430 }
431 #endif
#define RS_API_VERSION
Definition: rs.h:28
void timestamp_callback(rs_device *, rs_timestamp_data, void *)
uint32_t first_frame_to_capture
require_error(std::string message, bool message_validation=true)
static std::mutex cb_mtx
~require_no_error() NOEXCEPT_FALSE
uint32_t frames_to_capture
Definition: rs.cpp:16
const int max_capture_time_sec
GLuint GLenum matrix
Definition: glext.h:10445
GLint GLint GLsizei GLsizei height
Definition: glext.h:112
void motion_callback(rs_device *, rs_motion_data, void *)
double rs_get_frame_timestamp(const rs_device *device, rs_stream stream, rs_error **error)
Retrieves time at which the latest frame on a stream was captured.
Definition: rs.cpp:474
const void * rs_get_frame_data(const rs_device *device, rs_stream stream, rs_error **error)
Retrieves the contents of the latest frame on a stream.
Definition: rs.cpp:490
int rs_is_device_streaming(const rs_device *device, rs_error **error)
Determines if the device is currently streaming.
Definition: rs.cpp:413
std::string message
GLsizei const GLchar *const * string
Definition: glext.h:683
#define REQUIRE(expr)
Definition: catch.hpp:9333
void rs_delete_context(rs_context *context, rs_error **error)
Frees the relevant context object.
Definition: rs.cpp:106
void rs_set_device_option(rs_device *device, rs_option option, double value, rs_error **error)
Sets the current value of a single option.
Definition: rs.cpp:712
rs_option
Defines general configuration controls.
Definition: rs.h:128
const char * rs_get_error_message(const rs_error *error)
Returns static pointer to error message.
Definition: rs.cpp:751
rs_context * context
void require_cross_product(const float(&r)[3], const float(&a)[3], const float(&b)[3])
std::map< rs_stream, unsigned > number_of_frames_per_stream
GLuint GLuint stream
Definition: glext.h:1774
void require_zero_vector(const float(&vector)[3])
uint32_t actual_frames_to_receive
Exposes librealsense functionality for C compilers.
rs_stream rs_get_detached_frame_stream_type(const rs_frame_ref *frame, rs_error **error)
Retrieves frame stream type.
Definition: rs.cpp:562
const uint32_t max_frames_to_receive
require_error & operator=(const require_error &)=delete
rs_context * rs_create_context(int api_version, rs_error **error)
Creates RealSense context that is required for the rest of the API.
Definition: rs.cpp:75
double rs_get_device_option(rs_device *device, rs_option option, rs_error **error)
Retrieves the current value of a single option.
Definition: rs.cpp:694
void rs_disable_stream(rs_device *device, rs_stream stream, rs_error **error)
Disables a specific stream.
Definition: rs.cpp:241
void rs_set_frame_callback(rs_device *device, rs_stream stream, rs_frame_callback_ptr on_frame, void *user, rs_error **error)
Sets up a frame callback that is called immediately when an image is available, with no synchronizati...
Definition: rs.cpp:306
void frame_callback(rs_device *dev, rs_frame_ref *frame, void *user)
void rs_release_frame(rs_device *device, rs_frame_ref *frame, rs_error **error)
Releases frame handle.
Definition: rs.cpp:577
GLuint GLuint GLsizei count
Definition: glext.h:111
Motion data from gyroscope and accelerometer from the microcontroller.
Definition: rs.h:347
GLfloat f
Definition: glext.h:1868
format
Formats: defines how each stream can be encoded. rs_format specifies how a frame is represented in me...
Definition: rs.hpp:42
void require_rotation_matrix(const float(&matrix)[9])
static std::condition_variable cv
void test_streaming(rs_device *device, std::initializer_list< stream_mode > modes)
void require_transposed(const float(&a)[9], const float(&b)[9])
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * data
Definition: glext.h:223
void rs_enable_stream(rs_device *device, rs_stream stream, int width, int height, rs_format format, int framerate, rs_error **error)
Enables a specific stream and requests specific properties.
Definition: rs.cpp:220
rs_format
Formats: defines how each stream can be encoded.
Definition: rs.h:53
static std::mutex m
float vector_length(const float(&v)[3])
static int done
void test_wait_for_frames(rs_device *device, std::initializer_list< stream_mode > &modes, std::map< rs_stream, test_duration > &duration_per_stream)
double rs_get_detached_frame_timestamp(const rs_frame_ref *frame, rs_error **error)
Retrieves timestamp from frame reference.
Definition: rs.cpp:498
void test_frame_callback(rs_device *device, std::initializer_list< stream_mode > &modes, std::map< rs_stream, test_duration > &duration_per_stream)
Timestamp data from the motion microcontroller.
Definition: rs.h:339
unsigned long long rs_get_frame_number(const rs_device *device, rs_stream stream, rs_error **error)
Retrieves frame number.
Definition: rs.cpp:482
GLboolean GLboolean GLboolean GLboolean a
Definition: glext.h:1104
GLsizei const GLfloat * value
Definition: glext.h:693
const GLdouble * v
Definition: glext.h:232
GLenum mode
Definition: glext.h:1117
bool is_start_time_initialized
float dot_product(const float(&a)[3], const float(&b)[3])
void require_identity_matrix(const float(&matrix)[9])
GLboolean GLboolean GLboolean b
Definition: glext.h:1104
~require_error() NOEXCEPT_FALSE
void test_option(rs_device *device, rs_option option, std::initializer_list< int > good_values, std::initializer_list< int > bad_values)
void check_fps(double actual_fps, double configured_fps)
rs_stream
Streams are different types of data provided by RealSense devices.
Definition: rs.h:33
rs_device * dev
std::map< rs_stream, test_duration > duration_per_stream
static std::atomic< bool > stop_streaming
void rs_wait_for_frames(rs_device *device, rs_error **error)
Blocks until new frames are available.
Definition: rs.cpp:428
void rs_start_device(rs_device *device, rs_error **error)
Begins streaming on all enabled streams for this device.
Definition: rs.cpp:383
void rs_stop_device(rs_device *device, rs_error **error)
Ends data acquisition for the specified source providers.
Definition: rs.cpp:398
bool operator==(const float3 &a, const float3 &b)
Definition: types.h:113
int rs_is_stream_enabled(const rs_device *device, rs_stream stream, rs_error **error)
Determines if a specific stream is enabled.
Definition: rs.cpp:249
GLdouble GLdouble GLdouble r
Definition: glext.h:247
const void * rs_get_detached_frame_data(const rs_frame_ref *frame, rs_error **error)
Retrieves data from frame reference.
Definition: rs.cpp:512
std::chrono::high_resolution_clock::time_point start_time
#define NOEXCEPT_FALSE


librealsense
Author(s): Sergey Dorodnicov , Mark Horn , Reagan Lopez
autogenerated on Fri Mar 13 2020 03:16:18