3 #include "wvfileutils.h"
4 #include "uniconfroot.h"
11 #include "uniconfgen-sanitytest.h"
14 static inline int mkdir(const char *x
, int y
) { return mkdir(x
); }
17 // Returns the filename where the content was written. This file must be
18 // deleted when no longer needed. Returns WvString::null if unable to create
20 WvString
inigen(WvStringParm content
)
22 WvString ininame
= wvtmpfilename("inigen_test.ini");
24 WVPASS(file
.open(ininame
, O_CREAT
|O_RDWR
));
31 static int childcount(UniConf cfg
)
35 for (i
.rewind(); i
.next(); )
41 WVTEST_MAIN("UniIniGen Sanity Test")
43 WvString
inifile("/tmp/inigen-test-%s.ini", getpid());
44 UniIniGen
*gen
= new UniIniGen(inifile
);
45 UniConfGenSanityTester::sanity_test(gen
, WvString("ini:%s", inifile
));
50 WVTEST_MAIN("commit-without-refresh")
53 UniConfRoot
cfg("ini:/dev/does-not-exist");
57 WVFAIL(cfg
.haschildren());
61 WVTEST_MAIN("ini file permissions")
63 system("rm -f perm.ini");
64 system("touch perm.ini");
65 UniConfRoot
cfg("ini:perm.ini");
66 cfg
["foo"].setme("bar");
67 mode_t oldmask
= umask(02);
72 WVPASS(stat("perm.ini", &statbuf
) == 0);
73 WVPASSEQ(statbuf
.st_mode
, 0100664); //file and permissions 0664
75 system("rm -f perm.ini");
79 WVTEST_MAIN("parsing1")
81 WvString ininame
= inigen("[S1]\n"
87 UniConfRoot
cfg(WvString("ini:%s", ininame
));
89 WVPASSEQ(cfg
["S1/a"].getme(), "b");
90 WVPASSEQ(cfg
["S2/c"].getme(), "d");
91 WVPASSEQ(cfg
["S\n3/e"].getme(), "f");
92 WVPASSEQ(childcount(cfg
), 3);
98 WVTEST_MAIN("parsing2")
100 WvString ininame
= inigen("[x]\n"
103 " { a\n b} {c } = { a\n b2} {c }\n"
104 "apenwarr = {OBFU}scation!");
105 UniConfRoot
cfg(WvString("ini:%s", ininame
));
107 WVPASSEQ(cfg
["a"].getme(), "b c");
108 WVPASSEQ(cfg
[" a\n b} {c "].getme(), " a\n b2} {c ");
109 WVPASSEQ(cfg
["apenwarr"].getme(), "{OBFU}scation!");
115 WVTEST_MAIN("parsing3")
117 WvString ininame
= inigen("/ = foo\n");
118 UniConfRoot
cfg(WvString("ini:%s", ininame
));
119 WVPASSEQ(cfg
.getme(), "foo");
120 WVFAIL(cfg
.haschildren());
126 WVTEST_MAIN("parsing4")
128 // first line should be dropped to try to recover, since the entire
129 // file is invalid tcl-encoding.
130 WvString ininame
= inigen("{ broken\n/ = boo\n");
131 UniConfRoot
cfg(WvString("ini:%s", ininame
));
132 WVPASSEQ(cfg
.getme(), "boo");
133 WVFAIL(cfg
.haschildren());
137 WVTEST_MAIN("Setting and getting (bug 6090)")
139 WvString ininame
= inigen("");
140 UniConfRoot
cfg(WvString("ini:%s", ininame
));
142 cfg
["mrwise"].setme("{{bork!");
144 WVPASSEQ(cfg
["mrwise"].getme(), "{{bork!");
146 UniConfRoot
cfg2(WvString("ini:%s", ininame
));
147 WVPASSEQ(cfg2
["mrwise"].getme(), "{{bork!");
152 // This is probably covered by sanity tests now. OTOH, more tests is more
154 WVTEST_MAIN("Trailing slashes")
156 UniConfRoot
cfg("ini:tmp.ini");
158 cfg
["sfllaw"].setme("law");
159 WVPASSEQ(cfg
["sfllaw"].getme(), "law");
160 WVPASSEQ(cfg
["sfllaw/"].getme(), "");
162 cfg
["sfllaw/"].setme("LAW");
163 WVPASSEQ(cfg
["sfllaw"].getme(), "law");
164 WVPASSEQ(cfg
["sfllaw/"].getme(), "");
167 static void count_cb(int *i
, const UniConf
&cfg
, const UniConfKey
&key
)
173 WVTEST_MAIN("ini callbacks")
176 WvString ininame
= inigen("a/b/c/1 = 11\n"
178 UniConfRoot
cfg(WvString("ini:%s", ininame
));
179 UniWatch
w(cfg
, wv::bind(&count_cb
, &i
, _1
, _2
), true);
186 WvFile
f(ininame
, O_WRONLY
|O_TRUNC
);
187 f
.print("a/b/c/1 = 111\n"
192 f
.print("a/b/c/3 = 333\n");
212 static void inicmp(WvStringParm key
, WvStringParm val
, WvStringParm content
)
214 WvString ininame
= inigen("");
215 UniConfRoot
cfg(WvString("ini:%s", ininame
));
219 WvFile
f(ininame
, O_RDONLY
);
221 f
.read(buf
, 128*1024);
222 WVPASSEQ(content
, buf
.getstr());
227 WVTEST_MAIN("writing")
235 inicmp("x/y/z", "abc",
236 "\n[x]\ny/z = abc\n");
238 inicmp("x\n/y/z", "abc",
239 "\n[{x\n}]\ny/z = abc\n");
241 inicmp("/users/apenwarr", "{OBFU}scation!",
242 "\n[users]\napenwarr = {OBFU}scation!\n");
244 inicmp("/users/ apenwarr", "pbc ",
245 "\n[users]\n{ apenwarr} = {pbc }\n");
247 // FIXME: empty-string keys probably *should* be printed, but we don't,
248 // for compatibility with WvConf. This test makes sure it stays that
249 // way (until we think of something better?)
250 inicmp("/foo/blah", "",
255 static ino_t
inode_of(const char *fname
)
258 if (stat(fname
, &st
) != 0)
265 static off_t
size_of(const char *fname
)
268 if (stat(fname
, &st
) != 0)
275 WVTEST_MAIN("atomic updates")
277 WvString
dirname("atomic-update-dir.tmp"), fname("%s/test.ini", dirname
);
278 chmod(dirname
, 0700);
280 WVPASS(!mkdir(dirname
, 0700)); // honours umask
281 WVPASS(!chmod(dirname
, 0700)); // doesn't include umask
283 // the directory is writable, so we can safely do atomic file replacement.
284 // That means the file inode number will be *different* after doing
285 // a second commit().
286 UniConfRoot
ini(WvString("ini:%s", fname
));
287 ini
.xset("useless key", "useless value");
289 ino_t inode1
= inode_of(fname
);
290 off_t size1
= size_of(fname
);
295 ino_t inode2
= inode_of(fname
);
296 off_t size2
= size_of(fname
);
298 WVFAILEQ(inode1
, inode2
);
299 WVPASS(size2
> size1
);
301 // now let's make the directory non-writable. The inifile inside the
302 // directory is still writable, which means we can update it, but not
303 // atomically. Therefore its inode number *won't* change.
304 WVPASS(!chmod(dirname
, 0500));
307 ino_t inode3
= inode_of(fname
);
308 off_t size3
= size_of(fname
);
310 WVPASSEQ(inode2
, inode3
);
311 WVPASS(size3
> size2
);
314 chmod(dirname
, 0700);
319 WVTEST_MAIN("do not refresh if not requested")
321 WvString ininame
= inigen("[foo]\n"
323 UniConfRoot
cfg(WvString("ini:%s", ininame
), false);
325 WVFAIL(cfg
["foo/bar"].exists());
326 WVFAILEQ(cfg
["foo/bar"].getme(), "baz");
327 WVPASSEQ(childcount(cfg
), 0);