##// END OF EJS Templates
test: replace a many occurence of `python` with `$PYTHON`...
marmoute -
r46843:0826d684 default
parent child Browse files
Show More
@@ -1,49 +1,49 b''
1 1 scratchnodes() {
2 2 for node in `find ../repo/.hg/scratchbranches/index/nodemap/* | sort`; do
3 3 echo ${node##*/} `cat $node`
4 4 done
5 5 }
6 6
7 7 scratchbookmarks() {
8 8 for bookmark in `find ../repo/.hg/scratchbranches/index/bookmarkmap/* -type f | sort`; do
9 9 echo "${bookmark##*/bookmarkmap/} `cat $bookmark`"
10 10 done
11 11 }
12 12
13 13 setupcommon() {
14 14 cat >> $HGRCPATH << EOF
15 15 [extensions]
16 16 infinitepush=
17 17 [ui]
18 ssh = python "$TESTDIR/dummyssh"
18 ssh = $PYTHON "$TESTDIR/dummyssh"
19 19 [infinitepush]
20 20 branchpattern=re:scratch/.*
21 21 EOF
22 22 }
23 23
24 24 setupserver() {
25 25 cat >> .hg/hgrc << EOF
26 26 [infinitepush]
27 27 server=yes
28 28 indextype=disk
29 29 storetype=disk
30 30 reponame=babar
31 31 EOF
32 32 }
33 33
34 34 waitbgbackup() {
35 35 sleep 1
36 36 hg debugwaitbackup
37 37 }
38 38
39 39 mkcommitautobackup() {
40 40 echo $1 > $1
41 41 hg add $1
42 42 hg ci -m $1 --config infinitepushbackup.autobackup=True
43 43 }
44 44
45 45 setuplogdir() {
46 46 mkdir $TESTTMP/logs
47 47 chmod 0755 $TESTTMP/logs
48 48 chmod +t $TESTTMP/logs
49 49 }
@@ -1,75 +1,75 b''
1 1 CACHEDIR=$PWD/hgcache
2 2 cat >> $HGRCPATH <<EOF
3 3 [remotefilelog]
4 4 cachepath=$CACHEDIR
5 5 debug=True
6 6 [extensions]
7 7 remotefilelog=
8 8 rebase=
9 9 strip=
10 10 [ui]
11 ssh=python "$TESTDIR/dummyssh"
11 ssh=$PYTHON "$TESTDIR/dummyssh"
12 12 [server]
13 13 preferuncompressed=True
14 14 [experimental]
15 15 changegroup3=True
16 16 [rebase]
17 17 singletransaction=True
18 18 EOF
19 19
20 20 hgcloneshallow() {
21 21 local name
22 22 local dest
23 23 orig=$1
24 24 shift
25 25 dest=$1
26 26 shift
27 27 hg clone --shallow --config remotefilelog.reponame=master $orig $dest $@
28 28 cat >> $dest/.hg/hgrc <<EOF
29 29 [remotefilelog]
30 30 reponame=master
31 31 [phases]
32 32 publish=False
33 33 EOF
34 34 }
35 35
36 36 hgcloneshallowlfs() {
37 37 local name
38 38 local dest
39 39 local lfsdir
40 40 orig=$1
41 41 shift
42 42 dest=$1
43 43 shift
44 44 lfsdir=$1
45 45 shift
46 46 hg clone --shallow --config "extensions.lfs=" --config "lfs.url=$lfsdir" --config remotefilelog.reponame=master $orig $dest $@
47 47 cat >> $dest/.hg/hgrc <<EOF
48 48 [extensions]
49 49 lfs=
50 50 [lfs]
51 51 url=$lfsdir
52 52 [remotefilelog]
53 53 reponame=master
54 54 [phases]
55 55 publish=False
56 56 EOF
57 57 }
58 58
59 59 clearcache() {
60 60 rm -rf $CACHEDIR/*
61 61 }
62 62
63 63 mkcommit() {
64 64 echo "$1" > "$1"
65 65 hg add "$1"
66 66 hg ci -m "$1"
67 67 }
68 68
69 69 ls_l() {
70 70 $PYTHON $TESTDIR/ls-l.py "$@"
71 71 }
72 72
73 73 identifyrflcaps() {
74 74 xargs -n 1 echo | egrep '(remotefilelog|getflogheads|getfile)' | sort
75 75 }
@@ -1,238 +1,238 b''
1 1 from __future__ import absolute_import, print_function
2 2
3 3 from mercurial import demandimport
4 4
5 5 demandimport.enable()
6 6
7 7 import os
8 8 import subprocess
9 9 import sys
10 10 import types
11 11
12 12 # Don't import pycompat because it has too many side-effects.
13 13 ispy3 = sys.version_info[0] >= 3
14 14
15 15 # Only run if demandimport is allowed
16 16 if subprocess.call(
17 ['python', '%s/hghave' % os.environ['TESTDIR'], 'demandimport']
17 [os.environ['PYTHON'], '%s/hghave' % os.environ['TESTDIR'], 'demandimport']
18 18 ):
19 19 sys.exit(80)
20 20
21 21 # We rely on assert, which gets optimized out.
22 22 if sys.flags.optimize:
23 23 sys.exit(80)
24 24
25 25 # The demand importer doesn't work on Python 3.5.
26 26 if sys.version_info[0:2] == (3, 5):
27 27 sys.exit(80)
28 28
29 29 if ispy3:
30 30 from importlib.util import _LazyModule
31 31
32 32 try:
33 33 from importlib.util import _Module as moduletype
34 34 except ImportError:
35 35 moduletype = types.ModuleType
36 36 else:
37 37 moduletype = types.ModuleType
38 38
39 39 if os.name != 'nt':
40 40 try:
41 41 import distutils.msvc9compiler
42 42
43 43 print(
44 44 'distutils.msvc9compiler needs to be an immediate '
45 45 'importerror on non-windows platforms'
46 46 )
47 47 distutils.msvc9compiler
48 48 except ImportError:
49 49 pass
50 50
51 51 import re
52 52
53 53 rsub = re.sub
54 54
55 55
56 56 def f(obj):
57 57 l = repr(obj)
58 58 l = rsub("0x[0-9a-fA-F]+", "0x?", l)
59 59 l = rsub("from '.*'", "from '?'", l)
60 60 l = rsub("'<[a-z]*>'", "'<whatever>'", l)
61 61 return l
62 62
63 63
64 64 demandimport.disable()
65 65 os.environ['HGDEMANDIMPORT'] = 'disable'
66 66 # this enable call should not actually enable demandimport!
67 67 demandimport.enable()
68 68 from mercurial import node
69 69
70 70 # We use assert instead of a unittest test case because having imports inside
71 71 # functions changes behavior of the demand importer.
72 72 if ispy3:
73 73 assert not isinstance(node, _LazyModule)
74 74 else:
75 75 assert f(node) == "<module 'mercurial.node' from '?'>", f(node)
76 76
77 77 # now enable it for real
78 78 del os.environ['HGDEMANDIMPORT']
79 79 demandimport.enable()
80 80
81 81 # Test access to special attributes through demandmod proxy
82 82 assert 'mercurial.error' not in sys.modules
83 83 from mercurial import error as errorproxy
84 84
85 85 if ispy3:
86 86 # unsure why this isn't lazy.
87 87 assert not isinstance(f, _LazyModule)
88 88 assert f(errorproxy) == "<module 'mercurial.error' from '?'>", f(errorproxy)
89 89 else:
90 90 assert f(errorproxy) == "<unloaded module 'error'>", f(errorproxy)
91 91
92 92 doc = ' '.join(errorproxy.__doc__.split()[:3])
93 93 assert doc == 'Mercurial exceptions. This', doc
94 94 assert errorproxy.__name__ == 'mercurial.error', errorproxy.__name__
95 95
96 96 # __name__ must be accessible via __dict__ so the relative imports can be
97 97 # resolved
98 98 name = errorproxy.__dict__['__name__']
99 99 assert name == 'mercurial.error', name
100 100
101 101 if ispy3:
102 102 assert not isinstance(errorproxy, _LazyModule)
103 103 assert f(errorproxy) == "<module 'mercurial.error' from '?'>", f(errorproxy)
104 104 else:
105 105 assert f(errorproxy) == "<proxied module 'error'>", f(errorproxy)
106 106
107 107 import os
108 108
109 109 if ispy3:
110 110 assert not isinstance(os, _LazyModule)
111 111 assert f(os) == "<module 'os' from '?'>", f(os)
112 112 else:
113 113 assert f(os) == "<unloaded module 'os'>", f(os)
114 114
115 115 assert f(os.system) == '<built-in function system>', f(os.system)
116 116 assert f(os) == "<module 'os' from '?'>", f(os)
117 117
118 118 assert 'mercurial.utils.procutil' not in sys.modules
119 119 from mercurial.utils import procutil
120 120
121 121 if ispy3:
122 122 assert isinstance(procutil, _LazyModule)
123 123 assert f(procutil) == "<module 'mercurial.utils.procutil' from '?'>", f(
124 124 procutil
125 125 )
126 126 else:
127 127 assert f(procutil) == "<unloaded module 'procutil'>", f(procutil)
128 128
129 129 assert f(procutil.system) == '<function system at 0x?>', f(procutil.system)
130 130 assert procutil.__class__ == moduletype, procutil.__class__
131 131 assert f(procutil) == "<module 'mercurial.utils.procutil' from '?'>", f(
132 132 procutil
133 133 )
134 134 assert f(procutil.system) == '<function system at 0x?>', f(procutil.system)
135 135
136 136 assert 'mercurial.hgweb' not in sys.modules
137 137 from mercurial import hgweb
138 138
139 139 if ispy3:
140 140 assert isinstance(hgweb, _LazyModule)
141 141 assert f(hgweb) == "<module 'mercurial.hgweb' from '?'>", f(hgweb)
142 142 assert isinstance(hgweb.hgweb_mod, _LazyModule)
143 143 assert (
144 144 f(hgweb.hgweb_mod) == "<module 'mercurial.hgweb.hgweb_mod' from '?'>"
145 145 ), f(hgweb.hgweb_mod)
146 146 else:
147 147 assert f(hgweb) == "<unloaded module 'hgweb'>", f(hgweb)
148 148 assert f(hgweb.hgweb_mod) == "<unloaded module 'hgweb_mod'>", f(
149 149 hgweb.hgweb_mod
150 150 )
151 151
152 152 assert f(hgweb) == "<module 'mercurial.hgweb' from '?'>", f(hgweb)
153 153
154 154 import re as fred
155 155
156 156 if ispy3:
157 157 assert not isinstance(fred, _LazyModule)
158 158 assert f(fred) == "<module 're' from '?'>"
159 159 else:
160 160 assert f(fred) == "<unloaded module 're'>", f(fred)
161 161
162 162 import re as remod
163 163
164 164 if ispy3:
165 165 assert not isinstance(remod, _LazyModule)
166 166 assert f(remod) == "<module 're' from '?'>"
167 167 else:
168 168 assert f(remod) == "<unloaded module 're'>", f(remod)
169 169
170 170 import sys as re
171 171
172 172 if ispy3:
173 173 assert not isinstance(re, _LazyModule)
174 174 assert f(re) == "<module 'sys' (built-in)>"
175 175 else:
176 176 assert f(re) == "<unloaded module 'sys'>", f(re)
177 177
178 178 if ispy3:
179 179 assert not isinstance(fred, _LazyModule)
180 180 assert f(fred) == "<module 're' from '?'>", f(fred)
181 181 else:
182 182 assert f(fred) == "<unloaded module 're'>", f(fred)
183 183
184 184 assert f(fred.sub) == '<function sub at 0x?>', f(fred.sub)
185 185
186 186 if ispy3:
187 187 assert not isinstance(fred, _LazyModule)
188 188 assert f(fred) == "<module 're' from '?'>", f(fred)
189 189 else:
190 190 assert f(fred) == "<proxied module 're'>", f(fred)
191 191
192 192 remod.escape # use remod
193 193 assert f(remod) == "<module 're' from '?'>", f(remod)
194 194
195 195 if ispy3:
196 196 assert not isinstance(re, _LazyModule)
197 197 assert f(re) == "<module 'sys' (built-in)>"
198 198 assert f(type(re.stderr)) == "<class '_io.TextIOWrapper'>", f(
199 199 type(re.stderr)
200 200 )
201 201 assert f(re) == "<module 'sys' (built-in)>"
202 202 else:
203 203 assert f(re) == "<unloaded module 'sys'>", f(re)
204 204 assert f(re.stderr) == "<open file '<whatever>', mode 'w' at 0x?>", f(
205 205 re.stderr
206 206 )
207 207 assert f(re) == "<proxied module 'sys'>", f(re)
208 208
209 209 assert 'telnetlib' not in sys.modules
210 210 import telnetlib
211 211
212 212 if ispy3:
213 213 assert isinstance(telnetlib, _LazyModule)
214 214 assert f(telnetlib) == "<module 'telnetlib' from '?'>"
215 215 else:
216 216 assert f(telnetlib) == "<unloaded module 'telnetlib'>", f(telnetlib)
217 217
218 218 try:
219 219 from telnetlib import unknownattr
220 220
221 221 assert False, (
222 222 'no demandmod should be created for attribute of non-package '
223 223 'module:\ntelnetlib.unknownattr = %s' % f(unknownattr)
224 224 )
225 225 except ImportError as inst:
226 226 assert rsub(r"'", '', str(inst)).startswith(
227 227 'cannot import name unknownattr'
228 228 )
229 229
230 230 from mercurial import util
231 231
232 232 # Unlike the import statement, __import__() function should not raise
233 233 # ImportError even if fromlist has an unknown item
234 234 # (see Python/import.c:import_module_level() and ensure_fromlist())
235 235 assert 'ftplib' not in sys.modules
236 236 zipfileimp = __import__('ftplib', globals(), locals(), ['unknownattr'])
237 237 assert f(zipfileimp) == "<module 'ftplib' from '?'>", f(zipfileimp)
238 238 assert not util.safehasattr(zipfileimp, 'unknownattr')
@@ -1,443 +1,443 b''
1 1 #require no-reposimplestore
2 2
3 3 Testing the case when there is no infinitepush extension present on the client
4 4 side and the server routes each push to bundlestore. This case is very much
5 5 similar to CI use case.
6 6
7 7 Setup
8 8 -----
9 9
10 10 $ . "$TESTDIR/library-infinitepush.sh"
11 11 $ cat >> $HGRCPATH <<EOF
12 12 > [ui]
13 > ssh = python "$TESTDIR/dummyssh"
13 > ssh = $PYTHON "$TESTDIR/dummyssh"
14 14 > [alias]
15 15 > glog = log -GT "{rev}:{node|short} {desc}\n{phase}"
16 16 > EOF
17 17 $ cp $HGRCPATH $TESTTMP/defaulthgrc
18 18 $ hg init repo
19 19 $ cd repo
20 20 $ setupserver
21 21 $ echo "pushtobundlestore = True" >> .hg/hgrc
22 22 $ echo "[extensions]" >> .hg/hgrc
23 23 $ echo "infinitepush=" >> .hg/hgrc
24 24 $ echo initialcommit > initialcommit
25 25 $ hg ci -Aqm "initialcommit"
26 26 $ hg phase --public .
27 27
28 28 $ cd ..
29 29 $ hg clone repo client -q
30 30 $ hg clone repo client2 -q
31 31 $ hg clone ssh://user@dummy/repo client3 -q
32 32 $ cd client
33 33
34 34 Pushing a new commit from the client to the server
35 35 -----------------------------------------------------
36 36
37 37 $ echo foobar > a
38 38 $ hg ci -Aqm "added a"
39 39 $ hg glog
40 40 @ 1:6cb0989601f1 added a
41 41 | draft
42 42 o 0:67145f466344 initialcommit
43 43 public
44 44
45 45 $ hg push
46 46 pushing to $TESTTMP/repo
47 47 searching for changes
48 48 storing changesets on the bundlestore
49 49 pushing 1 commit:
50 50 6cb0989601f1 added a
51 51
52 52 $ scratchnodes
53 53 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
54 54
55 55 Understanding how data is stored on the bundlestore in server
56 56 -------------------------------------------------------------
57 57
58 58 There are two things, filebundlestore and index
59 59 $ ls ../repo/.hg/scratchbranches
60 60 filebundlestore
61 61 index
62 62
63 63 filebundlestore stores the bundles
64 64 $ ls ../repo/.hg/scratchbranches/filebundlestore/3b/41/
65 65 3b414252ff8acab801318445d88ff48faf4a28c3
66 66
67 67 index/nodemap stores a map of node id and file in which bundle is stored in filebundlestore
68 68 $ ls ../repo/.hg/scratchbranches/index/
69 69 nodemap
70 70 $ ls ../repo/.hg/scratchbranches/index/nodemap/
71 71 6cb0989601f1fb5805238edfb16f3606713d9a0b
72 72
73 73 $ cd ../repo
74 74
75 75 Checking that the commit was not applied to revlog on the server
76 76 ------------------------------------------------------------------
77 77
78 78 $ hg glog
79 79 @ 0:67145f466344 initialcommit
80 80 public
81 81
82 82 Applying the changeset from the bundlestore
83 83 --------------------------------------------
84 84
85 85 $ hg unbundle .hg/scratchbranches/filebundlestore/3b/41/3b414252ff8acab801318445d88ff48faf4a28c3
86 86 adding changesets
87 87 adding manifests
88 88 adding file changes
89 89 added 1 changesets with 1 changes to 1 files
90 90 new changesets 6cb0989601f1
91 91 (run 'hg update' to get a working copy)
92 92
93 93 $ hg glog
94 94 o 1:6cb0989601f1 added a
95 95 | public
96 96 @ 0:67145f466344 initialcommit
97 97 public
98 98
99 99 Pushing more changesets from the local repo
100 100 --------------------------------------------
101 101
102 102 $ cd ../client
103 103 $ echo b > b
104 104 $ hg ci -Aqm "added b"
105 105 $ echo c > c
106 106 $ hg ci -Aqm "added c"
107 107 $ hg glog
108 108 @ 3:bf8a6e3011b3 added c
109 109 | draft
110 110 o 2:eaba929e866c added b
111 111 | draft
112 112 o 1:6cb0989601f1 added a
113 113 | public
114 114 o 0:67145f466344 initialcommit
115 115 public
116 116
117 117 $ hg push
118 118 pushing to $TESTTMP/repo
119 119 searching for changes
120 120 storing changesets on the bundlestore
121 121 pushing 2 commits:
122 122 eaba929e866c added b
123 123 bf8a6e3011b3 added c
124 124
125 125 Checking that changesets are not applied on the server
126 126 ------------------------------------------------------
127 127
128 128 $ hg glog -R ../repo
129 129 o 1:6cb0989601f1 added a
130 130 | public
131 131 @ 0:67145f466344 initialcommit
132 132 public
133 133
134 134 Both of the new changesets are stored in a single bundle-file
135 135 $ scratchnodes
136 136 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
137 137 bf8a6e3011b345146bbbedbcb1ebd4837571492a 239585f5e61f0c09ce7106bdc1097bff731738f4
138 138 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 239585f5e61f0c09ce7106bdc1097bff731738f4
139 139
140 140 Pushing more changesets to the server
141 141 -------------------------------------
142 142
143 143 $ echo d > d
144 144 $ hg ci -Aqm "added d"
145 145 $ echo e > e
146 146 $ hg ci -Aqm "added e"
147 147
148 148 XXX: we should have pushed only the parts which are not in bundlestore
149 149 $ hg push
150 150 pushing to $TESTTMP/repo
151 151 searching for changes
152 152 storing changesets on the bundlestore
153 153 pushing 4 commits:
154 154 eaba929e866c added b
155 155 bf8a6e3011b3 added c
156 156 1bb96358eda2 added d
157 157 b4e4bce66051 added e
158 158
159 159 Sneak peek into the bundlestore at the server
160 160 $ scratchnodes
161 161 1bb96358eda285b536c6d1c66846a7cdb2336cea 98fbae0016662521b0007da1b7bc349cd3caacd1
162 162 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
163 163 b4e4bce660512ad3e71189e14588a70ac8e31fef 98fbae0016662521b0007da1b7bc349cd3caacd1
164 164 bf8a6e3011b345146bbbedbcb1ebd4837571492a 98fbae0016662521b0007da1b7bc349cd3caacd1
165 165 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 98fbae0016662521b0007da1b7bc349cd3caacd1
166 166
167 167 Checking if `hg pull` pulls something or `hg incoming` shows something
168 168 -----------------------------------------------------------------------
169 169
170 170 $ hg incoming
171 171 comparing with $TESTTMP/repo
172 172 searching for changes
173 173 no changes found
174 174 [1]
175 175
176 176 $ hg pull
177 177 pulling from $TESTTMP/repo
178 178 searching for changes
179 179 no changes found
180 180
181 181 Pulling from second client which is a localpeer to test `hg pull -r <rev>`
182 182 --------------------------------------------------------------------------
183 183
184 184 Pulling the revision which is applied
185 185
186 186 $ cd ../client2
187 187 $ hg pull -r 6cb0989601f1
188 188 pulling from $TESTTMP/repo
189 189 searching for changes
190 190 adding changesets
191 191 adding manifests
192 192 adding file changes
193 193 added 1 changesets with 1 changes to 1 files
194 194 new changesets 6cb0989601f1
195 195 (run 'hg update' to get a working copy)
196 196 $ hg glog
197 197 o 1:6cb0989601f1 added a
198 198 | public
199 199 @ 0:67145f466344 initialcommit
200 200 public
201 201
202 202 Pulling the revision which is in bundlestore
203 203 XXX: we should support pulling revisions from a local peers bundlestore without
204 204 client side wrapping
205 205
206 206 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
207 207 pulling from $TESTTMP/repo
208 208 abort: unknown revision 'b4e4bce660512ad3e71189e14588a70ac8e31fef'
209 209 [255]
210 210 $ hg glog
211 211 o 1:6cb0989601f1 added a
212 212 | public
213 213 @ 0:67145f466344 initialcommit
214 214 public
215 215
216 216 $ cd ../client
217 217
218 218 Pulling from third client which is not a localpeer
219 219 ---------------------------------------------------
220 220
221 221 Pulling the revision which is applied
222 222
223 223 $ cd ../client3
224 224 $ hg pull -r 6cb0989601f1
225 225 pulling from ssh://user@dummy/repo
226 226 searching for changes
227 227 adding changesets
228 228 adding manifests
229 229 adding file changes
230 230 added 1 changesets with 1 changes to 1 files
231 231 new changesets 6cb0989601f1
232 232 (run 'hg update' to get a working copy)
233 233 $ hg glog
234 234 o 1:6cb0989601f1 added a
235 235 | public
236 236 @ 0:67145f466344 initialcommit
237 237 public
238 238
239 239 Pulling the revision which is in bundlestore
240 240
241 241 Trying to specify short hash
242 242 XXX: we should support this
243 243 $ hg pull -r b4e4bce660512
244 244 pulling from ssh://user@dummy/repo
245 245 abort: unknown revision 'b4e4bce660512'
246 246 [255]
247 247
248 248 XXX: we should show better message when the pull is happening from bundlestore
249 249 $ hg pull -r b4e4bce660512ad3e71189e14588a70ac8e31fef
250 250 pulling from ssh://user@dummy/repo
251 251 searching for changes
252 252 adding changesets
253 253 adding manifests
254 254 adding file changes
255 255 added 4 changesets with 4 changes to 4 files
256 256 new changesets eaba929e866c:b4e4bce66051
257 257 (run 'hg update' to get a working copy)
258 258 $ hg glog
259 259 o 5:b4e4bce66051 added e
260 260 | public
261 261 o 4:1bb96358eda2 added d
262 262 | public
263 263 o 3:bf8a6e3011b3 added c
264 264 | public
265 265 o 2:eaba929e866c added b
266 266 | public
267 267 o 1:6cb0989601f1 added a
268 268 | public
269 269 @ 0:67145f466344 initialcommit
270 270 public
271 271
272 272 $ cd ../client
273 273
274 274 Checking storage of phase information with the bundle on bundlestore
275 275 ---------------------------------------------------------------------
276 276
277 277 creating a draft commit
278 278 $ cat >> $HGRCPATH <<EOF
279 279 > [phases]
280 280 > publish = False
281 281 > EOF
282 282 $ echo f > f
283 283 $ hg ci -Aqm "added f"
284 284 $ hg glog -r '.^::'
285 285 @ 6:9b42578d4447 added f
286 286 | draft
287 287 o 5:b4e4bce66051 added e
288 288 | public
289 289 ~
290 290
291 291 $ hg push
292 292 pushing to $TESTTMP/repo
293 293 searching for changes
294 294 storing changesets on the bundlestore
295 295 pushing 5 commits:
296 296 eaba929e866c added b
297 297 bf8a6e3011b3 added c
298 298 1bb96358eda2 added d
299 299 b4e4bce66051 added e
300 300 9b42578d4447 added f
301 301
302 302 XXX: the phase of 9b42578d4447 should not be changed here
303 303 $ hg glog -r .
304 304 @ 6:9b42578d4447 added f
305 305 | public
306 306 ~
307 307
308 308 applying the bundle on the server to check preservation of phase-information
309 309
310 310 $ cd ../repo
311 311 $ scratchnodes
312 312 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
313 313 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
314 314 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
315 315 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
316 316 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
317 317 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
318 318
319 319 $ hg unbundle .hg/scratchbranches/filebundlestore/28/0a/280a46a259a268f0e740c81c5a7751bdbfaec85f
320 320 adding changesets
321 321 adding manifests
322 322 adding file changes
323 323 added 5 changesets with 5 changes to 5 files
324 324 new changesets eaba929e866c:9b42578d4447 (1 drafts)
325 325 (run 'hg update' to get a working copy)
326 326
327 327 $ hg glog
328 328 o 6:9b42578d4447 added f
329 329 | draft
330 330 o 5:b4e4bce66051 added e
331 331 | public
332 332 o 4:1bb96358eda2 added d
333 333 | public
334 334 o 3:bf8a6e3011b3 added c
335 335 | public
336 336 o 2:eaba929e866c added b
337 337 | public
338 338 o 1:6cb0989601f1 added a
339 339 | public
340 340 @ 0:67145f466344 initialcommit
341 341 public
342 342
343 343 Checking storage of obsmarkers in the bundlestore
344 344 --------------------------------------------------
345 345
346 346 enabling obsmarkers and rebase extension
347 347
348 348 $ cat >> $HGRCPATH << EOF
349 349 > [experimental]
350 350 > evolution = all
351 351 > [extensions]
352 352 > rebase =
353 353 > EOF
354 354
355 355 $ cd ../client
356 356
357 357 $ hg phase -r . --draft --force
358 358 $ hg rebase -r 6 -d 3
359 359 rebasing 6:9b42578d4447 tip "added f"
360 360
361 361 $ hg glog
362 362 @ 7:99949238d9ac added f
363 363 | draft
364 364 | o 5:b4e4bce66051 added e
365 365 | | public
366 366 | o 4:1bb96358eda2 added d
367 367 |/ public
368 368 o 3:bf8a6e3011b3 added c
369 369 | public
370 370 o 2:eaba929e866c added b
371 371 | public
372 372 o 1:6cb0989601f1 added a
373 373 | public
374 374 o 0:67145f466344 initialcommit
375 375 public
376 376
377 377 $ hg push -f
378 378 pushing to $TESTTMP/repo
379 379 searching for changes
380 380 storing changesets on the bundlestore
381 381 pushing 1 commit:
382 382 99949238d9ac added f
383 383
384 384 XXX: the phase should not have changed here
385 385 $ hg glog -r .
386 386 @ 7:99949238d9ac added f
387 387 | public
388 388 ~
389 389
390 390 Unbundling on server to see obsmarkers being applied
391 391
392 392 $ cd ../repo
393 393
394 394 $ scratchnodes
395 395 1bb96358eda285b536c6d1c66846a7cdb2336cea 280a46a259a268f0e740c81c5a7751bdbfaec85f
396 396 6cb0989601f1fb5805238edfb16f3606713d9a0b 3b414252ff8acab801318445d88ff48faf4a28c3
397 397 99949238d9ac7f2424a33a46dface6f866afd059 090a24fe63f31d3b4bee714447f835c8c362ff57
398 398 9b42578d44473575994109161430d65dd147d16d 280a46a259a268f0e740c81c5a7751bdbfaec85f
399 399 b4e4bce660512ad3e71189e14588a70ac8e31fef 280a46a259a268f0e740c81c5a7751bdbfaec85f
400 400 bf8a6e3011b345146bbbedbcb1ebd4837571492a 280a46a259a268f0e740c81c5a7751bdbfaec85f
401 401 eaba929e866c59bc9a6aada5a9dd2f6990db83c0 280a46a259a268f0e740c81c5a7751bdbfaec85f
402 402
403 403 $ hg glog
404 404 o 6:9b42578d4447 added f
405 405 | draft
406 406 o 5:b4e4bce66051 added e
407 407 | public
408 408 o 4:1bb96358eda2 added d
409 409 | public
410 410 o 3:bf8a6e3011b3 added c
411 411 | public
412 412 o 2:eaba929e866c added b
413 413 | public
414 414 o 1:6cb0989601f1 added a
415 415 | public
416 416 @ 0:67145f466344 initialcommit
417 417 public
418 418
419 419 $ hg unbundle .hg/scratchbranches/filebundlestore/09/0a/090a24fe63f31d3b4bee714447f835c8c362ff57
420 420 adding changesets
421 421 adding manifests
422 422 adding file changes
423 423 added 1 changesets with 0 changes to 1 files (+1 heads)
424 424 1 new obsolescence markers
425 425 obsoleted 1 changesets
426 426 new changesets 99949238d9ac (1 drafts)
427 427 (run 'hg heads' to see heads, 'hg merge' to merge)
428 428
429 429 $ hg glog
430 430 o 7:99949238d9ac added f
431 431 | draft
432 432 | o 5:b4e4bce66051 added e
433 433 | | public
434 434 | o 4:1bb96358eda2 added d
435 435 |/ public
436 436 o 3:bf8a6e3011b3 added c
437 437 | public
438 438 o 2:eaba929e866c added b
439 439 | public
440 440 o 1:6cb0989601f1 added a
441 441 | public
442 442 @ 0:67145f466344 initialcommit
443 443 public
@@ -1,85 +1,85 b''
1 1 #require no-windows
2 2
3 3 Dummy extension simulating unsafe long running command
4 4 $ cat > sleepext.py <<EOF
5 5 > import itertools
6 6 > import time
7 7 >
8 8 > from mercurial.i18n import _
9 9 > from mercurial import registrar
10 10 >
11 11 > cmdtable = {}
12 12 > command = registrar.command(cmdtable)
13 13 >
14 14 > @command(b'sleep', [], _(b'TIME'), norepo=True)
15 15 > def sleep(ui, sleeptime=b"1", **opts):
16 16 > with ui.uninterruptible():
17 17 > for _i in itertools.repeat(None, int(sleeptime)):
18 18 > time.sleep(1)
19 19 > ui.warn(b"end of unsafe operation\n")
20 20 > ui.warn(b"%s second(s) passed\n" % sleeptime)
21 21 > EOF
22 22
23 23 Kludge to emulate timeout(1) which is not generally available.
24 24 $ cat > timeout.py <<EOF
25 25 > from __future__ import print_function
26 26 > import argparse
27 27 > import signal
28 28 > import subprocess
29 29 > import sys
30 30 > import time
31 31 >
32 32 > ap = argparse.ArgumentParser()
33 33 > ap.add_argument('-s', nargs=1, default='SIGTERM')
34 34 > ap.add_argument('duration', nargs=1, type=int)
35 35 > ap.add_argument('argv', nargs='*')
36 36 > opts = ap.parse_args()
37 37 > try:
38 38 > sig = int(opts.s[0])
39 39 > except ValueError:
40 40 > sname = opts.s[0]
41 41 > if not sname.startswith('SIG'):
42 42 > sname = 'SIG' + sname
43 43 > sig = getattr(signal, sname)
44 44 > proc = subprocess.Popen(opts.argv)
45 45 > time.sleep(opts.duration[0])
46 46 > proc.poll()
47 47 > if proc.returncode is None:
48 48 > proc.send_signal(sig)
49 49 > proc.wait()
50 50 > sys.exit(124)
51 51 > EOF
52 52
53 53 Set up repository
54 54 $ hg init repo
55 55 $ cd repo
56 56 $ cat >> $HGRCPATH << EOF
57 57 > [extensions]
58 58 > sleepext = ../sleepext.py
59 59 > EOF
60 60
61 61 Test ctrl-c
62 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
62 $ $PYTHON $TESTTMP/timeout.py -s INT 1 hg sleep 2
63 63 interrupted!
64 64 [124]
65 65
66 66 $ cat >> $HGRCPATH << EOF
67 67 > [experimental]
68 68 > nointerrupt = yes
69 69 > EOF
70 70
71 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
71 $ $PYTHON $TESTTMP/timeout.py -s INT 1 hg sleep 2
72 72 interrupted!
73 73 [124]
74 74
75 75 $ cat >> $HGRCPATH << EOF
76 76 > [experimental]
77 77 > nointerrupt-interactiveonly = False
78 78 > EOF
79 79
80 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
80 $ $PYTHON $TESTTMP/timeout.py -s INT 1 hg sleep 2
81 81 shutting down cleanly
82 82 press ^C again to terminate immediately (dangerous)
83 83 end of unsafe operation
84 84 interrupted!
85 85 [124]
@@ -1,125 +1,125 b''
1 1 #require no-windows
2 2
3 3 $ . "$TESTDIR/remotefilelog-library.sh"
4 4
5 5 $ hg init repo
6 6 $ cd repo
7 7 $ cat >> .hg/hgrc <<EOF
8 8 > [remotefilelog]
9 9 > server=True
10 10 > EOF
11 11 $ echo x > x
12 12 $ echo y > y
13 13 $ echo z > z
14 14 $ hg commit -qAm xy
15 15 $ cd ..
16 16
17 17 $ cat > cacheprocess-logger.py <<EOF
18 18 > import os
19 19 > import shutil
20 20 > import sys
21 21 > if sys.version_info[0] > 2:
22 22 > xrange = range
23 23 > f = open('$TESTTMP/cachelog.log', 'w')
24 24 > srccache = os.path.join('$TESTTMP', 'oldhgcache')
25 25 > def log(message):
26 26 > f.write(message)
27 27 > f.flush()
28 28 > destcache = sys.argv[-1]
29 29 > try:
30 30 > while True:
31 31 > cmd = sys.stdin.readline().strip()
32 32 > log('got command %r\n' % cmd)
33 33 > if cmd == 'exit':
34 34 > sys.exit(0)
35 35 > elif cmd == 'get':
36 36 > count = int(sys.stdin.readline())
37 37 > log('client wants %r blobs\n' % count)
38 38 > wants = []
39 39 > for _ in xrange(count):
40 40 > key = sys.stdin.readline()[:-1]
41 41 > wants.append(key)
42 42 > if '\0' in key:
43 43 > _, key = key.split('\0')
44 44 > srcpath = os.path.join(srccache, key)
45 45 > if os.path.exists(srcpath):
46 46 > dest = os.path.join(destcache, key)
47 47 > destdir = os.path.dirname(dest)
48 48 > if not os.path.exists(destdir):
49 49 > os.makedirs(destdir)
50 50 > shutil.copyfile(srcpath, dest)
51 51 > else:
52 52 > # report a cache miss
53 53 > sys.stdout.write(key + '\n')
54 54 > sys.stdout.write('0\n')
55 55 > for key in sorted(wants):
56 56 > log('requested %r\n' % key)
57 57 > sys.stdout.flush()
58 58 > elif cmd == 'set':
59 59 > raise Exception('todo writing')
60 60 > else:
61 61 > raise Exception('unknown command! %r' % cmd)
62 62 > except Exception as e:
63 63 > log('Exception! %s\n' % e)
64 64 > raise
65 65 > EOF
66 66
67 67 $ cat >> $HGRCPATH <<EOF
68 68 > [remotefilelog]
69 > cacheprocess = python $TESTTMP/cacheprocess-logger.py
69 > cacheprocess = $PYTHON $TESTTMP/cacheprocess-logger.py
70 70 > EOF
71 71
72 72 Test cache keys and cache misses.
73 73 $ hgcloneshallow ssh://user@dummy/repo clone -q
74 74 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
75 75 $ cat cachelog.log
76 76 got command 'get'
77 77 client wants 3 blobs
78 78 requested 'master/11/f6ad8ec52a2984abaafd7c3b516503785c2072/1406e74118627694268417491f018a4a883152f0'
79 79 requested 'master/39/5df8f7c51f007019cb30201c49e884b46b92fa/69a1b67522704ec122181c0890bd16e9d3e7516a'
80 80 requested 'master/95/cb0bfd2977c761298d9624e4b4d4c72a39974a/076f5e2225b3ff0400b98c92aa6cdf403ee24cca'
81 81 got command 'set'
82 82 Exception! todo writing
83 83
84 84 Test cache hits.
85 85 $ mv hgcache oldhgcache
86 86 $ rm cachelog.log
87 87 $ hgcloneshallow ssh://user@dummy/repo clone-cachehit -q
88 88 3 files fetched over 1 fetches - (0 misses, 100.00% hit ratio) over *s (glob)
89 89 $ cat cachelog.log | grep -v exit
90 90 got command 'get'
91 91 client wants 3 blobs
92 92 requested 'master/11/f6ad8ec52a2984abaafd7c3b516503785c2072/1406e74118627694268417491f018a4a883152f0'
93 93 requested 'master/39/5df8f7c51f007019cb30201c49e884b46b92fa/69a1b67522704ec122181c0890bd16e9d3e7516a'
94 94 requested 'master/95/cb0bfd2977c761298d9624e4b4d4c72a39974a/076f5e2225b3ff0400b98c92aa6cdf403ee24cca'
95 95
96 96 $ cat >> $HGRCPATH <<EOF
97 97 > [remotefilelog]
98 98 > cacheprocess.includepath = yes
99 99 > EOF
100 100
101 101 Test cache keys and cache misses with includepath.
102 102 $ rm -r hgcache oldhgcache
103 103 $ rm cachelog.log
104 104 $ hgcloneshallow ssh://user@dummy/repo clone-withpath -q
105 105 3 files fetched over 1 fetches - (3 misses, 0.00% hit ratio) over *s (glob)
106 106 $ cat cachelog.log
107 107 got command 'get'
108 108 client wants 3 blobs
109 109 requested 'x\x00master/11/f6ad8ec52a2984abaafd7c3b516503785c2072/1406e74118627694268417491f018a4a883152f0'
110 110 requested 'y\x00master/95/cb0bfd2977c761298d9624e4b4d4c72a39974a/076f5e2225b3ff0400b98c92aa6cdf403ee24cca'
111 111 requested 'z\x00master/39/5df8f7c51f007019cb30201c49e884b46b92fa/69a1b67522704ec122181c0890bd16e9d3e7516a'
112 112 got command 'set'
113 113 Exception! todo writing
114 114
115 115 Test cache hits with includepath.
116 116 $ mv hgcache oldhgcache
117 117 $ rm cachelog.log
118 118 $ hgcloneshallow ssh://user@dummy/repo clone-withpath-cachehit -q
119 119 3 files fetched over 1 fetches - (0 misses, 100.00% hit ratio) over *s (glob)
120 120 $ cat cachelog.log | grep -v exit
121 121 got command 'get'
122 122 client wants 3 blobs
123 123 requested 'x\x00master/11/f6ad8ec52a2984abaafd7c3b516503785c2072/1406e74118627694268417491f018a4a883152f0'
124 124 requested 'y\x00master/95/cb0bfd2977c761298d9624e4b4d4c72a39974a/076f5e2225b3ff0400b98c92aa6cdf403ee24cca'
125 125 requested 'z\x00master/39/5df8f7c51f007019cb30201c49e884b46b92fa/69a1b67522704ec122181c0890bd16e9d3e7516a'
@@ -1,147 +1,147 b''
1 1 #require execbit unix-permissions no-chg
2 2
3 3 Checking that experimental.atomic-file works.
4 4
5 5 $ cat > $TESTTMP/show_mode.py <<EOF
6 6 > from __future__ import print_function
7 7 > import os
8 8 > import stat
9 9 > import sys
10 10 > ST_MODE = stat.ST_MODE
11 11 >
12 12 > for file_path in sys.argv[1:]:
13 13 > file_stat = os.stat(file_path)
14 14 > octal_mode = oct(file_stat[ST_MODE] & 0o777).replace('o', '')
15 15 > print("%s:%s" % (file_path, octal_mode))
16 16 >
17 17 > EOF
18 18
19 19 $ hg init repo
20 20 $ cd repo
21 21
22 22 $ cat > .hg/showwrites.py <<EOF
23 23 > from __future__ import print_function
24 24 > from mercurial import pycompat
25 25 > from mercurial.utils import stringutil
26 26 > def uisetup(ui):
27 27 > from mercurial import vfs
28 28 > class newvfs(vfs.vfs):
29 29 > def __call__(self, *args, **kwargs):
30 30 > print(pycompat.sysstr(stringutil.pprint(
31 31 > ('vfs open', args, sorted(list(kwargs.items()))))))
32 32 > return super(newvfs, self).__call__(*args, **kwargs)
33 33 > vfs.vfs = newvfs
34 34 > EOF
35 35
36 36 $ for v in a1 a2 b1 b2 c ro; do echo $v > $v; done
37 37 $ chmod +x b*
38 38 $ hg commit -Aqm _
39 39
40 40 # We check that
41 41 # - the changes are actually atomic
42 42 # - that permissions are correct (all 4 cases of (executable before) * (executable after))
43 43 # - that renames work, though they should be atomic anyway
44 44 # - that it works when source files are read-only (but directories are read-write still)
45 45
46 46 $ for v in a1 a2 b1 b2 ro; do echo changed-$v > $v; done
47 47 $ chmod -x *1; chmod +x *2
48 48 $ hg rename c d
49 49 $ hg commit -qm _
50 50
51 51 Check behavior without update.atomic-file
52 52
53 53 $ hg update -r 0 -q
54 54 $ hg update -r 1 --config extensions.showwrites=.hg/showwrites.py 2>&1 | grep "a1'.*wb"
55 55 ('vfs open', ('a1', 'wb'), [('atomictemp', False), ('backgroundclose', True)])
56 56
57 $ python $TESTTMP/show_mode.py *
57 $ $PYTHON $TESTTMP/show_mode.py *
58 58 a1:0644
59 59 a2:0755
60 60 b1:0644
61 61 b2:0755
62 62 d:0644
63 63 ro:0644
64 64
65 65 Add a second revision for the ro file so we can test update when the file is
66 66 present or not
67 67
68 68 $ echo "ro" > ro
69 69
70 70 $ hg commit -qm _
71 71
72 72 Check behavior without update.atomic-file first
73 73
74 74 $ hg update -C -r 0 -q
75 75
76 76 $ hg update -r 1
77 77 6 files updated, 0 files merged, 1 files removed, 0 files unresolved
78 78
79 $ python $TESTTMP/show_mode.py *
79 $ $PYTHON $TESTTMP/show_mode.py *
80 80 a1:0644
81 81 a2:0755
82 82 b1:0644
83 83 b2:0755
84 84 d:0644
85 85 ro:0644
86 86
87 87 Manually reset the mode of the read-only file
88 88
89 89 $ chmod a-w ro
90 90
91 $ python $TESTTMP/show_mode.py ro
91 $ $PYTHON $TESTTMP/show_mode.py ro
92 92 ro:0444
93 93
94 94 Now the file is present, try to update and check the permissions of the file
95 95
96 96 $ hg up -r 2
97 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98
99 $ python $TESTTMP/show_mode.py ro
99 $ $PYTHON $TESTTMP/show_mode.py ro
100 100 ro:0644
101 101
102 102 # The file which was read-only is now writable in the default behavior
103 103
104 104 Check behavior with update.atomic-files
105 105
106 106
107 107 $ cat >> .hg/hgrc <<EOF
108 108 > [experimental]
109 109 > update.atomic-file = true
110 110 > EOF
111 111
112 112 $ hg update -C -r 0 -q
113 113 $ hg update -r 1 --config extensions.showwrites=.hg/showwrites.py 2>&1 | grep "a1'.*wb"
114 114 ('vfs open', ('a1', 'wb'), [('atomictemp', True), ('backgroundclose', True)])
115 115 $ hg st -A --rev 1
116 116 C a1
117 117 C a2
118 118 C b1
119 119 C b2
120 120 C d
121 121 C ro
122 122
123 123 Check the file permission after update
124 $ python $TESTTMP/show_mode.py *
124 $ $PYTHON $TESTTMP/show_mode.py *
125 125 a1:0644
126 126 a2:0755
127 127 b1:0644
128 128 b2:0755
129 129 d:0644
130 130 ro:0644
131 131
132 132 Manually reset the mode of the read-only file
133 133
134 134 $ chmod a-w ro
135 135
136 $ python $TESTTMP/show_mode.py ro
136 $ $PYTHON $TESTTMP/show_mode.py ro
137 137 ro:0444
138 138
139 139 Now the file is present, try to update and check the permissions of the file
140 140
141 141 $ hg update -r 2 --traceback
142 142 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
143 143
144 $ python $TESTTMP/show_mode.py ro
144 $ $PYTHON $TESTTMP/show_mode.py ro
145 145 ro:0644
146 146
147 147 # The behavior is the same as without atomic update
@@ -1,640 +1,642 b''
1 1 from __future__ import print_function, absolute_import
2 2
3 3 """Fuzz testing for operations against a Mercurial repository
4 4
5 5 This uses Hypothesis's stateful testing to generate random repository
6 6 operations and test Mercurial using them, both to see if there are any
7 7 unexpected errors and to compare different versions of it."""
8 8
9 9 import os
10 10 import subprocess
11 11 import sys
12 12
13 13 # Only run if slow tests are allowed
14 if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'slow']):
14 if subprocess.call(
15 [os.environ['PYTHON'], '%s/hghave' % os.environ['TESTDIR'], 'slow']
16 ):
15 17 sys.exit(80)
16 18
17 19 # These tests require Hypothesis and pytz to be installed.
18 20 # Running 'pip install hypothesis pytz' will achieve that.
19 21 # Note: This won't work if you're running Python < 2.7.
20 22 try:
21 23 from hypothesis.extra.datetime import datetimes
22 24 except ImportError:
23 25 sys.stderr.write("skipped: hypothesis or pytz not installed" + os.linesep)
24 26 sys.exit(80)
25 27
26 28 # If you are running an old version of pip you may find that the enum34
27 29 # backport is not installed automatically. If so 'pip install enum34' will
28 30 # fix this problem.
29 31 try:
30 32 import enum
31 33
32 34 assert enum # Silence pyflakes
33 35 except ImportError:
34 36 sys.stderr.write("skipped: enum34 not installed" + os.linesep)
35 37 sys.exit(80)
36 38
37 39 import binascii
38 40 from contextlib import contextmanager
39 41 import errno
40 42 import pipes
41 43 import shutil
42 44 import silenttestrunner
43 45 import subprocess
44 46
45 47 from hypothesis.errors import HypothesisException
46 48 from hypothesis.stateful import (
47 49 rule,
48 50 RuleBasedStateMachine,
49 51 Bundle,
50 52 precondition,
51 53 )
52 54 from hypothesis import settings, note, strategies as st
53 55 from hypothesis.configuration import set_hypothesis_home_dir
54 56 from hypothesis.database import ExampleDatabase
55 57
56 58 testdir = os.path.abspath(os.environ["TESTDIR"])
57 59
58 60 # We store Hypothesis examples here rather in the temporary test directory
59 61 # so that when rerunning a failing test this always results in refinding the
60 62 # previous failure. This directory is in .hgignore and should not be checked in
61 63 # but is useful to have for development.
62 64 set_hypothesis_home_dir(os.path.join(testdir, ".hypothesis"))
63 65
64 66 runtests = os.path.join(os.environ["RUNTESTDIR"], "run-tests.py")
65 67 testtmp = os.environ["TESTTMP"]
66 68 assert os.path.isdir(testtmp)
67 69
68 70 generatedtests = os.path.join(testdir, "hypothesis-generated")
69 71
70 72 try:
71 73 os.makedirs(generatedtests)
72 74 except OSError:
73 75 pass
74 76
75 77 # We write out generated .t files to a file in order to ease debugging and to
76 78 # give a starting point for turning failures Hypothesis finds into normal
77 79 # tests. In order to ensure that multiple copies of this test can be run in
78 80 # parallel we use atomic file create to ensure that we always get a unique
79 81 # name.
80 82 file_index = 0
81 83 while True:
82 84 file_index += 1
83 85 savefile = os.path.join(
84 86 generatedtests, "test-generated-%d.t" % (file_index,)
85 87 )
86 88 try:
87 89 os.close(os.open(savefile, os.O_CREAT | os.O_EXCL | os.O_WRONLY))
88 90 break
89 91 except OSError as e:
90 92 if e.errno != errno.EEXIST:
91 93 raise
92 94 assert os.path.exists(savefile)
93 95
94 96 hgrc = os.path.join(".hg", "hgrc")
95 97
96 98 filecharacters = (
97 99 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
98 100 "[]^_`;=@{}~ !#$%&'()+,-"
99 101 )
100 102
101 103 files = (
102 104 st.text(filecharacters, min_size=1)
103 105 .map(lambda x: x.strip())
104 106 .filter(bool)
105 107 .map(lambda s: s.encode('ascii'))
106 108 )
107 109
108 110 safetext = st.text(
109 111 st.characters(
110 112 min_codepoint=1, max_codepoint=127, blacklist_categories=('Cc', 'Cs')
111 113 ),
112 114 min_size=1,
113 115 ).map(lambda s: s.encode('utf-8'))
114 116
115 117 extensions = st.sampled_from(
116 118 (
117 119 'shelve',
118 120 'mq',
119 121 'blackbox',
120 122 )
121 123 )
122 124
123 125
124 126 @contextmanager
125 127 def acceptableerrors(*args):
126 128 """Sometimes we know an operation we're about to perform might fail, and
127 129 we're OK with some of the failures. In those cases this may be used as a
128 130 context manager and will swallow expected failures, as identified by
129 131 substrings of the error message Mercurial emits."""
130 132 try:
131 133 yield
132 134 except subprocess.CalledProcessError as e:
133 135 if not any(a in e.output for a in args):
134 136 note(e.output)
135 137 raise
136 138
137 139
138 140 reponames = st.text("abcdefghijklmnopqrstuvwxyz01234556789", min_size=1).map(
139 141 lambda s: s.encode('ascii')
140 142 )
141 143
142 144
143 145 class verifyingstatemachine(RuleBasedStateMachine):
144 146 """This defines the set of acceptable operations on a Mercurial repository
145 147 using Hypothesis's RuleBasedStateMachine.
146 148
147 149 The general concept is that we manage multiple repositories inside a
148 150 repos/ directory in our temporary test location. Some of these are freshly
149 151 inited, some are clones of the others. Our current working directory is
150 152 always inside one of these repositories while the tests are running.
151 153
152 154 Hypothesis then performs a series of operations against these repositories,
153 155 including hg commands, generating contents and editing the .hgrc file.
154 156 If these operations fail in unexpected ways or behave differently in
155 157 different configurations of Mercurial, the test will fail and a minimized
156 158 .t test file will be written to the hypothesis-generated directory to
157 159 exhibit that failure.
158 160
159 161 Operations are defined as methods with @rule() decorators. See the
160 162 Hypothesis documentation at
161 163 http://hypothesis.readthedocs.org/en/release/stateful.html for more
162 164 details."""
163 165
164 166 # A bundle is a reusable collection of previously generated data which may
165 167 # be provided as arguments to future operations.
166 168 repos = Bundle('repos')
167 169 paths = Bundle('paths')
168 170 contents = Bundle('contents')
169 171 branches = Bundle('branches')
170 172 committimes = Bundle('committimes')
171 173
172 174 def __init__(self):
173 175 super(verifyingstatemachine, self).__init__()
174 176 self.repodir = os.path.join(testtmp, "repos")
175 177 if os.path.exists(self.repodir):
176 178 shutil.rmtree(self.repodir)
177 179 os.chdir(testtmp)
178 180 self.log = []
179 181 self.failed = False
180 182 self.configperrepo = {}
181 183 self.all_extensions = set()
182 184 self.non_skippable_extensions = set()
183 185
184 186 self.mkdirp("repos")
185 187 self.cd("repos")
186 188 self.mkdirp("repo1")
187 189 self.cd("repo1")
188 190 self.hg("init")
189 191
190 192 def teardown(self):
191 193 """On teardown we clean up after ourselves as usual, but we also
192 194 do some additional testing: We generate a .t file based on our test
193 195 run using run-test.py -i to get the correct output.
194 196
195 197 We then test it in a number of other configurations, verifying that
196 198 each passes the same test."""
197 199 super(verifyingstatemachine, self).teardown()
198 200 try:
199 201 shutil.rmtree(self.repodir)
200 202 except OSError:
201 203 pass
202 204 ttest = os.linesep.join(" " + l for l in self.log)
203 205 os.chdir(testtmp)
204 206 path = os.path.join(testtmp, "test-generated.t")
205 207 with open(path, 'w') as o:
206 208 o.write(ttest + os.linesep)
207 209 with open(os.devnull, "w") as devnull:
208 210 rewriter = subprocess.Popen(
209 211 [runtests, "--local", "-i", path],
210 212 stdin=subprocess.PIPE,
211 213 stdout=devnull,
212 214 stderr=devnull,
213 215 )
214 216 rewriter.communicate("yes")
215 217 with open(path, 'r') as i:
216 218 ttest = i.read()
217 219
218 220 e = None
219 221 if not self.failed:
220 222 try:
221 223 output = subprocess.check_output(
222 224 [runtests, path, "--local", "--pure"],
223 225 stderr=subprocess.STDOUT,
224 226 )
225 227 assert "Ran 1 test" in output, output
226 228 for ext in self.all_extensions - self.non_skippable_extensions:
227 229 tf = os.path.join(
228 230 testtmp, "test-generated-no-%s.t" % (ext,)
229 231 )
230 232 with open(tf, 'w') as o:
231 233 for l in ttest.splitlines():
232 234 if l.startswith(" $ hg"):
233 235 l = l.replace(
234 236 "--config %s=" % (extensionconfigkey(ext),),
235 237 "",
236 238 )
237 239 o.write(l + os.linesep)
238 240 with open(tf, 'r') as r:
239 241 t = r.read()
240 242 assert ext not in t, t
241 243 output = subprocess.check_output(
242 244 [
243 245 runtests,
244 246 tf,
245 247 "--local",
246 248 ],
247 249 stderr=subprocess.STDOUT,
248 250 )
249 251 assert "Ran 1 test" in output, output
250 252 except subprocess.CalledProcessError as e:
251 253 note(e.output)
252 254 if self.failed or e is not None:
253 255 with open(savefile, "wb") as o:
254 256 o.write(ttest)
255 257 if e is not None:
256 258 raise e
257 259
258 260 def execute_step(self, step):
259 261 try:
260 262 return super(verifyingstatemachine, self).execute_step(step)
261 263 except (HypothesisException, KeyboardInterrupt):
262 264 raise
263 265 except Exception:
264 266 self.failed = True
265 267 raise
266 268
267 269 # Section: Basic commands.
268 270 def mkdirp(self, path):
269 271 if os.path.exists(path):
270 272 return
271 273 self.log.append(
272 274 "$ mkdir -p -- %s" % (pipes.quote(os.path.relpath(path)),)
273 275 )
274 276 os.makedirs(path)
275 277
276 278 def cd(self, path):
277 279 path = os.path.relpath(path)
278 280 if path == ".":
279 281 return
280 282 os.chdir(path)
281 283 self.log.append("$ cd -- %s" % (pipes.quote(path),))
282 284
283 285 def hg(self, *args):
284 286 extra_flags = []
285 287 for key, value in self.config.items():
286 288 extra_flags.append("--config")
287 289 extra_flags.append("%s=%s" % (key, value))
288 290 self.command("hg", *(tuple(extra_flags) + args))
289 291
290 292 def command(self, *args):
291 293 self.log.append("$ " + ' '.join(map(pipes.quote, args)))
292 294 subprocess.check_output(args, stderr=subprocess.STDOUT)
293 295
294 296 # Section: Set up basic data
295 297 # This section has no side effects but generates data that we will want
296 298 # to use later.
297 299 @rule(
298 300 target=paths,
299 301 source=st.lists(files, min_size=1).map(lambda l: os.path.join(*l)),
300 302 )
301 303 def genpath(self, source):
302 304 return source
303 305
304 306 @rule(
305 307 target=committimes,
306 308 when=datetimes(min_year=1970, max_year=2038) | st.none(),
307 309 )
308 310 def gentime(self, when):
309 311 return when
310 312
311 313 @rule(
312 314 target=contents,
313 315 content=st.one_of(
314 316 st.binary(), st.text().map(lambda x: x.encode('utf-8'))
315 317 ),
316 318 )
317 319 def gencontent(self, content):
318 320 return content
319 321
320 322 @rule(
321 323 target=branches,
322 324 name=safetext,
323 325 )
324 326 def genbranch(self, name):
325 327 return name
326 328
327 329 @rule(target=paths, source=paths)
328 330 def lowerpath(self, source):
329 331 return source.lower()
330 332
331 333 @rule(target=paths, source=paths)
332 334 def upperpath(self, source):
333 335 return source.upper()
334 336
335 337 # Section: Basic path operations
336 338 @rule(path=paths, content=contents)
337 339 def writecontent(self, path, content):
338 340 self.unadded_changes = True
339 341 if os.path.isdir(path):
340 342 return
341 343 parent = os.path.dirname(path)
342 344 if parent:
343 345 try:
344 346 self.mkdirp(parent)
345 347 except OSError:
346 348 # It may be the case that there is a regular file that has
347 349 # previously been created that has the same name as an ancestor
348 350 # of the current path. This will cause mkdirp to fail with this
349 351 # error. We just turn this into a no-op in that case.
350 352 return
351 353 with open(path, 'wb') as o:
352 354 o.write(content)
353 355 self.log.append(
354 356 (
355 "$ python -c 'import binascii; "
357 "$ $PYTHON -c 'import binascii; "
356 358 "print(binascii.unhexlify(\"%s\"))' > %s"
357 359 )
358 360 % (
359 361 binascii.hexlify(content),
360 362 pipes.quote(path),
361 363 )
362 364 )
363 365
364 366 @rule(path=paths)
365 367 def addpath(self, path):
366 368 if os.path.exists(path):
367 369 self.hg("add", "--", path)
368 370
369 371 @rule(path=paths)
370 372 def forgetpath(self, path):
371 373 if os.path.exists(path):
372 374 with acceptableerrors(
373 375 "file is already untracked",
374 376 ):
375 377 self.hg("forget", "--", path)
376 378
377 379 @rule(s=st.none() | st.integers(0, 100))
378 380 def addremove(self, s):
379 381 args = ["addremove"]
380 382 if s is not None:
381 383 args.extend(["-s", str(s)])
382 384 self.hg(*args)
383 385
384 386 @rule(path=paths)
385 387 def removepath(self, path):
386 388 if os.path.exists(path):
387 389 with acceptableerrors(
388 390 'file is untracked',
389 391 'file has been marked for add',
390 392 'file is modified',
391 393 ):
392 394 self.hg("remove", "--", path)
393 395
394 396 @rule(
395 397 message=safetext,
396 398 amend=st.booleans(),
397 399 when=committimes,
398 400 addremove=st.booleans(),
399 401 secret=st.booleans(),
400 402 close_branch=st.booleans(),
401 403 )
402 404 def maybecommit(
403 405 self, message, amend, when, addremove, secret, close_branch
404 406 ):
405 407 command = ["commit"]
406 408 errors = ["nothing changed"]
407 409 if amend:
408 410 errors.append("cannot amend public changesets")
409 411 command.append("--amend")
410 412 command.append("-m" + pipes.quote(message))
411 413 if secret:
412 414 command.append("--secret")
413 415 if close_branch:
414 416 command.append("--close-branch")
415 417 errors.append("can only close branch heads")
416 418 if addremove:
417 419 command.append("--addremove")
418 420 if when is not None:
419 421 if when.year == 1970:
420 422 errors.append('negative date value')
421 423 if when.year == 2038:
422 424 errors.append('exceeds 32 bits')
423 425 command.append(
424 426 "--date=%s" % (when.strftime('%Y-%m-%d %H:%M:%S %z'),)
425 427 )
426 428
427 429 with acceptableerrors(*errors):
428 430 self.hg(*command)
429 431
430 432 # Section: Repository management
431 433 @property
432 434 def currentrepo(self):
433 435 return os.path.basename(os.getcwd())
434 436
435 437 @property
436 438 def config(self):
437 439 return self.configperrepo.setdefault(self.currentrepo, {})
438 440
439 441 @rule(
440 442 target=repos,
441 443 source=repos,
442 444 name=reponames,
443 445 )
444 446 def clone(self, source, name):
445 447 if not os.path.exists(os.path.join("..", name)):
446 448 self.cd("..")
447 449 self.hg("clone", source, name)
448 450 self.cd(name)
449 451 return name
450 452
451 453 @rule(
452 454 target=repos,
453 455 name=reponames,
454 456 )
455 457 def fresh(self, name):
456 458 if not os.path.exists(os.path.join("..", name)):
457 459 self.cd("..")
458 460 self.mkdirp(name)
459 461 self.cd(name)
460 462 self.hg("init")
461 463 return name
462 464
463 465 @rule(name=repos)
464 466 def switch(self, name):
465 467 self.cd(os.path.join("..", name))
466 468 assert self.currentrepo == name
467 469 assert os.path.exists(".hg")
468 470
469 471 @rule(target=repos)
470 472 def origin(self):
471 473 return "repo1"
472 474
473 475 @rule()
474 476 def pull(self, repo=repos):
475 477 with acceptableerrors(
476 478 "repository default not found",
477 479 "repository is unrelated",
478 480 ):
479 481 self.hg("pull")
480 482
481 483 @rule(newbranch=st.booleans())
482 484 def push(self, newbranch):
483 485 with acceptableerrors(
484 486 "default repository not configured",
485 487 "no changes found",
486 488 ):
487 489 if newbranch:
488 490 self.hg("push", "--new-branch")
489 491 else:
490 492 with acceptableerrors("creates new branches"):
491 493 self.hg("push")
492 494
493 495 # Section: Simple side effect free "check" operations
494 496 @rule()
495 497 def log(self):
496 498 self.hg("log")
497 499
498 500 @rule()
499 501 def verify(self):
500 502 self.hg("verify")
501 503
502 504 @rule()
503 505 def diff(self):
504 506 self.hg("diff", "--nodates")
505 507
506 508 @rule()
507 509 def status(self):
508 510 self.hg("status")
509 511
510 512 @rule()
511 513 def export(self):
512 514 self.hg("export")
513 515
514 516 # Section: Branch management
515 517 @rule()
516 518 def checkbranch(self):
517 519 self.hg("branch")
518 520
519 521 @rule(branch=branches)
520 522 def switchbranch(self, branch):
521 523 with acceptableerrors(
522 524 'cannot use an integer as a name',
523 525 'cannot be used in a name',
524 526 'a branch of the same name already exists',
525 527 'is reserved',
526 528 ):
527 529 self.hg("branch", "--", branch)
528 530
529 531 @rule(branch=branches, clean=st.booleans())
530 532 def update(self, branch, clean):
531 533 with acceptableerrors(
532 534 'unknown revision',
533 535 'parse error',
534 536 ):
535 537 if clean:
536 538 self.hg("update", "-C", "--", branch)
537 539 else:
538 540 self.hg("update", "--", branch)
539 541
540 542 # Section: Extension management
541 543 def hasextension(self, extension):
542 544 return extensionconfigkey(extension) in self.config
543 545
544 546 def commandused(self, extension):
545 547 assert extension in self.all_extensions
546 548 self.non_skippable_extensions.add(extension)
547 549
548 550 @rule(extension=extensions)
549 551 def addextension(self, extension):
550 552 self.all_extensions.add(extension)
551 553 self.config[extensionconfigkey(extension)] = ""
552 554
553 555 @rule(extension=extensions)
554 556 def removeextension(self, extension):
555 557 self.config.pop(extensionconfigkey(extension), None)
556 558
557 559 # Section: Commands from the shelve extension
558 560 @rule()
559 561 @precondition(lambda self: self.hasextension("shelve"))
560 562 def shelve(self):
561 563 self.commandused("shelve")
562 564 with acceptableerrors("nothing changed"):
563 565 self.hg("shelve")
564 566
565 567 @rule()
566 568 @precondition(lambda self: self.hasextension("shelve"))
567 569 def unshelve(self):
568 570 self.commandused("shelve")
569 571 with acceptableerrors("no shelved changes to apply"):
570 572 self.hg("unshelve")
571 573
572 574
573 575 class writeonlydatabase(ExampleDatabase):
574 576 def __init__(self, underlying):
575 577 super(ExampleDatabase, self).__init__()
576 578 self.underlying = underlying
577 579
578 580 def fetch(self, key):
579 581 return ()
580 582
581 583 def save(self, key, value):
582 584 self.underlying.save(key, value)
583 585
584 586 def delete(self, key, value):
585 587 self.underlying.delete(key, value)
586 588
587 589 def close(self):
588 590 self.underlying.close()
589 591
590 592
591 593 def extensionconfigkey(extension):
592 594 return "extensions." + extension
593 595
594 596
595 597 settings.register_profile(
596 598 'default',
597 599 settings(
598 600 timeout=300,
599 601 stateful_step_count=50,
600 602 max_examples=10,
601 603 ),
602 604 )
603 605
604 606 settings.register_profile(
605 607 'fast',
606 608 settings(
607 609 timeout=10,
608 610 stateful_step_count=20,
609 611 max_examples=5,
610 612 min_satisfying_examples=1,
611 613 max_shrinks=0,
612 614 ),
613 615 )
614 616
615 617 settings.register_profile(
616 618 'continuous',
617 619 settings(
618 620 timeout=-1,
619 621 stateful_step_count=1000,
620 622 max_examples=10 ** 8,
621 623 max_iterations=10 ** 8,
622 624 database=writeonlydatabase(settings.default.database),
623 625 ),
624 626 )
625 627
626 628 settings.load_profile(os.getenv('HYPOTHESIS_PROFILE', 'default'))
627 629
628 630 verifyingtest = verifyingstatemachine.TestCase
629 631
630 632 verifyingtest.settings = settings.default
631 633
632 634 if __name__ == '__main__':
633 635 try:
634 636 silenttestrunner.main(__name__)
635 637 finally:
636 638 # So as to prevent proliferation of useless test files, if we never
637 639 # actually wrote a failing test we clean up after ourselves and delete
638 640 # the file for doing so that we owned.
639 641 if os.path.exists(savefile) and os.path.getsize(savefile) == 0:
640 642 os.unlink(savefile)
General Comments 0
You need to be logged in to leave comments. Login now