aboutsummaryrefslogtreecommitdiff
path: root/tests/README.md
blob: b76d3507d7abfcbf300a68581b5a24d087e2eb39 (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# Crypto tests

## Running crypto Perl tests

Crypto tests require the Math::GMP Perl library, make sure it is installed on you system before running the tests.

Installing dependencies (using cpan):

```
cpan
cpan> install Math::BigInt::GMP
cpan> install Digest::Keccak
```

Running tests:

```
TESTPATH=/path/to/monero/tests
cd $TESTPATH
perl -I $TESTPATH cryptotest.pl
```

Important: must include test path for perl to import cryptolib.pl

## Writing new crypto tests

[TODO]

# Core tests

## Running core tests

Monero uses the Google C++ Testing Framework (`gtest`) to write unit, integration and functional tests for core and other features of the project.
`gtest` runs on top of cmake, and you can run all tests by:

```
cd /path/to/monero
make [-jn] debug-test # where n is number of compiler processes
```

To test a release build, replace `debug-test` with `release-test` in the previous command.

One can also run individual test suites by building monero, then running `ctest` in test suite folders.

Run only the hash tests:

```
cd /path/to/monero
make [-j#] debug
cd build/debug/tests/hash
ctest
```

To run the same tests on a release build, replace `debug` with `release` in previous commands.

## Writing new tests

Based on local tests and Google's guide on creating [simple tests with gtest](https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#simple-tests)

Tests consist of a test harness (defined with the TEST() macro), and the test body consisting of gtest assertions.

Example of a test harness:

```
TEST(test_case_name, test_name) {
 ... test body ...

}
```

As an example in Monero's [crypto unit test](./unit_tests/crypto.cpp):

```
TEST(Crypto, Ostream)
{
  EXPECT_TRUE(is_formatted<crypto::hash8>());
  EXPECT_TRUE(is_formatted<crypto::hash>());
  EXPECT_TRUE(is_formatted<crypto::public_key>());
  EXPECT_TRUE(is_formatted<crypto::secret_key>());
  EXPECT_TRUE(is_formatted<crypto::signature>());
  EXPECT_TRUE(is_formatted<crypto::key_derivation>());
  EXPECT_TRUE(is_formatted<crypto::key_image>());
}

```

The assertions inside the test harness are a bit complex, but fairly straightforward.

- `is_formatted<T>()` is a polymorphic function that accepts the various types of structs defined in [crypto/hash.h](../src/crypto/hash.h).

Just above the test harness, we have the definition for `is_formatted`:

```
  template<typename T>
  bool is_formatted()
  {
    T value{};

    static_assert(alignof(T) == 1, "T must have 1 byte alignment");
    static_assert(sizeof(T) <= sizeof(source), "T is too large for source");
    static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination");
    std::memcpy(std::addressof(value), source, sizeof(T));

    std::stringstream out;
    out << "BEGIN" << value << "END";
    return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END";
  }
```

`T value {}` produces the data member of the struct (`hash8` has `char data[8]`), which runs a number of tests to ensure well structured hash data.

Let's write a new test for the keccak function:

```
  bool keccak_harness()
  {
      size_t inlen = sizeof(source);
      int mdlen = (int)sizeof(md);
      int ret = keccak(source, inlen, md, mdlen);
      if (md[0] != 0x00)
      {
          return true;
      }
      else if (!ret)
      {
          return true;
      }
      else
      {
          return false;
      }
  }
```

This is a basic test that ensures `keccak()` returns successfully when given proper input. It reuses the `source` array for input, and a new byte array `md` for storing the hash digest. Full source is in the [crypto unit test](./unit_tests/crypto.cpp).

Now let's create a new test harness:

```
TEST(Crypto, Keccak)
{
  # ...
  EXPECT_TRUE(keccak_harness());
}

```

This creates a new test under the `Crypto` test case named `Keccak`. The harness includes one assertion `EXPECT_TRUE(keccak_harness())`, which invokes `keccak_harness()`. More complex logic can be added to test various functionality of the `Keccak` library.

To run the new test:

```
cd /path/to/monero
make -jn debug # if no debug build exists
cd build/debug/tests/unit_test
make -jn
make -jn test
```

# Fuzz tests

## Running fuzz tests

```
cd /path/to/monero
make [-jn] fuzz # where n is number of compiler processes
```

or

```
cd path/to/monero
./contrib/fuzz_testing/fuzz.sh
```

## Writing fuzz tests

[TODO]