1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * isolation_main --- pg_regress test launcher for isolation tests |
4 | * |
5 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
6 | * Portions Copyright (c) 1994, Regents of the University of California |
7 | * |
8 | * src/test/isolation/isolation_main.c |
9 | * |
10 | *------------------------------------------------------------------------- |
11 | */ |
12 | |
13 | #include "postgres_fe.h" |
14 | |
15 | #include "pg_regress.h" |
16 | |
17 | char saved_argv0[MAXPGPATH]; |
18 | char isolation_exec[MAXPGPATH]; |
19 | bool looked_up_isolation_exec = false; |
20 | |
21 | #define PG_ISOLATION_VERSIONSTR "isolationtester (PostgreSQL) " PG_VERSION "\n" |
22 | |
23 | /* |
24 | * start an isolation tester process for specified file (including |
25 | * redirection), and return process ID |
26 | */ |
27 | static PID_TYPE |
28 | isolation_start_test(const char *testname, |
29 | _stringlist **resultfiles, |
30 | _stringlist **expectfiles, |
31 | _stringlist **tags) |
32 | { |
33 | PID_TYPE pid; |
34 | char infile[MAXPGPATH]; |
35 | char outfile[MAXPGPATH]; |
36 | char expectfile[MAXPGPATH]; |
37 | char psql_cmd[MAXPGPATH * 3]; |
38 | size_t offset = 0; |
39 | |
40 | /* need to do the path lookup here, check isolation_init() for details */ |
41 | if (!looked_up_isolation_exec) |
42 | { |
43 | /* look for isolationtester binary */ |
44 | if (find_other_exec(saved_argv0, "isolationtester" , |
45 | PG_ISOLATION_VERSIONSTR, isolation_exec) != 0) |
46 | { |
47 | fprintf(stderr, _("could not find proper isolationtester binary\n" )); |
48 | exit(2); |
49 | } |
50 | looked_up_isolation_exec = true; |
51 | } |
52 | |
53 | /* |
54 | * Look for files in the output dir first, consistent with a vpath search. |
55 | * This is mainly to create more reasonable error messages if the file is |
56 | * not found. It also allows local test overrides when running pg_regress |
57 | * outside of the source tree. |
58 | */ |
59 | snprintf(infile, sizeof(infile), "%s/specs/%s.spec" , |
60 | outputdir, testname); |
61 | if (!file_exists(infile)) |
62 | snprintf(infile, sizeof(infile), "%s/specs/%s.spec" , |
63 | inputdir, testname); |
64 | |
65 | snprintf(outfile, sizeof(outfile), "%s/results/%s.out" , |
66 | outputdir, testname); |
67 | |
68 | snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out" , |
69 | outputdir, testname); |
70 | if (!file_exists(expectfile)) |
71 | snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out" , |
72 | inputdir, testname); |
73 | |
74 | add_stringlist_item(resultfiles, outfile); |
75 | add_stringlist_item(expectfiles, expectfile); |
76 | |
77 | if (launcher) |
78 | { |
79 | offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset, |
80 | "%s " , launcher); |
81 | if (offset >= sizeof(psql_cmd)) |
82 | { |
83 | fprintf(stderr, _("command too long\n" )); |
84 | exit(2); |
85 | } |
86 | } |
87 | |
88 | offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset, |
89 | "\"%s\" \"dbname=%s\" < \"%s\" > \"%s\" 2>&1" , |
90 | isolation_exec, |
91 | dblist->str, |
92 | infile, |
93 | outfile); |
94 | if (offset >= sizeof(psql_cmd)) |
95 | { |
96 | fprintf(stderr, _("command too long\n" )); |
97 | exit(2); |
98 | } |
99 | |
100 | pid = spawn_process(psql_cmd); |
101 | |
102 | if (pid == INVALID_PID) |
103 | { |
104 | fprintf(stderr, _("could not start process for test %s\n" ), |
105 | testname); |
106 | exit(2); |
107 | } |
108 | |
109 | return pid; |
110 | } |
111 | |
112 | static void |
113 | isolation_init(int argc, char **argv) |
114 | { |
115 | size_t argv0_len; |
116 | |
117 | /* |
118 | * We unfortunately cannot do the find_other_exec() lookup to find the |
119 | * "isolationtester" binary here. regression_main() calls the |
120 | * initialization functions before parsing the commandline arguments and |
121 | * thus hasn't changed the library search path at this point which in turn |
122 | * can cause the "isolationtester -V" invocation that find_other_exec() |
123 | * does to fail since it's linked to libpq. So we instead copy argv[0] |
124 | * and do the lookup the first time through isolation_start_test(). |
125 | */ |
126 | argv0_len = strlcpy(saved_argv0, argv[0], MAXPGPATH); |
127 | if (argv0_len >= MAXPGPATH) |
128 | { |
129 | fprintf(stderr, _("path for isolationtester executable is longer than %d bytes\n" ), |
130 | (int) (MAXPGPATH - 1)); |
131 | exit(2); |
132 | } |
133 | |
134 | /* set default regression database name */ |
135 | add_stringlist_item(&dblist, "isolation_regression" ); |
136 | } |
137 | |
138 | int |
139 | main(int argc, char *argv[]) |
140 | { |
141 | return regression_main(argc, argv, isolation_init, isolation_start_test); |
142 | } |
143 | |