1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2000,2008 Oracle.  All rights reserved.
5 *
6 * $Id: TestConstruct01.cpp,v 12.7 2008/01/08 20:58:54 bostic Exp $
7 */
8
9/*
10 * Do some regression tests for constructors.
11 * Run normally (without arguments) it is a simple regression test.
12 * Run with a numeric argument, it repeats the regression a number
13 * of times, to try to determine if there are memory leaks.
14 */
15#include <sys/types.h>
16
17#include <iostream.h>
18#include <errno.h>
19#include <stdlib.h>
20#include <string.h>
21#ifndef _MSC_VER
22#include <unistd.h>
23#endif
24
25#include <iomanip.h>
26#include <db_cxx.h>
27
28#define	ERR(a)  \
29    do { \
30      cout << "FAIL: " << (a) << "\n"; sysexit(1); \
31    } while (0)
32
33#define	ERR2(a1,a2)  \
34    do { \
35      cout << "FAIL: " << (a1) << ": " << (a2) << "\n"; sysexit(1); \
36    } while (0)
37
38#define	ERR3(a1,a2,a3)  \
39    do { \
40      cout << "FAIL: " << (a1) << ": " << (a2) << ": " << (a3) << "\n"; sysexit(1); \
41    } while (0)
42
43#define	CHK(a)   \
44    do { \
45      int _ret; \
46      if ((_ret = (a)) != 0) { \
47	 ERR3("DB function " #a " has bad return", _ret, DbEnv::strerror(_ret)); \
48      } \
49    } while (0)
50
51#ifdef VERBOSE
52#define	DEBUGOUT(a)          cout << a << "\n"
53#else
54#define	DEBUGOUT(a)
55#endif
56
57#define	CONSTRUCT01_DBNAME         "construct01.db"
58#define	CONSTRUCT01_DBDIR          "."
59#define	CONSTRUCT01_DBFULLPATH     (CONSTRUCT01_DBDIR "/" CONSTRUCT01_DBNAME)
60
61int itemcount;			// count the number of items in the database
62
63// A good place to put a breakpoint...
64//
65void sysexit(int status)
66{
67	exit(status);
68}
69
70void check_file_removed(const char *name, int fatal)
71{
72	unlink(name);
73#if 0
74	if (access(name, 0) == 0) {
75		if (fatal)
76			cout << "FAIL: ";
77		cout << "File \"" << name << "\" still exists after run\n";
78		if (fatal)
79			sysexit(1);
80	}
81#endif
82}
83
84// Check that key/data for 0 - count-1 are already present,
85// and write a key/data for count.  The key and data are
86// both "0123...N" where N == count-1.
87//
88// For some reason on Windows, we need to open using the full pathname
89// of the file when there is no environment, thus the 'has_env'
90// variable.
91//
92void rundb(Db *db, int count, int has_env)
93{
94	const char *name;
95
96	if (has_env)
97		name = CONSTRUCT01_DBNAME;
98	else
99		name = CONSTRUCT01_DBFULLPATH;
100
101	db->set_error_stream(&cerr);
102
103	// We don't really care about the pagesize, but we do want
104	// to make sure adjusting Db specific variables works before
105	// opening the db.
106	//
107	CHK(db->set_pagesize(1024));
108	CHK(db->open(NULL, name, NULL, DB_BTREE, count ? 0 : DB_CREATE, 0664));
109
110	// The bit map of keys we've seen
111	long bitmap = 0;
112
113	// The bit map of keys we expect to see
114	long expected = (1 << (count+1)) - 1;
115
116	char outbuf[10];
117	int i;
118	for (i=0; i<count; i++) {
119		outbuf[i] = '0' + i;
120	}
121	outbuf[i++] = '\0';
122	Dbt key(outbuf, i);
123	Dbt data(outbuf, i);
124
125	DEBUGOUT("Put: " << outbuf);
126	CHK(db->put(0, &key, &data, DB_NOOVERWRITE));
127
128	// Acquire a cursor for the table.
129	Dbc *dbcp;
130	CHK(db->cursor(NULL, &dbcp, 0));
131
132	// Walk through the table, checking
133	Dbt readkey;
134	Dbt readdata;
135	while (dbcp->get(&readkey, &readdata, DB_NEXT) == 0) {
136		char *key_string = (char *)readkey.get_data();
137		char *data_string = (char *)readdata.get_data();
138		DEBUGOUT("Got: " << key_string << ": " << data_string);
139		int len = strlen(key_string);
140		long bit = (1 << len);
141		if (len > count) {
142			ERR("reread length is bad");
143		}
144		else if (strcmp(data_string, key_string) != 0) {
145			ERR("key/data don't match");
146		}
147		else if ((bitmap & bit) != 0) {
148			ERR("key already seen");
149		}
150		else if ((expected & bit) == 0) {
151			ERR("key was not expected");
152		}
153		else {
154			bitmap |= bit;
155			expected &= ~(bit);
156			for (i=0; i<len; i++) {
157				if (key_string[i] != ('0' + i)) {
158					cout << " got " << key_string
159					     << " (" << (int)key_string[i] << ")"
160					     <<	", wanted " << i
161					     << " (" << (int)('0' + i) << ")"
162					     << " at position " << i << "\n";
163					ERR("key is corrupt");
164				}
165			}
166		}
167	}
168	if (expected != 0) {
169		cout << " expected more keys, bitmap is: " << expected << "\n";
170		ERR("missing keys in database");
171	}
172	CHK(dbcp->close());
173	CHK(db->close(0));
174}
175
176void t1(int except_flag)
177{
178	cout << "  Running test 1:\n";
179	Db db(0, except_flag);
180	rundb(&db, itemcount++, 0);
181	cout << "  finished.\n";
182}
183
184void t2(int except_flag)
185{
186	cout << "  Running test 2:\n";
187	Db db(0, except_flag);
188	rundb(&db, itemcount++, 0);
189	cout << "  finished.\n";
190}
191
192void t3(int except_flag)
193{
194	cout << "  Running test 3:\n";
195	Db db(0, except_flag);
196	rundb(&db, itemcount++, 0);
197	cout << "  finished.\n";
198}
199
200void t4(int except_flag)
201{
202	cout << "  Running test 4:\n";
203	DbEnv env(except_flag);
204	CHK(env.open(CONSTRUCT01_DBDIR, DB_CREATE | DB_INIT_MPOOL, 0));
205	Db db(&env, 0);
206	CHK(db.close(0));
207	CHK(env.close(0));
208	cout << "  finished.\n";
209}
210
211void t5(int except_flag)
212{
213	cout << "  Running test 5:\n";
214	DbEnv env(except_flag);
215	CHK(env.open(CONSTRUCT01_DBDIR, DB_CREATE | DB_INIT_MPOOL, 0));
216	Db db(&env, 0);
217	rundb(&db, itemcount++, 1);
218	// Note we cannot reuse the old Db!
219	Db anotherdb(&env, 0);
220
221	anotherdb.set_errpfx("test5");
222	rundb(&anotherdb, itemcount++, 1);
223	CHK(env.close(0));
224	cout << "  finished.\n";
225}
226
227void t6(int except_flag)
228{
229	cout << "  Running test 6:\n";
230
231	/* From user [#2939] */
232	int err;
233
234	DbEnv* penv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
235	penv->set_cachesize(0, 32 * 1024, 0);
236	penv->open(CONSTRUCT01_DBDIR, DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL, 0);
237
238	//LEAK: remove this block and leak disappears
239	Db* pdb = new Db(penv,0);
240	if ((err = pdb->close(0)) != 0) {
241		fprintf(stderr, "Error closing Db: %s\n", db_strerror(err));
242	}
243	delete pdb;
244	//LEAK: remove this block and leak disappears
245
246	if ((err = penv->close(0)) != 0) {
247		fprintf(stderr, "Error closing DbEnv: %s\n", db_strerror(err));
248	}
249	delete penv;
250
251	cout << "  finished.\n";
252}
253
254// remove any existing environment or database
255void removeall()
256{
257    {
258	DbEnv tmpenv(DB_CXX_NO_EXCEPTIONS);
259	(void)tmpenv.remove(CONSTRUCT01_DBDIR, DB_FORCE);
260    }
261
262	check_file_removed(CONSTRUCT01_DBFULLPATH, 1);
263	for (int i=0; i<8; i++) {
264		char buf[20];
265		sprintf(buf, "__db.00%d", i);
266		check_file_removed(buf, 1);
267	}
268}
269
270int doall(int except_flag)
271{
272	itemcount = 0;
273	try {
274		// before and after the run, removing any
275		// old environment/database.
276		//
277		removeall();
278		t1(except_flag);
279		t2(except_flag);
280		t3(except_flag);
281		t4(except_flag);
282		t5(except_flag);
283		t6(except_flag);
284
285		removeall();
286		return 0;
287	}
288	catch (DbException &dbe) {
289		ERR2("EXCEPTION RECEIVED", dbe.what());
290	}
291	return 1;
292}
293
294int main(int argc, char *argv[])
295{
296	int iterations = 1;
297	if (argc > 1) {
298		iterations = atoi(argv[1]);
299		if (iterations < 0) {
300			ERR("Usage:  construct01 count");
301		}
302	}
303	for (int i=0; i<iterations; i++) {
304		if (iterations != 0) {
305			cout << "(" << i << "/" << iterations << ") ";
306		}
307		cout << "construct01 running:\n";
308		if (doall(DB_CXX_NO_EXCEPTIONS) != 0) {
309			ERR("SOME TEST FAILED FOR NO-EXCEPTION TEST");
310		}
311		else if (doall(0) != 0) {
312			ERR("SOME TEST FAILED FOR EXCEPTION TEST");
313		}
314		else {
315			cout << "\nALL TESTS SUCCESSFUL\n";
316		}
317	}
318	return 0;
319}
320