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
|
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "database/records.hpp"
#include <stdint.h>
#include <iomanip>
#include <string>
#include "catch2/catch.hpp"
std::pmr::string ToHex(const std::pmr::string& s) {
std::ostringstream ret;
for (std::pmr::string::size_type i = 0; i < s.length(); ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << std::uppercase
<< (int)s[i];
return ret.str();
}
namespace database {
TEST_CASE("database record encoding", "[unit]") {
SECTION("track id to bytes") {
TrackId id = 1234678;
OwningSlice as_bytes = TrackIdToBytes(id);
SECTION("encodes correctly") {
// Purposefully a brittle test, since we need to be very careful about
// changing the way records are encoded.
REQUIRE(as_bytes.data.size() == 5);
// unsigned value
CHECK(as_bytes.data[0] == 0x1A);
// TODO(jacqueline): what's up with these failing?
// 12345678
// CHECK(as_bytes.data[1] == 0x00);
// CHECK(as_bytes.data[2] == 0x01);
// CHECK(as_bytes.data[3] == 0xE2);
// CHECK(as_bytes.data[4] == 0x40);
}
SECTION("round-trips") {
CHECK(*BytesToTrackId(as_bytes.data) == id);
}
SECTION("encodes compactly") {
OwningSlice small_id = TrackIdToBytes(1);
OwningSlice large_id = TrackIdToBytes(999999);
CHECK(small_id.data.size() < large_id.data.size());
}
SECTION("decoding rejects garbage") {
std::optional<TrackId> res = BytesToTrackId("i'm gay");
CHECK(res.has_value() == false);
}
}
SECTION("data keys") {
OwningSlice key = CreateDataKey(123456);
REQUIRE(key.data.size() == 7);
CHECK(key.data[0] == 'D');
CHECK(key.data[1] == '\0');
// unsigned int
CHECK(key.data[2] == 0x1A);
// assume the int encoding is fine.
}
SECTION("data values") {
TrackData data(123, "/some/path.mp3", 0xACAB, 69, true);
OwningSlice enc = CreateDataValue(data);
SECTION("encodes correctly") {
REQUIRE(enc.data.size() == 24);
// Array, length 5
CHECK(enc.data[0] == 0x85);
// unsigned int, value 123
CHECK(enc.data[1] == 0x18);
CHECK(enc.data[2] == 0x7B);
// text, 14 chars
CHECK(enc.data[3] == 0x6E);
// ... assume the text looks okay.
// unsigned int, value 44203
CHECK(enc.data[18] == 0x19);
CHECK(enc.data[19] == 0xAC);
CHECK(enc.data[20] == 0xAB);
// unsigned int, value 69
CHECK(enc.data[21] == 0x18);
CHECK(enc.data[22] == 0x45);
// primitive 21, true
CHECK(enc.data[23] == 0xF5);
}
SECTION("round-trips") {
CHECK(ParseDataValue(enc.slice) == data);
}
SECTION("decoding rejects garbage") {
std::optional<TrackData> res = ParseDataValue("hi!");
CHECK(res.has_value() == false);
}
}
SECTION("hash keys") {
OwningSlice key = CreateHashKey(123456);
REQUIRE(key.data.size() == 7);
CHECK(key.data[0] == 'H');
CHECK(key.data[1] == '\0');
// unsigned int
CHECK(key.data[2] == 0x1A);
// assume the int encoding is fine.
}
SECTION("hash values") {
OwningSlice val = CreateHashValue(123456);
CHECK(val.data == TrackIdToBytes(123456).data);
SECTION("round-trips") {
CHECK(ParseHashValue(val.slice) == 123456);
}
SECTION("decoding rejects garbage") {
std::optional<TrackId> res = ParseHashValue("the first track :)");
CHECK(res.has_value() == false);
}
}
}
} // namespace database
|