1 /*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 #include <tap/basic.h>
19 #include <string.h>
20 
21 #include "libknot/rdataset.c"
22 #include "libknot/libknot.h"
23 
24 // Inits rdataset with given rdata.
25 #define RDATASET_INIT_WITH(set, rdata) \
26 	knot_rdataset_clear(&set, NULL); \
27 	ret = knot_rdataset_add(&set, rdata, NULL); \
28 	assert(ret == KNOT_EOK);
29 
rdataset_size(const knot_rdataset_t * rrs)30 static size_t rdataset_size(const knot_rdataset_t *rrs)
31 {
32 	if (rrs == NULL || rrs->count == 0) {
33 		return 0;
34 	}
35 
36 	const knot_rdata_t *last = rr_seek(rrs, rrs->count - 1);
37 	return (uint8_t *)last + knot_rdata_size(last->len) - (uint8_t *)rrs->rdata;
38 }
39 
main(int argc,char * argv[])40 int main(int argc, char *argv[])
41 {
42 	plan_lazy();
43 
44 	// Test init
45 	knot_rdataset_t rdataset;
46 	knot_rdataset_init(&rdataset);
47 	ok(rdataset.rdata == NULL && rdataset.count == 0 && rdataset.size == 0, "rdataset: init.");
48 
49 	// Test rdata addition
50 	uint8_t buf_gt[knot_rdata_size(4)];
51 	knot_rdata_t *rdata_gt = (knot_rdata_t *)buf_gt;
52 	knot_rdata_init(rdata_gt, 4, (uint8_t *)"wxyz");
53 
54 	int ret = knot_rdataset_add(NULL, NULL, NULL);
55 	is_int(KNOT_EINVAL, ret, "rdataset: add NULL.");
56 	ret = knot_rdataset_add(&rdataset, rdata_gt, NULL);
57 	bool add_ok = ret == KNOT_EOK && rdataset.count == 1 &&
58 	              knot_rdata_cmp(rdata_gt, rdataset.rdata) == 0;
59 	ok(add_ok, "rdataset: add.");
60 
61 	uint8_t buf_lo[knot_rdata_size(4)];
62 	knot_rdata_t *rdata_lo = (knot_rdata_t *)buf_lo;
63 	knot_rdata_init(rdata_lo, 4, (uint8_t *)"abcd");
64 	ret = knot_rdataset_add(&rdataset, rdata_lo, NULL);
65 	add_ok = ret == KNOT_EOK && rdataset.count == 2 &&
66 	         knot_rdata_cmp(rdata_lo, rdataset.rdata) == 0;
67 	ok(add_ok, "rdataset: add lower.");
68 
69 	// Test getters
70 	ok(knot_rdata_cmp(knot_rdataset_at(&rdataset, 0), rdata_lo) == 0 &&
71 	   knot_rdata_cmp(knot_rdataset_at(&rdataset, 1), rdata_gt) == 0,
72 	   "rdataset: at.");
73 
74 	ok(rdataset_size(&rdataset) == knot_rdata_size(4) * 2,
75 	   "rdataset: size.");
76 	ok(rdataset.size == rdataset_size(&rdataset), "rdataset: size precomputed (%u %zu).",
77 	   rdataset.size, rdataset_size(&rdataset));
78 
79 	// Test copy
80 	ok(knot_rdataset_copy(NULL, NULL, NULL) == KNOT_EINVAL,
81 	   "rdataset: copy NULL.");
82 	knot_rdataset_t copy;
83 	ret = knot_rdataset_copy(&copy, &rdataset, NULL);
84 	const bool copy_ok = ret == KNOT_EOK && copy.count == rdataset.count &&
85 	                     rdataset_size(&copy) == rdataset_size(&rdataset) &&
86 	                     rdataset.rdata != NULL && copy.rdata != NULL &&
87 	                     memcmp(rdataset.rdata, copy.rdata,
88 	                            rdataset_size(&rdataset)) == 0;
89 	ok(copy_ok, "rdataset: copy");
90 	ok(copy.size == rdataset_size(&copy), "copy: size precomputed.");
91 
92 	// Test eq
93 	ok(knot_rdataset_eq(&rdataset, &copy), "rdataset: equal");
94 
95 	// Test clear
96 	knot_rdataset_clear(&copy, NULL);
97 	ok(copy.count == 0 && copy.rdata == NULL, "rdataset: clear.");
98 
99 	// Test not equal (different count)
100 	ok(!knot_rdataset_eq(&rdataset, &copy), "rdataset: not equal - count");
101 
102 	// Test member
103 	uint8_t buf_not[knot_rdata_size(1)];
104 	knot_rdata_t *not_a_member = (knot_rdata_t *)buf_not;
105 	knot_rdata_init(not_a_member, 1, (uint8_t *)"?");
106 	ok(knot_rdataset_member(&rdataset, rdata_gt), "rdataset: is member.");
107 	ok(!knot_rdataset_member(&rdataset, not_a_member), "rdataset: is not member.");
108 
109 	// Test merge
110 	ok(knot_rdataset_merge(NULL, NULL, NULL) == KNOT_EINVAL,
111 	   "rdataset: merge NULL.");
112 	knot_rdataset_t empty;
113 	knot_rdataset_init(&empty);
114 	ret = knot_rdataset_merge(&empty, &rdataset, NULL);
115 	bool merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&empty, &rdataset);
116 	ok(merge_ok, "rdataset: merge empty.");
117 	knot_rdata_t *data_before = rdataset.rdata;
118 	ret = knot_rdataset_merge(&rdataset, &rdataset, NULL);
119 	merge_ok = ret == KNOT_EOK && rdataset.count == 2 &&
120 	           data_before == rdataset.rdata;
121 	ok(merge_ok, "rdataset: merge self.");
122 
123 	knot_rdataset_clear(&empty, NULL);
124 
125 	// Init structs for merge sort testing
126 	knot_rdataset_t rdataset_lo; // "Lower" rdataset
127 	knot_rdataset_init(&rdataset_lo);
128 	RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
129 	knot_rdataset_t rdataset_gt; // "Greater" rdataset
130 	knot_rdataset_init(&rdataset_gt);
131 	RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
132 
133 	// Test not equal - different data
134 	ok(!knot_rdataset_eq(&rdataset_gt, &rdataset_lo), "rdataset: data not equal.");
135 
136 	// Test that merge keeps the sorted order
137 	ret = knot_rdataset_merge(&rdataset_lo, &rdataset_gt, NULL);
138 	merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_lo, &rdataset);
139 	ok(merge_ok, "rdataset: merge into lower.");
140 
141 	RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
142 	RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
143 	ret = knot_rdataset_merge(&rdataset_gt, &rdataset_lo, NULL);
144 	merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_gt, &rdataset);
145 	ok(merge_ok, "rdataset: merge into greater.");
146 
147 	// Test intersect
148 	ok(knot_rdataset_intersect(NULL, NULL, NULL, NULL) == KNOT_EINVAL,
149 	   "rdataset: intersect NULL.");
150 
151 	knot_rdataset_t intersection;
152 	ret = knot_rdataset_intersect(&rdataset, &rdataset, &intersection, NULL);
153 	bool intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset, &intersection);
154 	ok(intersect_ok, "rdataset: intersect self.");
155 	knot_rdataset_clear(&intersection, NULL);
156 
157 	RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
158 	RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
159 	ret = knot_rdataset_intersect(&rdataset_lo, &rdataset_gt, &intersection, NULL);
160 	intersect_ok = ret == KNOT_EOK && intersection.count == 0;
161 	ok(intersect_ok, "rdataset: intersect no common.");
162 
163 	ret = knot_rdataset_intersect(&rdataset, &rdataset_lo, &intersection, NULL);
164 	intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&intersection, &rdataset_lo);
165 	ok(intersect_ok, "rdataset: intersect normal.");
166 	knot_rdataset_clear(&intersection, NULL);
167 
168 	// Test subtract
169 	ok(knot_rdataset_subtract(NULL, NULL, NULL) == KNOT_EINVAL,
170 	   "rdataset: subtract NULL.");
171 	ret = knot_rdataset_copy(&copy, &rdataset, NULL);
172 	assert(ret == KNOT_EOK);
173 	ok(knot_rdataset_subtract(&copy, &copy, NULL) == KNOT_EOK &&
174 	   copy.count == 0, "rdataset: subtract self.");
175 
176 	ret = knot_rdataset_copy(&copy, &rdataset, NULL);
177 	assert(ret == KNOT_EOK);
178 	ret = knot_rdataset_subtract(&copy, &rdataset, NULL);
179 	bool subtract_ok = ret == KNOT_EOK && copy.count == 0;
180 	ok(subtract_ok, "rdataset: subtract identical.");
181 
182 	RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
183 	RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
184 	data_before = rdataset_lo.rdata;
185 	ret = knot_rdataset_subtract(&rdataset_lo, &rdataset_gt, NULL);
186 	subtract_ok = ret == KNOT_EOK && rdataset_lo.count == 1 &&
187 	              rdataset_lo.rdata == data_before;
188 	ok(subtract_ok, "rdataset: subtract no common.");
189 
190 	ret = knot_rdataset_subtract(&rdataset, &rdataset_gt, NULL);
191 	subtract_ok = ret == KNOT_EOK && rdataset.count == 1;
192 	ok(subtract_ok, "rdataset: subtract the second.");
193 
194 	ret = knot_rdataset_subtract(&rdataset, &rdataset_lo, NULL);
195 	subtract_ok = ret == KNOT_EOK && rdataset.count == 0 &&
196 	              rdataset.rdata == NULL;
197 	ok(subtract_ok, "rdataset: subtract last.");
198 
199 	knot_rdataset_clear(&copy, NULL);
200 	knot_rdataset_clear(&rdataset, NULL);
201 	knot_rdataset_clear(&rdataset_lo, NULL);
202 	knot_rdataset_clear(&rdataset_gt, NULL);
203 
204 	return EXIT_SUCCESS;
205 }
206