tests/test-lib: cmocka helpers to simulate path and map discovery
[multipath-tools/.git] / tests / test-lib.c
1 #include <string.h>
2 #include <setjmp.h>
3 #include <stdarg.h>
4 #include <cmocka.h>
5 #include <libudev.h>
6 #include <sys/sysmacros.h>
7 #include "debug.h"
8 #include "util.h"
9 #include "vector.h"
10 #include "structs.h"
11 #include "structs_vec.h"
12 #include "config.h"
13 #include "discovery.h"
14 #include "propsel.h"
15 #include "test-lib.h"
16
17 const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
18 const char default_devnode[] = "sdTEST";
19 const char default_wwid[] = "TEST-WWID";
20 /* default_wwid should be a substring of default_wwid_1! */
21 const char default_wwid_1[] = "TEST-WWID-1";
22
23 /*
24  * Helper wrappers for mock_path().
25  *
26  * We need to make pathinfo() think it has detected a device with
27  * certain vendor/product/rev. This requires faking lots of udev
28  * and sysfs function responses.
29  *
30  * This requires hwtable-test_OBJDEPS = ../libmultipath/discovery.o
31  * in the Makefile in order to wrap calls from discovery.o.
32  *
33  * Note that functions that are called and defined in discovery.o can't
34  * be wrapped this way (e.g. sysfs_get_vendor), because symbols are
35  * resolved by the assembler before the linking stage.
36  */
37
38 int __real_open(const char *path, int flags, int mode);
39
40 static const char _mocked_filename[] = "mocked_path";
41 int __wrap_open(const char *path, int flags, int mode)
42 {
43         condlog(4, "%s: %s", __func__, path);
44
45         if (!strcmp(path, _mocked_filename))
46                 return 111;
47         return __real_open(path, flags, mode);
48 }
49
50 int __wrap_execute_program(char *path, char *value, int len)
51 {
52         char *val = mock_ptr_type(char *);
53
54         condlog(5, "%s: %s", __func__, val);
55         strlcpy(value, val, len);
56         return 0;
57 }
58
59 bool __wrap_is_claimed_by_foreign(struct udev_device *ud)
60 {
61         condlog(5, "%s: %p", __func__, ud);
62         return false;
63 }
64
65 struct udev_list_entry
66 *__wrap_udev_device_get_properties_list_entry(struct udev_device *ud)
67 {
68         void *p = (void*)0x12345678;
69         condlog(5, "%s: %p", __func__, p);
70
71         return p;
72 }
73
74 struct udev_list_entry
75 *__wrap_udev_list_entry_get_next(struct udev_list_entry *udle)
76 {
77         void *p  = NULL;
78         condlog(5, "%s: %p", __func__, p);
79
80         return p;
81 }
82
83 const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *udle)
84 {
85         char *val = mock_ptr_type(char *);
86
87         condlog(5, "%s: %s", __func__, val);
88         return val;
89 }
90
91 struct udev_device *__wrap_udev_device_ref(struct udev_device *ud)
92 {
93         return ud;
94 }
95
96 struct udev_device *__wrap_udev_device_unref(struct udev_device *ud)
97 {
98         return ud;
99 }
100
101 char *__wrap_udev_device_get_subsystem(struct udev_device *ud)
102 {
103         char *val = mock_ptr_type(char *);
104
105         condlog(5, "%s: %s", __func__, val);
106         return val;
107 }
108
109 char *__wrap_udev_device_get_sysname(struct udev_device *ud)
110 {
111         char *val  = mock_ptr_type(char *);
112
113         condlog(5, "%s: %s", __func__, val);
114         return val;
115 }
116
117 char *__wrap_udev_device_get_devnode(struct udev_device *ud)
118 {
119         char *val  = mock_ptr_type(char *);
120
121         condlog(5, "%s: %s", __func__, val);
122         return val;
123 }
124
125 dev_t __wrap_udev_device_get_devnum(struct udev_device *ud)
126 {
127         condlog(5, "%s: %p", __func__, ud);
128         return makedev(17, 17);
129 }
130
131 char *__wrap_udev_device_get_sysattr_value(struct udev_device *ud,
132                                              const char *attr)
133 {
134         char *val  = mock_ptr_type(char *);
135
136         condlog(5, "%s: %s->%s", __func__, attr, val);
137         return val;
138 }
139
140 char *__wrap_udev_device_get_property_value(struct udev_device *ud,
141                                             const char *attr)
142 {
143         char *val  = mock_ptr_type(char *);
144
145         condlog(5, "%s: %s->%s", __func__, attr, val);
146         return val;
147 }
148
149 int __wrap_sysfs_get_size(struct path *pp, unsigned long long *sz)
150 {
151         *sz = 12345678UL;
152         return 0;
153 }
154
155 void *__wrap_udev_device_get_parent_with_subsystem_devtype(
156         struct udev_device *ud, const char *subsys, char *type)
157 {
158         /* return non-NULL for sysfs_get_tgt_nodename */
159         return type;
160 }
161
162 void *__wrap_udev_device_get_parent(struct udev_device *ud)
163 {
164         char *val  = mock_ptr_type(void *);
165
166         condlog(5, "%s: %p", __func__, val);
167         return val;
168 }
169
170 ssize_t __wrap_sysfs_attr_get_value(struct udev_device *dev,
171                                     const char *attr_name,
172                                     char *value, size_t sz)
173 {
174         char *val  = mock_ptr_type(char *);
175
176         condlog(5, "%s: %s", __func__, val);
177         strlcpy(value, val, sz);
178         return strlen(value);
179 }
180
181 int __wrap_checker_check(struct checker *c, int st)
182 {
183         condlog(5, "%s: %d", __func__, st);
184         return st;
185 }
186
187 int __wrap_prio_getprio(struct prio *p, struct path *pp, unsigned int tmo)
188 {
189         int pr = 5;
190
191         condlog(5, "%s: %d", __func__, pr);
192         return pr;
193 }
194
195 struct mocked_path *fill_mocked_path(struct mocked_path *mp,
196                                      const char *vendor, const char *product,
197                                      const char *rev, const char *wwid,
198                                      const char *devnode, unsigned int flags)
199 {
200         mp->vendor = (vendor ? vendor : "noname");
201         mp->product = (product ? product : "noprod");
202         mp->rev = (rev ? rev : "0");
203         mp->wwid = (wwid ? wwid : default_wwid);
204         mp->devnode = (devnode ? devnode : default_devnode);
205         mp->flags = flags|NEED_SELECT_PRIO|NEED_FD;
206         return mp;
207 }
208
209 struct mocked_path *mocked_path_from_path(struct mocked_path *mp,
210                                           const struct path *pp)
211 {
212         mp->vendor = pp->vendor_id;
213         mp->product = pp->product_id;
214         mp->rev = pp->rev;
215         mp->wwid = pp->wwid;
216         mp->devnode = pp->dev;
217         mp->flags = (prio_selected(&pp->prio) ? 0 : NEED_SELECT_PRIO) |
218                 (pp->fd < 0 ? NEED_FD : 0) |
219                 (pp->getuid ? USE_GETUID : 0);
220         return mp;
221 }
222
223 static void mock_sysfs_pathinfo(const struct mocked_path *mp)
224 {
225         static const char hbtl[] = "4:0:3:1";
226
227         will_return(__wrap_udev_device_get_subsystem, "scsi");
228         will_return(__wrap_udev_device_get_sysname, hbtl);
229         will_return(__wrap_udev_device_get_sysname, hbtl);
230         will_return(__wrap_udev_device_get_sysattr_value, mp->vendor);
231         will_return(__wrap_udev_device_get_sysname, hbtl);
232         will_return(__wrap_udev_device_get_sysattr_value, mp->product);
233         will_return(__wrap_udev_device_get_sysname, hbtl);
234         will_return(__wrap_udev_device_get_sysattr_value, mp->rev);
235
236         /* sysfs_get_tgt_nodename */
237         will_return(__wrap_udev_device_get_sysattr_value, NULL);
238         will_return(__wrap_udev_device_get_parent, NULL);
239         will_return(__wrap_udev_device_get_parent, NULL);
240         will_return(__wrap_udev_device_get_sysname, "nofibre");
241         will_return(__wrap_udev_device_get_sysname, "noiscsi");
242         will_return(__wrap_udev_device_get_parent, NULL);
243         will_return(__wrap_udev_device_get_sysname, "ata25");
244 }
245
246 /*
247  * Pretend we detected a SCSI device with given vendor/prod/rev
248  */
249 void mock_pathinfo(int mask, const struct mocked_path *mp)
250 {
251         /* filter_property */
252         will_return(__wrap_udev_device_get_sysname, mp->devnode);
253         if (mp->flags & BL_BY_PROPERTY) {
254                 will_return(__wrap_udev_list_entry_get_name, "BAZ");
255                 return;
256         } else
257                 will_return(__wrap_udev_list_entry_get_name,
258                             "SCSI_IDENT_LUN_NAA_EXT");
259         if (mask & DI_SYSFS)
260                 mock_sysfs_pathinfo(mp);
261
262         if (mp->flags & BL_BY_DEVICE &&
263             (mask & DI_BLACKLIST && mask & DI_SYSFS))
264                 return;
265
266         /* path_offline */
267         will_return(__wrap_udev_device_get_subsystem, "scsi");
268         will_return(__wrap_sysfs_attr_get_value, "running");
269
270         if (mask & DI_NOIO)
271                 return;
272
273         /* fake open() in pathinfo() */
274         if (mp->flags & NEED_FD)
275                 will_return(__wrap_udev_device_get_devnode, _mocked_filename);
276         /* DI_SERIAL is unsupported */
277         assert_false(mask & DI_SERIAL);
278
279         if (mask & DI_WWID) {
280                 if (mp->flags & USE_GETUID)
281                         will_return(__wrap_execute_program, mp->wwid);
282                 else
283                         /* get_udev_uid() */
284                         will_return(__wrap_udev_device_get_property_value,
285                                     mp->wwid);
286         }
287
288         if (mask & DI_CHECKER) {
289                 /* get_state -> sysfs_get_timeout  */
290                 will_return(__wrap_udev_device_get_subsystem, "scsi");
291                 will_return(__wrap_udev_device_get_sysattr_value, "180");
292         }
293
294         if (mask & DI_PRIO && mp->flags & NEED_SELECT_PRIO) {
295
296                 /* sysfs_get_timeout, again (!?) */
297                 will_return(__wrap_udev_device_get_subsystem, "scsi");
298                 will_return(__wrap_udev_device_get_sysattr_value, "180");
299
300         }
301 }
302
303 void mock_store_pathinfo(int mask,  const struct mocked_path *mp)
304 {
305         will_return(__wrap_udev_device_get_sysname, mp->devnode);
306         mock_pathinfo(mask, mp);
307 }
308
309 struct path *__mock_path(vector pathvec,
310                          const char *vnd, const char *prd,
311                          const char *rev, const char *wwid,
312                          const char *dev,
313                          unsigned int flags, int mask)
314 {
315         struct mocked_path mop;
316         struct path *pp;
317         struct config *conf;
318         int r;
319
320         fill_mocked_path(&mop, vnd, prd, rev, wwid, dev, flags);
321         mock_store_pathinfo(mask, &mop);
322
323         conf = get_multipath_config();
324         r = store_pathinfo(pathvec, conf, (void *)&mop, mask, &pp);
325         put_multipath_config(conf);
326
327         if (flags & BL_MASK) {
328                 assert_int_equal(r, PATHINFO_SKIPPED);
329                 return NULL;
330         }
331         assert_int_equal(r, PATHINFO_OK);
332         assert_non_null(pp);
333         return pp;
334 }
335
336
337 struct multipath *__mock_multipath(struct vectors *vecs, struct path *pp)
338 {
339         struct multipath *mp;
340         struct config *conf;
341         struct mocked_path mop;
342
343         mocked_path_from_path(&mop, pp);
344         /* pathinfo() call in adopt_paths */
345         mock_pathinfo(DI_CHECKER|DI_PRIO, &mop);
346
347         mp = add_map_with_path(vecs, pp, 1);
348         assert_ptr_not_equal(mp, NULL);
349
350         /* TBD: mock setup_map() ... */
351         conf = get_multipath_config();
352         select_pgpolicy(conf, mp);
353         select_no_path_retry(conf, mp);
354         select_retain_hwhandler(conf, mp);
355         select_minio(conf, mp);
356         put_multipath_config(conf);
357
358         return mp;
359 }