summaryrefslogtreecommitdiffstats
path: root/source/test.h
blob: d45c2fefa41692e198946a722de4a7f5d7a66152 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#ifndef EXITEST_TEST_H_INCLUDED
#define EXITEST_TEST_H_INCLUDED

#include <gccore.h>
#include <assert.h>
#include "state.h"
#include "macros.h"

typedef enum {
  TEST_INTERACTIVE = 0x01u,
  TEST_PAUSE_ON_ERROR = 0x02u /* for "essential" tests that should pass */
} et_test_flags;

struct et_test;
struct et_test_plan;

struct et_test {
  struct et_state parent;
  const char *name;
  et_test_flags flags;

  et_state_init_f *test_init_f;
  et_state_cleanup_f *test_cleanup_f;
};

/* 0000eeee eeeeeeee eeeeeeee x00000ss */
/*  e = error code bits,
 *  x = 1 if this test is still in the "clean" (not executed) state
 * ss = status type (pass/fail/etc.) */
typedef u32 et_test_status;

typedef enum {
  TEST_ENONE = (u32)0,

  /* test was aborted (i.e., requested to go to next test without setting a status code) */
  TEST_EABORT,

  /* minimum private test error (define test-private error codes as TEST_EPRIV_BASE+n) */
  TEST_EPRIV_BASE = 32,

  /* largest error code allowed */
  TEST_EMAX = ((u32)1 << 20) - 1
} et_test_error;

typedef enum {
  TEST_SSKIPPED = 0u,
  TEST_SERROR,
  TEST_SFAIL,
  TEST_SPASS,

  TEST_SMAX = ((u32)1 << 2) - 1
} et_test_status_type;

#define TEST_STATUS_GET_ERROR(_status) (et_test_error)(((u32)(_status) >> 8) & TEST_EMAX)
#define TEST_STATUS_GET_TYPE(_status)  (et_test_status_type)((u32)(_status) & TEST_SMAX)
#define TEST_STATUS_IS_CLEAN(_status)  (!!((u32)(_status) & 0x80u))

#define TEST_MAKE_STATUS(_type, _error, _clean) (et_test_status) \
  (((u32)(_type) & TEST_SMAX) |                                  \
  (((u32)(_clean) & 1) << 7)  |                                  \
  (((u32)(_error) & TEST_EMAX) << 8))

#define TEST_SKIPPED   TEST_MAKE_STATUS(TEST_SSKIPPED, TEST_ENONE, 0)
#define TEST_FAIL      TEST_MAKE_STATUS(TEST_SFAIL, TEST_ENONE, 0)
#define TEST_PASS      TEST_MAKE_STATUS(TEST_SPASS, TEST_ENONE, 0)
#define TEST_ERROR(_e) TEST_MAKE_STATUS(TEST_SERROR, _e, 0)

#define TEST_PRIMORDIAL TEST_MAKE_STATUS(TEST_SSKIPPED, TEST_ENONE, 1)

/* Expected setup date: pointer to test plan */
extern const struct et_state *et_state_begin_testing;
extern const struct et_state *et_state_summarize_test_plan;

const char *et_test_get_status_mnemonic(et_test_status status);

struct et_test_plan_entry {
  const struct et_test *test;
  et_test_status status;
  const char *ext_status;
  void (*ext_status_free)(void *);
};

/* a properly initialized test plan will have all the tests' statuses set to TEST_PRIMORDIAL */
struct et_test_plan {
  int exi_channel;

  /* this state will be set after testing is complete.
   * NOTE: next_state_init should somehow point to this test plan,
   * since it's probably dynamically allocated and will be leaked if not
   * freed. */
  const struct et_state *next_state;
  et_state_init_data next_state_init;

  size_t num_tests;
  size_t current_test;
  struct et_test_plan_entry *tests;
};

void et_test_plan_entry_cleanup(struct et_test_plan_entry *entry);

#define FOREACH_TEST(O, _sep) \
  O(check_id)

#define TEST_SYM(_name) PASTE(et_test_, _name)
#define DECL_TEST(_name) extern const struct et_test *const TEST_SYM(_name)

FOREACH_TEST(DECL_TEST, ;);
#undef DECL_TEST

const struct et_test *const *et_get_tests(size_t *ntests);

#if defined(ET_TEST_IMPL) || defined(TEST_INTERNAL)
#define ET__ALLOW_TEST_IMPL_INC
#include "test-impl.inc.h"
#else
#undef FOREACH_TEST
#undef TEST_SYM
#endif

#endif /* include guard */