Mercurial > hg
annotate contrib/chg/util.c @ 39772:ae531f5e583c
testing: add interface unit tests for file storage
Our strategy for supporting alternate storage backends is to define
interfaces for everything then "code to the interface."
We already have interfaces for various primitives, including file
and manifest storage.
What we don't have is generic unit tests for those interfaces. Up
to this point we've been relying on high-level integration tests
(mainly in the form of existing .t tests) to test alternate storage
backends. And my experience with developing the "simple store" test
extension is that such testing is very tedious: it takes several
minutes to run all tests and when you find a failure, it is often
non-trivial to debug.
This commit starts to change that.
This commit introduces the mercurial.testing.storage module. It
contains testing code for storage. Currently, it defines some
unittest.TestCase classes for testing the file storage interfaces.
It also defines some factory functions that allow a caller to easily
spawn a custom TestCase "bound" to a specific file storage backend
implementation.
A new .py test has been added. It simply defines a callable to produce
filelog and transaction instances on demand and then "registers" the
various test classes so the filelog class can be tested with the
storage interface unit tests.
As part of writing the tests, I identified a couple of apparent
bugs in revlog.py and filelog.py! These are tracked with inline
TODO comments.
Writing the tests makes it more obvious where the storage interface
is lacking. For example, we raise either IndexError or
error.LookupError for missing revisions depending on whether we
use an integer revision or a node. Also, we raise error.RevlogError
in various places when we should be raising a storage-agnostic
error type.
The storage interfaces are currently far from perfect and there is much
work to be done to improve them. But at least with this commit we
finally have the start of unit tests that can be used to "qualify"
the behavior of a storage backend. And when implementing and debugging
new storage backends, we now have an obvious place to define new
tests and have obvious places to insert breakpoints to facilitate
debugging. This should be invaluable when implementing new storage
backends.
I added the mercurial.testing package because these interface
conformance tests are generic and need to be usable by all storage
backends. Having the code live in tests/ would make it difficult for
storage backends implemented in extensions to test their interface
conformance. First, it would require obtaining a copy of Mercurial's
storage test code in order to test. Second, it would make testing
against multiple Mercurial versions difficult, as you would need to
import N copies of the storage testing code in order to achieve test
coverage. By making the test code part of the Mercurial distribution
itself, extensions can `import mercurial.testing.*` to access and run
the test code. The test will run against whatever Mercurial version
is active.
FWIW I've always wanted to move parts of run-tests.py into the
mercurial.* package to make the testing story simpler (e.g. imagine an
`hg debugruntests` command that could invoke the test harness). While I
have no plans to do that in the near future, establishing the
mercurial.testing package does provide a natural home for that code
should someone do this in the future.
Differential Revision: https://phab.mercurial-scm.org/D4650
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 18 Sep 2018 16:52:11 -0700 |
parents | 9724f54923ec |
children | 763b45bc4483 |
rev | line source |
---|---|
28060 | 1 /* |
2 * Utility functions | |
3 * | |
4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org> | |
5 * | |
6 * This software may be used and distributed according to the terms of the | |
7 * GNU General Public License version 2 or any later version. | |
8 */ | |
9 | |
28788
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
10 #include <errno.h> |
28855
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
11 #include <fcntl.h> |
28060 | 12 #include <signal.h> |
13 #include <stdarg.h> | |
14 #include <stdio.h> | |
15 #include <stdlib.h> | |
28084
3fc45956c978
chg: initialize sigaction fields more reliably
Yuya Nishihara <yuya@tcha.org>
parents:
28060
diff
changeset
|
16 #include <string.h> |
34309
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
17 #include <sys/time.h> |
28060 | 18 #include <sys/types.h> |
19 #include <sys/wait.h> | |
20 #include <unistd.h> | |
21 | |
22 #include "util.h" | |
23 | |
28787
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
24 static int colorenabled = 0; |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
25 |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
26 static inline void fsetcolor(FILE *fp, const char *code) |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
27 { |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
28 if (!colorenabled) |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
29 return; |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
30 fprintf(fp, "\033[%sm", code); |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
31 } |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
32 |
28788
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
33 static void vabortmsgerrno(int no, const char *fmt, va_list args) |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
34 { |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
35 fsetcolor(stderr, "1;31"); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
36 fputs("chg: abort: ", stderr); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
37 vfprintf(stderr, fmt, args); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
38 if (no != 0) |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
39 fprintf(stderr, " (errno = %d, %s)", no, strerror(no)); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
40 fsetcolor(stderr, ""); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
41 fputc('\n', stderr); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
42 exit(255); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
43 } |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
44 |
28060 | 45 void abortmsg(const char *fmt, ...) |
46 { | |
47 va_list args; | |
48 va_start(args, fmt); | |
28788
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
49 vabortmsgerrno(0, fmt, args); |
28060 | 50 va_end(args); |
28788
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
51 } |
28060 | 52 |
28788
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
53 void abortmsgerrno(const char *fmt, ...) |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
54 { |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
55 int no = errno; |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
56 va_list args; |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
57 va_start(args, fmt); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
58 vabortmsgerrno(no, fmt, args); |
57a78a64de44
chg: add util function abortmsgerrno to print error with errno
Jun Wu <quark@fb.com>
parents:
28787
diff
changeset
|
59 va_end(args); |
28060 | 60 } |
61 | |
62 static int debugmsgenabled = 0; | |
34309
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
63 static double debugstart = 0; |
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
64 |
35959
9724f54923ec
chg: enable clang-format on all .c and .h files
Augie Fackler <augie@google.com>
parents:
34309
diff
changeset
|
65 static double now() |
9724f54923ec
chg: enable clang-format on all .c and .h files
Augie Fackler <augie@google.com>
parents:
34309
diff
changeset
|
66 { |
34309
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
67 struct timeval t; |
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
68 gettimeofday(&t, NULL); |
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
69 return t.tv_usec / 1e6 + t.tv_sec; |
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
70 } |
28060 | 71 |
28787
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
72 void enablecolor(void) |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
73 { |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
74 colorenabled = 1; |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
75 } |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
76 |
28060 | 77 void enabledebugmsg(void) |
78 { | |
79 debugmsgenabled = 1; | |
34309
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
80 debugstart = now(); |
28060 | 81 } |
82 | |
83 void debugmsg(const char *fmt, ...) | |
84 { | |
85 if (!debugmsgenabled) | |
86 return; | |
87 | |
88 va_list args; | |
89 va_start(args, fmt); | |
28787
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
90 fsetcolor(stderr, "1;30"); |
34309
b94db1780365
chg: show timestamp with debug messages
Jun Wu <quark@fb.com>
parents:
28855
diff
changeset
|
91 fprintf(stderr, "chg: debug: %4.6f ", now() - debugstart); |
28060 | 92 vfprintf(stderr, fmt, args); |
28787
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
93 fsetcolor(stderr, ""); |
ea86cdcd9b50
chg: use color in debug/error messages conditionally
Jun Wu <quark@fb.com>
parents:
28165
diff
changeset
|
94 fputc('\n', stderr); |
28060 | 95 va_end(args); |
96 } | |
97 | |
28854
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
98 void fchdirx(int dirfd) |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
99 { |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
100 int r = fchdir(dirfd); |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
101 if (r == -1) |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
102 abortmsgerrno("failed to fchdir"); |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
103 } |
ddef14468952
chg: add fchdirx as a utility function
Jun Wu <quark@fb.com>
parents:
28788
diff
changeset
|
104 |
28855
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
105 void fsetcloexec(int fd) |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
106 { |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
107 int flags = fcntl(fd, F_GETFD); |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
108 if (flags < 0) |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
109 abortmsgerrno("cannot get flags of fd %d", fd); |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
110 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
111 abortmsgerrno("cannot set flags of fd %d", fd); |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
112 } |
f5764e177bbe
chg: extract the logic of setting FD_CLOEXEC to a utility function
Jun Wu <quark@fb.com>
parents:
28854
diff
changeset
|
113 |
28165
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
114 void *mallocx(size_t size) |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
115 { |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
116 void *result = malloc(size); |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
117 if (!result) |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
118 abortmsg("failed to malloc"); |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
119 return result; |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
120 } |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
121 |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
122 void *reallocx(void *ptr, size_t size) |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
123 { |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
124 void *result = realloc(ptr, size); |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
125 if (!result) |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
126 abortmsg("failed to realloc"); |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
127 return result; |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
128 } |
c6705c6303dd
chg: add utility functions mallocx, reallocx
Jun Wu <quark@fb.com>
parents:
28084
diff
changeset
|
129 |
28060 | 130 /* |
131 * Execute a shell command in mostly the same manner as system(), with the | |
132 * give environment variables, after chdir to the given cwd. Returns a status | |
133 * code compatible with the Python subprocess module. | |
134 */ | |
135 int runshellcmd(const char *cmd, const char *envp[], const char *cwd) | |
136 { | |
137 enum { F_SIGINT = 1, F_SIGQUIT = 2, F_SIGMASK = 4, F_WAITPID = 8 }; | |
138 unsigned int doneflags = 0; | |
139 int status = 0; | |
140 struct sigaction newsa, oldsaint, oldsaquit; | |
141 sigset_t oldmask; | |
142 | |
143 /* block or mask signals just as system() does */ | |
28084
3fc45956c978
chg: initialize sigaction fields more reliably
Yuya Nishihara <yuya@tcha.org>
parents:
28060
diff
changeset
|
144 memset(&newsa, 0, sizeof(newsa)); |
28060 | 145 newsa.sa_handler = SIG_IGN; |
146 newsa.sa_flags = 0; | |
147 if (sigemptyset(&newsa.sa_mask) < 0) | |
148 goto done; | |
149 if (sigaction(SIGINT, &newsa, &oldsaint) < 0) | |
150 goto done; | |
151 doneflags |= F_SIGINT; | |
152 if (sigaction(SIGQUIT, &newsa, &oldsaquit) < 0) | |
153 goto done; | |
154 doneflags |= F_SIGQUIT; | |
155 | |
156 if (sigaddset(&newsa.sa_mask, SIGCHLD) < 0) | |
157 goto done; | |
158 if (sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldmask) < 0) | |
159 goto done; | |
160 doneflags |= F_SIGMASK; | |
161 | |
162 pid_t pid = fork(); | |
163 if (pid < 0) | |
164 goto done; | |
165 if (pid == 0) { | |
166 sigaction(SIGINT, &oldsaint, NULL); | |
167 sigaction(SIGQUIT, &oldsaquit, NULL); | |
168 sigprocmask(SIG_SETMASK, &oldmask, NULL); | |
169 if (cwd && chdir(cwd) < 0) | |
170 _exit(127); | |
171 const char *argv[] = {"sh", "-c", cmd, NULL}; | |
172 if (envp) { | |
173 execve("/bin/sh", (char **)argv, (char **)envp); | |
174 } else { | |
175 execv("/bin/sh", (char **)argv); | |
176 } | |
177 _exit(127); | |
178 } else { | |
179 if (waitpid(pid, &status, 0) < 0) | |
180 goto done; | |
181 doneflags |= F_WAITPID; | |
182 } | |
183 | |
184 done: | |
185 if (doneflags & F_SIGINT) | |
186 sigaction(SIGINT, &oldsaint, NULL); | |
187 if (doneflags & F_SIGQUIT) | |
188 sigaction(SIGQUIT, &oldsaquit, NULL); | |
189 if (doneflags & F_SIGMASK) | |
190 sigprocmask(SIG_SETMASK, &oldmask, NULL); | |
191 | |
192 /* no way to report other errors, use 127 (= shell termination) */ | |
193 if (!(doneflags & F_WAITPID)) | |
194 return 127; | |
195 if (WIFEXITED(status)) | |
196 return WEXITSTATUS(status); | |
197 if (WIFSIGNALED(status)) | |
198 return -WTERMSIG(status); | |
199 return 127; | |
200 } |