Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_regress_ecpg --- regression test driver for ecpg
4 : *
5 : * This is a C implementation of the previous shell script for running
6 : * the regression tests, and should be mostly compatible with it.
7 : * Initial author of C translation: Magnus Hagander
8 : *
9 : * This code is released under the terms of the PostgreSQL License.
10 : *
11 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * src/interfaces/ecpg/test/pg_regress_ecpg.c
15 : *
16 : *-------------------------------------------------------------------------
17 : */
18 :
19 : #include "postgres_fe.h"
20 :
21 : #include "common/string.h"
22 : #include "lib/stringinfo.h"
23 : #include "pg_regress.h"
24 :
25 :
26 : /*
27 : * Create a filtered copy of sourcefile, removing any path
28 : * appearing in #line directives; for example, replace
29 : * #line x "./../bla/foo.h" with #line x "foo.h".
30 : * This is needed because the path part can vary depending
31 : * on compiler, platform, build options, etc.
32 : */
33 : static void
34 0 : ecpg_filter_source(const char *sourcefile, const char *outfile)
35 : {
36 0 : FILE *s,
37 : *t;
38 0 : StringInfoData linebuf;
39 :
40 0 : s = fopen(sourcefile, "r");
41 0 : if (!s)
42 : {
43 0 : fprintf(stderr, "Could not open file %s for reading\n", sourcefile);
44 0 : exit(2);
45 : }
46 0 : t = fopen(outfile, "w");
47 0 : if (!t)
48 : {
49 0 : fprintf(stderr, "Could not open file %s for writing\n", outfile);
50 0 : exit(2);
51 : }
52 :
53 0 : initStringInfo(&linebuf);
54 :
55 0 : while (pg_get_line_buf(s, &linebuf))
56 : {
57 : /* check for "#line " in the beginning */
58 0 : if (strstr(linebuf.data, "#line ") == linebuf.data)
59 : {
60 0 : char *p = strchr(linebuf.data, '"');
61 0 : int plen = 1;
62 :
63 0 : while (*p && (*(p + plen) == '.' || strchr(p + plen, '/') != NULL))
64 : {
65 0 : plen++;
66 : }
67 : /* plen is one more than the number of . and / characters */
68 0 : if (plen > 1)
69 : {
70 0 : memmove(p + 1, p + plen, strlen(p + plen) + 1);
71 : /* we don't bother to fix up linebuf.len */
72 0 : }
73 0 : }
74 0 : fputs(linebuf.data, t);
75 : }
76 :
77 0 : pfree(linebuf.data);
78 0 : fclose(s);
79 0 : fclose(t);
80 0 : }
81 :
82 : /*
83 : * Remove the details of connection failure error messages
84 : * in a test result file, since the target host/pathname and/or port
85 : * can vary. Rewrite the result file in-place.
86 : *
87 : * At some point it might be interesting to unify this with
88 : * ecpg_filter_source, but building a general pattern matcher
89 : * is no fun, nor does it seem desirable to introduce a
90 : * dependency on an external one.
91 : */
92 : static void
93 0 : ecpg_filter_stderr(const char *resultfile, const char *tmpfile)
94 : {
95 0 : FILE *s,
96 : *t;
97 0 : StringInfoData linebuf;
98 :
99 0 : s = fopen(resultfile, "r");
100 0 : if (!s)
101 : {
102 0 : fprintf(stderr, "Could not open file %s for reading\n", resultfile);
103 0 : exit(2);
104 : }
105 0 : t = fopen(tmpfile, "w");
106 0 : if (!t)
107 : {
108 0 : fprintf(stderr, "Could not open file %s for writing\n", tmpfile);
109 0 : exit(2);
110 : }
111 :
112 0 : initStringInfo(&linebuf);
113 :
114 0 : while (pg_get_line_buf(s, &linebuf))
115 : {
116 0 : char *p1 = strstr(linebuf.data, "connection to server ");
117 :
118 0 : if (p1)
119 : {
120 0 : char *p2 = strstr(p1, "failed: ");
121 :
122 0 : if (p2)
123 : {
124 0 : memmove(p1 + 21, p2, strlen(p2) + 1);
125 : /* we don't bother to fix up linebuf.len */
126 0 : }
127 0 : }
128 0 : fputs(linebuf.data, t);
129 0 : }
130 :
131 0 : pfree(linebuf.data);
132 0 : fclose(s);
133 0 : fclose(t);
134 0 : if (rename(tmpfile, resultfile) != 0)
135 : {
136 0 : fprintf(stderr, "Could not overwrite file %s with %s\n",
137 0 : resultfile, tmpfile);
138 0 : exit(2);
139 : }
140 0 : }
141 :
142 : /*
143 : * start an ecpg test process for specified file (including redirection),
144 : * and return process ID
145 : */
146 :
147 : static PID_TYPE
148 0 : ecpg_start_test(const char *testname,
149 : _stringlist **resultfiles,
150 : _stringlist **expectfiles,
151 : _stringlist **tags)
152 : {
153 0 : PID_TYPE pid;
154 0 : char inprg[MAXPGPATH];
155 0 : char insource[MAXPGPATH];
156 0 : StringInfoData testname_dash;
157 0 : char outfile_stdout[MAXPGPATH],
158 : expectfile_stdout[MAXPGPATH];
159 0 : char outfile_stderr[MAXPGPATH],
160 : expectfile_stderr[MAXPGPATH];
161 0 : char outfile_source[MAXPGPATH],
162 : expectfile_source[MAXPGPATH];
163 0 : char cmd[MAXPGPATH * 3];
164 0 : char *appnameenv;
165 :
166 0 : snprintf(inprg, sizeof(inprg), "%s/%s", inputdir, testname);
167 0 : snprintf(insource, sizeof(insource), "%s/%s.c", inputdir, testname);
168 :
169 : /* make a version of the test name that has dashes in place of slashes */
170 0 : initStringInfo(&testname_dash);
171 0 : appendStringInfoString(&testname_dash, testname);
172 0 : for (char *c = testname_dash.data; *c != '\0'; c++)
173 : {
174 0 : if (*c == '/')
175 0 : *c = '-';
176 0 : }
177 :
178 0 : snprintf(expectfile_stdout, sizeof(expectfile_stdout),
179 : "%s/expected/%s.stdout",
180 0 : expecteddir, testname_dash.data);
181 0 : snprintf(expectfile_stderr, sizeof(expectfile_stderr),
182 : "%s/expected/%s.stderr",
183 0 : expecteddir, testname_dash.data);
184 0 : snprintf(expectfile_source, sizeof(expectfile_source),
185 : "%s/expected/%s.c",
186 0 : expecteddir, testname_dash.data);
187 :
188 0 : snprintf(outfile_stdout, sizeof(outfile_stdout),
189 : "%s/results/%s.stdout",
190 0 : outputdir, testname_dash.data);
191 0 : snprintf(outfile_stderr, sizeof(outfile_stderr),
192 : "%s/results/%s.stderr",
193 0 : outputdir, testname_dash.data);
194 0 : snprintf(outfile_source, sizeof(outfile_source),
195 : "%s/results/%s.c",
196 0 : outputdir, testname_dash.data);
197 :
198 0 : add_stringlist_item(resultfiles, outfile_stdout);
199 0 : add_stringlist_item(expectfiles, expectfile_stdout);
200 0 : add_stringlist_item(tags, "stdout");
201 :
202 0 : add_stringlist_item(resultfiles, outfile_stderr);
203 0 : add_stringlist_item(expectfiles, expectfile_stderr);
204 0 : add_stringlist_item(tags, "stderr");
205 :
206 0 : add_stringlist_item(resultfiles, outfile_source);
207 0 : add_stringlist_item(expectfiles, expectfile_source);
208 0 : add_stringlist_item(tags, "source");
209 :
210 0 : ecpg_filter_source(insource, outfile_source);
211 :
212 0 : snprintf(cmd, sizeof(cmd),
213 : "\"%s\" >\"%s\" 2>\"%s\"",
214 0 : inprg,
215 0 : outfile_stdout,
216 0 : outfile_stderr);
217 :
218 0 : appnameenv = psprintf("ecpg/%s", testname_dash.data);
219 0 : setenv("PGAPPNAME", appnameenv, 1);
220 0 : free(appnameenv);
221 :
222 0 : pid = spawn_process(cmd);
223 :
224 0 : if (pid == INVALID_PID)
225 : {
226 0 : fprintf(stderr, _("could not start process for test %s\n"),
227 0 : testname);
228 0 : exit(2);
229 : }
230 :
231 0 : unsetenv("PGAPPNAME");
232 :
233 0 : free(testname_dash.data);
234 :
235 0 : return pid;
236 0 : }
237 :
238 : static void
239 0 : ecpg_postprocess_result(const char *filename)
240 : {
241 0 : int nlen = strlen(filename);
242 :
243 : /* Only stderr files require filtering, at the moment */
244 0 : if (nlen > 7 && strcmp(filename + nlen - 7, ".stderr") == 0)
245 : {
246 0 : char *tmpfile = psprintf("%s.tmp", filename);
247 :
248 0 : ecpg_filter_stderr(filename, tmpfile);
249 0 : pfree(tmpfile);
250 0 : }
251 0 : }
252 :
253 : static void
254 0 : ecpg_init(int argc, char *argv[])
255 : {
256 : /* nothing to do here at the moment */
257 0 : }
258 :
259 : int
260 0 : main(int argc, char *argv[])
261 : {
262 0 : return regression_main(argc, argv,
263 : ecpg_init,
264 : ecpg_start_test,
265 : ecpg_postprocess_result);
266 : }
|