##// END OF EJS Templates
branching: merge stable into default
Raphaël Gomès -
r50139:cd51d495 merge default
parent child Browse files
Show More
@@ -1,229 +1,230 b''
1 1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
17 17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
18 18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
19 19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
20 20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
21 21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
22 22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
23 23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
24 24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
25 25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
26 26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
27 27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
28 28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
29 29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
30 30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
31 31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
32 32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
33 33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
34 34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
35 35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
36 36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
37 37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
38 38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
39 39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
40 40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
41 41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
42 42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
43 43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
44 44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
45 45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
46 46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
47 47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
48 48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
49 49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
50 50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
51 51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
52 52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
53 53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
54 54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
55 55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
56 56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
57 57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
58 58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
59 59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
60 60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
61 61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
62 62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
63 63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
64 64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
65 65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
66 66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
67 67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
68 68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
69 69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
70 70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
71 71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
72 72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
73 73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
74 74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
75 75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
76 76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
77 77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
78 78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
79 79 335a558f81dc73afeab4d7be63617392b130117f 0 iQIVAwUAUiZrIyBXgaxoKi1yAQK2iw//cquNqqSkc8Re5/TZT9I6NH+lh6DbOKjJP0Xl1Wqq0K+KSIUgZG4G32ovaEb2l5X0uY+3unRPiZ0ebl0YSw4Fb2ZiPIADXLBTOYRrY2Wwd3tpJeGI6wEgZt3SfcITV/g7NJrCjT3FlYoSOIayrExM80InSdcEM0Q3Rx6HKzY2acyxzgZeAtAW5ohFvHilSvY6p5Gcm4+QptMxvw45GPdreUmjeXZxNXNXZ8P+MjMz/QJbai/N7PjmK8lqnhkBsT48Ng/KhhmOkGntNJ2/ImBWLFGcWngSvJ7sfWwnyhndvGhe0Hq1NcCf7I8TjNDxU5TR+m+uW7xjXdLoDbUjBdX4sKXnh8ZjbYiODKBOrrDq25cf8nA/tnpKyE/qsVy60kOk6loY4XKiYmn1V49Ta0emmDx0hqo3HgxHHsHX0NDnGdWGol7cPRET0RzVobKq1A0jnrhPooWidvLh9bPzLonrWDo+ib+DuySoRkuYUK4pgZJ2mbg6daFOBEZygkSyRB8bo1UQUP7EgQDrWe4khb/5GHEfDkrQz3qu/sXvc0Ir1mOUWBFPHC2DjjCn/oMJuUkG1SwM8l2Bfv7h67ssES6YQ2+RjOix4yid7EXS/Ogl45PzCIPSI5+BbNs10JhE0w5uErBHlF53EDTe/TSLc+GU6DB6PP6dH912Njdr3jpNSUQ=
80 80 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
@@ -1,242 +1,243 b''
1 1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
2 2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
3 3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
4 4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
5 5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
6 6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
7 7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
8 8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
9 9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
10 10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
11 11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
15 15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
16 16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
17 17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
18 18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
19 19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
20 20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
21 21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
22 22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
23 23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
24 24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
25 25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
26 26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
27 27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
28 28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
29 29 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4
30 30 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1
31 31 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2
32 32 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3
33 33 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5
34 34 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1
35 35 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2
36 36 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3
37 37 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4
38 38 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6
39 39 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1
40 40 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2
41 41 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3
42 42 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4
43 43 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7
44 44 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1
45 45 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2
46 46 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3
47 47 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4
48 48 2b2155623ee2559caf288fd333f30475966c4525 1.7.5
49 49 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8
50 50 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1
51 51 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2
52 52 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
53 53 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
54 54 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
55 55 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
56 56 d629f1e89021103f1753addcef6b310e4435b184 1.9.2
57 57 351a9292e430e35766c552066ed3e87c557b803b 1.9.3
58 58 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc
59 59 41453d55b481ddfcc1dacb445179649e24ca861d 2.0
60 60 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1
61 61 6344043924497cd06d781d9014c66802285072e4 2.0.2
62 62 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc
63 63 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1
64 64 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1
65 65 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2
66 66 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc
67 67 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2
68 68 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1
69 69 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2
70 70 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3
71 71 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc
72 72 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3
73 73 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1
74 74 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2
75 75 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc
76 76 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4
77 77 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1
78 78 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2
79 79 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc
80 80 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
81 81 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
82 82 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
83 83 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
84 84 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
85 85 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc
86 86 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6
87 87 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1
88 88 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2
89 89 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3
90 90 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc
91 91 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7
92 92 335a558f81dc73afeab4d7be63617392b130117f 2.7.1
93 93 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2
94 94 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc
95 95 d825e4025e39d1c39db943cdc89818abd0a87c27 2.8
96 96 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1
97 97 ca387377df7a3a67dbb90b6336b781cdadc3ef41 2.8.2
98 98 8862469e16f9236208581b20de5f96bd13cc039d 2.9-rc
99 99 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 2.9
100 100 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 2.9.1
101 101 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 2.9.2
102 102 564f55b251224f16508dd1311452db7780dafe2b 3.0-rc
103 103 2195ac506c6ababe86985b932f4948837c0891b5 3.0
104 104 269c80ee5b3cb3684fa8edc61501b3506d02eb10 3.0.1
105 105 2d8cd3d0e83c7336c0cb45a9f88638363f993848 3.0.2
106 106 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 3.1-rc
107 107 3178e49892020336491cdc6945885c4de26ffa8b 3.1
108 108 5dc91146f35369949ea56b40172308158b59063a 3.1.1
109 109 f768c888aaa68d12dd7f509dcc7f01c9584357d0 3.1.2
110 110 7f8d16af8cae246fa5a48e723d48d58b015aed94 3.2-rc
111 111 ced632394371a36953ce4d394f86278ae51a2aae 3.2
112 112 643c58303fb0ec020907af28b9e486be299ba043 3.2.1
113 113 902554884335e5ca3661d63be9978eb4aec3f68a 3.2.2
114 114 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 3.2.3
115 115 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 3.2.4
116 116 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 3.3-rc
117 117 fbdd5195528fae4f41feebc1838215c110b25d6a 3.3
118 118 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 3.3.1
119 119 07a92bbd02e5e3a625e0820389b47786b02b2cea 3.3.2
120 120 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 3.3.3
121 121 e89f909edffad558b56f4affa8239e4832f88de0 3.4-rc
122 122 8cc6036bca532e06681c5a8fa37efaa812de67b5 3.4
123 123 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 3.4.1
124 124 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 3.4.2
125 125 96a38d44ba093bd1d1ecfd34119e94056030278b 3.5-rc
126 126 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 3.5
127 127 1a45e49a6bed023deb229102a8903234d18054d3 3.5.1
128 128 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 3.5.2
129 129 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 3.6-rc
130 130 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 3.6
131 131 1aa5083cbebbe7575c88f3402ab377539b484897 3.6.1
132 132 2d437a0f3355834a9485bbbeb30a52a052c98f19 3.6.2
133 133 ea389970c08449440587712117f178d33bab3f1e 3.6.3
134 134 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc
135 135 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7
136 136 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1
137 137 d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2
138 138 ae279d4a19e9683214cbd1fe8298cf0b50571432 3.7.3
139 139 740156eedf2c450aee58b1a90b0e826f47c5da64 3.8-rc
140 140 f85de28eae32e7d3064b1a1321309071bbaaa069 3.8
141 141 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
142 142 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
143 143 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
144 144 26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
145 145 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 3.9-rc
146 146 299546f84e68dbb9bd026f0f3a974ce4bdb93686 3.9
147 147 ccd436f7db6d5d7b9af89715179b911d031d44f1 3.9.1
148 148 149433e68974eb5c63ccb03f794d8b57339a80c4 3.9.2
149 149 438173c415874f6ac653efc1099dec9c9150e90f 4.0-rc
150 150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
151 151 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 4.0.1
152 152 e69874dc1f4e142746ff3df91e678a09c6fc208c 4.0.2
153 153 a1dd2c0c479e0550040542e392e87bc91262517e 4.1-rc
154 154 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 4.1
155 155 25703b624d27e3917d978af56d6ad59331e0464a 4.1.1
156 156 ed5b25874d998ababb181a939dd37a16ea644435 4.1.2
157 157 77eaf9539499a1b8be259ffe7ada787d07857f80 4.1.3
158 158 616e788321cc4ae9975b7f0c54c849f36d82182b 4.2-rc
159 159 bb96d4a497432722623ae60d9bc734a1e360179e 4.2
160 160 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
161 161 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
162 162 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
163 163 5544af8622863796a0027566f6b646e10d522c4c 4.3
164 164 943c91326b23954e6e1c6960d0239511f9530258 4.2.3
165 165 3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
166 166 920977f72c7b70acfdaf56ab35360584d7845827 4.3.2
167 167 2f427b57bf9019c6dc3750baa539dc22c1be50f6 4.3.3
168 168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
169 169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
170 170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
171 171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
172 172 27b6df1b5adbdf647cf5c6675b40575e1b197c60 4.5-rc
173 173 d334afc585e29577f271c5eda03378736a16ca6b 4.5
174 174 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1
175 175 8bba684efde7f45add05f737952093bb2aa07155 4.5.2
176 176 7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3
177 177 ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0
178 178 1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1
179 179 6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6
180 180 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 4.6.1
181 181 0b63a6743010dfdbf8a8154186e119949bdaa1cc 4.6.2
182 182 e90130af47ce8dd53a3109aed9d15876b3e7dee8 4.7rc0
183 183 33ac6a72308a215e6086fbced347ec10aa963b0a 4.7
184 184 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 4.7.1
185 185 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
186 186 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
187 187 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
188 188 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
189 189 197f092b2cd9691e2a55d198f717b231af9be6f9 4.8.2
190 190 593718ff5844cad7a27ee3eb5adad89ac8550949 4.9rc0
191 191 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 4.9
192 192 4ea21df312ec7159c5b3633096b6ecf68750b0dd 4.9.1
193 193 4a8d9ed864754837a185a642170cde24392f9abf 5.0rc0
194 194 07e479ef7c9639be0029f00e6a722b96dcc05fee 5.0
195 195 c3484ddbdb9621256d597ed86b90d229c59c2af9 5.0.1
196 196 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 5.0.2
197 197 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 5.1rc0
198 198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
199 199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
200 200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
201 201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
202 202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
203 203 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 5.2.1
204 204 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 5.2.2
205 205 84a0102c05c7852c8215ef6cf21d809927586b69 5.3rc0
206 206 e4344e463c0c888a2f437b78b5982ecdf3f6650a 5.3rc1
207 207 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 5.3
208 208 6d121acbb82e65fe4dd3c2318a1b61981b958492 5.3.1
209 209 8fca7e8449a847e3cf1054f2c07b51237699fad3 5.3.2
210 210 26ce8e7515036d3431a03aaeb7bc72dd96cb1112 5.4rc0
211 211 cf3e07d7648a4371ce584d15dd692e7a6845792f 5.4
212 212 065704cbdbdbb05dcd6bb814eb9bbdd982211b28 5.4.1
213 213 0ea9c86fac8974cd74dc12ea681c8986eb6da6c4 5.4.2
214 214 28163c5de797e5416f9b588940f4608269b4d50a 5.5rc0
215 215 7fc3c5fbc65f6fe85d70ea63923b8767dda4f2e0 5.5
216 216 f62bb5d07848ca598aa860a517394130b61bf2ee 5.5.1
217 217 07731064ac41dacdf0ec869ebd05c2e848c14fbf 5.5.2
218 218 0e06a7ab9e0d5c65af4e511aee1e0342998799df 5.6rc0
219 219 18c17d63fdabd009e70bf994e5efb7db422f4f7f 5.6
220 220 1d5189a57405ceca5aa244052c9f948977f4699b 5.6.1
221 221 9da65e3cf3706ff41e08b311381c588440c27baf 5.7rc0
222 222 0e2e7300f4302b02412b0b734717697049494c4c 5.7
223 223 d5d9177c0045d206db575bae6daa98e2cb2fe5bc 5.7.1
224 224 f67b8946bb1b6cfa8328dbf8d6a9128b69ccdcb4 5.8rc0
225 225 8d2b62d716b095507effaa8d56f87cd27ba659ab 5.8rc1
226 226 067f2c53fb24506c9e9fb4639871b13b19a85f8a 5.8
227 227 411dc27fd9fd076d6a031a08fcaace659afe2fe3 5.8.1
228 228 d7515d29761d5ada7d9c765f517db67db75dea9a 5.9rc0
229 229 2813d406b03607cdb8c06cb04c44efcc9a79d9a2 5.9rc1
230 230 53221078e0de65d1a821ce5311dec45a7a978301 5.9
231 231 86a60679cf619e14cee9442f865fcf31b142cb9f 5.9.1
232 232 750920b18aaaddd654756be40dec59d90f2643be 5.9.2
233 233 6ee0244fc1cf889ae543d2ce0ec45201ae0be6e1 5.9.3
234 234 a44bb185f6bdbecc754996d8386722e2f0123b0a 6.0rc0
235 235 5d08b289e2e526259d7d5ea32b70fe76d5b327d7 6.0
236 236 799fdf4cca80cb9ae40537a90995e6bd163ebc0b 6.0.1
237 237 75676122c2bf7594ac732b7388db4c74c648b365 6.0.2
238 238 dcec16e799ddb6d33fcd11b04af530250a417a58 6.0.3
239 239 c00d3ce4e94bb0ee8d809e25e1dcb2a5fab84e2c 6.1rc0
240 240 d4486810a1795fba9521449b8885ced034f3a6dd 6.1
241 241 5bd6bcd31dd1ebb63b8914b00064f96297267af7 6.1.1
242 242 0ddd5e1f5f67438af85d12e4ce6c39021dde9916 6.1.2
243 6b10151b962108f65bfa12b3918b1021ca334f73 6.1.3
@@ -1,154 +1,160 b''
1 1 # narrowwirepeer.py - passes narrow spec with unbundle command
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 from mercurial import (
10 10 bundle2,
11 11 error,
12 exchange,
12 13 extensions,
13 14 hg,
14 15 narrowspec,
15 16 wireprototypes,
16 17 wireprotov1peer,
17 18 wireprotov1server,
18 19 )
19 20
20 21 from . import narrowbundle2
21 22
22 23
23 24 def uisetup():
24 25 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
25 26
26 27
27 28 def reposetup(repo):
28 29 def wirereposetup(ui, peer):
29 30 def wrapped(orig, cmd, *args, **kwargs):
30 31 if cmd == b'unbundle':
31 32 # TODO: don't blindly add include/exclude wireproto
32 33 # arguments to unbundle.
33 34 include, exclude = repo.narrowpats
34 35 kwargs["includepats"] = b','.join(include)
35 36 kwargs["excludepats"] = b','.join(exclude)
36 37 return orig(cmd, *args, **kwargs)
37 38
38 39 extensions.wrapfunction(peer, b'_calltwowaystream', wrapped)
39 40
40 41 hg.wirepeersetupfuncs.append(wirereposetup)
41 42
42 43
43 44 @wireprotov1server.wireprotocommand(
44 45 b'narrow_widen',
45 46 b'oldincludes oldexcludes'
46 47 b' newincludes newexcludes'
47 48 b' commonheads cgversion'
48 49 b' known ellipses',
49 50 permission=b'pull',
50 51 )
51 52 def narrow_widen(
52 53 repo,
53 54 proto,
54 55 oldincludes,
55 56 oldexcludes,
56 57 newincludes,
57 58 newexcludes,
58 59 commonheads,
59 60 cgversion,
60 61 known,
61 62 ellipses,
62 63 ):
63 64 """wireprotocol command to send data when a narrow clone is widen. We will
64 65 be sending a changegroup here.
65 66
66 67 The current set of arguments which are required:
67 68 oldincludes: the old includes of the narrow copy
68 69 oldexcludes: the old excludes of the narrow copy
69 70 newincludes: the new includes of the narrow copy
70 71 newexcludes: the new excludes of the narrow copy
71 72 commonheads: list of heads which are common between the server and client
72 73 cgversion(maybe): the changegroup version to produce
73 74 known: list of nodes which are known on the client (used in ellipses cases)
74 75 ellipses: whether to send ellipses data or not
75 76 """
76 77
77 78 preferuncompressed = False
78 79 try:
79 80
80 81 def splitpaths(data):
81 82 # work around ''.split(',') => ['']
82 83 return data.split(b',') if data else []
83 84
84 85 oldincludes = splitpaths(oldincludes)
85 86 newincludes = splitpaths(newincludes)
86 87 oldexcludes = splitpaths(oldexcludes)
87 88 newexcludes = splitpaths(newexcludes)
89
90 # enforce narrow acl if set
91 if repo.ui.has_section(exchange._NARROWACL_SECTION):
92 exchange.applynarrowacl(repo, {'includepats': newincludes})
93
88 94 # validate the patterns
89 95 narrowspec.validatepatterns(set(oldincludes))
90 96 narrowspec.validatepatterns(set(newincludes))
91 97 narrowspec.validatepatterns(set(oldexcludes))
92 98 narrowspec.validatepatterns(set(newexcludes))
93 99
94 100 common = wireprototypes.decodelist(commonheads)
95 101 known = wireprototypes.decodelist(known)
96 102 if ellipses == b'0':
97 103 ellipses = False
98 104 else:
99 105 ellipses = bool(ellipses)
100 106 cgversion = cgversion
101 107
102 108 bundler = bundle2.bundle20(repo.ui)
103 109 newmatch = narrowspec.match(
104 110 repo.root, include=newincludes, exclude=newexcludes
105 111 )
106 112 oldmatch = narrowspec.match(
107 113 repo.root, include=oldincludes, exclude=oldexcludes
108 114 )
109 115 if not ellipses:
110 116 bundle2.widen_bundle(
111 117 bundler,
112 118 repo,
113 119 oldmatch,
114 120 newmatch,
115 121 common,
116 122 known,
117 123 cgversion,
118 124 ellipses,
119 125 )
120 126 else:
121 127 narrowbundle2.generate_ellipses_bundle2_for_widening(
122 128 bundler,
123 129 repo,
124 130 oldmatch,
125 131 newmatch,
126 132 cgversion,
127 133 common,
128 134 known,
129 135 )
130 136 except error.Abort as exc:
131 137 bundler = bundle2.bundle20(repo.ui)
132 138 manargs = [(b'message', exc.message)]
133 139 advargs = []
134 140 if exc.hint is not None:
135 141 advargs.append((b'hint', exc.hint))
136 142 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
137 143 preferuncompressed = True
138 144
139 145 chunks = bundler.getchunks()
140 146 return wireprototypes.streamres(
141 147 gen=chunks, prefer_uncompressed=preferuncompressed
142 148 )
143 149
144 150
145 151 def peernarrowwiden(remote, **kwargs):
146 152 for ch in ('commonheads', 'known'):
147 153 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
148 154
149 155 for ch in ('oldincludes', 'newincludes', 'oldexcludes', 'newexcludes'):
150 156 kwargs[ch] = b','.join(kwargs[ch])
151 157
152 158 kwargs['ellipses'] = b'%i' % bool(kwargs['ellipses'])
153 159 f = remote._callcompressable(b'narrow_widen', **kwargs)
154 160 return bundle2.getunbundler(remote.ui, f)
@@ -1,749 +1,749 b''
1 1 # chgserver.py - command server extension for cHg
2 2 #
3 3 # Copyright 2011 Yuya Nishihara <yuya@tcha.org>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """command server extension for cHg
9 9
10 10 'S' channel (read/write)
11 11 propagate ui.system() request to client
12 12
13 13 'attachio' command
14 14 attach client's stdio passed by sendmsg()
15 15
16 16 'chdir' command
17 17 change current directory
18 18
19 19 'setenv' command
20 20 replace os.environ completely
21 21
22 22 'setumask' command (DEPRECATED)
23 23 'setumask2' command
24 24 set umask
25 25
26 26 'validate' command
27 27 reload the config and check if the server is up to date
28 28
29 29 Config
30 30 ------
31 31
32 32 ::
33 33
34 34 [chgserver]
35 35 # how long (in seconds) should an idle chg server exit
36 36 idletimeout = 3600
37 37
38 38 # whether to skip config or env change checks
39 39 skiphash = False
40 40 """
41 41
42 42
43 43 import inspect
44 44 import os
45 45 import re
46 46 import socket
47 47 import stat
48 48 import struct
49 49 import time
50 50
51 51 from .i18n import _
52 52 from .pycompat import (
53 53 getattr,
54 54 setattr,
55 55 )
56 56 from .node import hex
57 57
58 58 from . import (
59 59 commandserver,
60 60 encoding,
61 61 error,
62 62 extensions,
63 63 pycompat,
64 64 util,
65 65 )
66 66
67 67 from .utils import (
68 68 hashutil,
69 69 procutil,
70 70 stringutil,
71 71 )
72 72
73 73
74 74 def _hashlist(items):
75 75 """return sha1 hexdigest for a list"""
76 76 return hex(hashutil.sha1(stringutil.pprint(items)).digest())
77 77
78 78
79 79 # sensitive config sections affecting confighash
80 80 _configsections = [
81 81 b'alias', # affects global state commands.table
82 82 b'diff-tools', # affects whether gui or not in extdiff's uisetup
83 83 b'eol', # uses setconfig('eol', ...)
84 84 b'extdiff', # uisetup will register new commands
85 85 b'extensions',
86 86 b'fastannotate', # affects annotate command and adds fastannonate cmd
87 87 b'merge-tools', # affects whether gui or not in extdiff's uisetup
88 88 b'schemes', # extsetup will update global hg.schemes
89 89 ]
90 90
91 91 _configsectionitems = [
92 92 (b'commands', b'show.aliasprefix'), # show.py reads it in extsetup
93 93 ]
94 94
95 95 # sensitive environment variables affecting confighash
96 96 _envre = re.compile(
97 97 br'''\A(?:
98 98 CHGHG
99 99 |HG(?:DEMANDIMPORT|EMITWARNINGS|MODULEPOLICY|PROF|RCPATH)?
100 100 |HG(?:ENCODING|PLAIN).*
101 101 |LANG(?:UAGE)?
102 102 |LC_.*
103 103 |LD_.*
104 104 |PATH
105 105 |PYTHON.*
106 106 |TERM(?:INFO)?
107 107 |TZ
108 108 )\Z''',
109 109 re.X,
110 110 )
111 111
112 112
113 113 def _confighash(ui):
114 114 """return a quick hash for detecting config/env changes
115 115
116 116 confighash is the hash of sensitive config items and environment variables.
117 117
118 118 for chgserver, it is designed that once confighash changes, the server is
119 119 not qualified to serve its client and should redirect the client to a new
120 120 server. different from mtimehash, confighash change will not mark the
121 121 server outdated and exit since the user can have different configs at the
122 122 same time.
123 123 """
124 124 sectionitems = []
125 125 for section in _configsections:
126 126 sectionitems.append(ui.configitems(section))
127 127 for section, item in _configsectionitems:
128 128 sectionitems.append(ui.config(section, item))
129 129 sectionhash = _hashlist(sectionitems)
130 130 # If $CHGHG is set, the change to $HG should not trigger a new chg server
131 131 if b'CHGHG' in encoding.environ:
132 132 ignored = {b'HG'}
133 133 else:
134 134 ignored = set()
135 135 envitems = [
136 136 (k, v)
137 137 for k, v in encoding.environ.items()
138 138 if _envre.match(k) and k not in ignored
139 139 ]
140 140 envhash = _hashlist(sorted(envitems))
141 141 return sectionhash[:6] + envhash[:6]
142 142
143 143
144 144 def _getmtimepaths(ui):
145 145 """get a list of paths that should be checked to detect change
146 146
147 147 The list will include:
148 148 - extensions (will not cover all files for complex extensions)
149 149 - mercurial/__version__.py
150 150 - python binary
151 151 """
152 152 modules = [m for n, m in extensions.extensions(ui)]
153 153 try:
154 154 from . import __version__
155 155
156 156 modules.append(__version__)
157 157 except ImportError:
158 158 pass
159 159 files = []
160 160 if pycompat.sysexecutable:
161 161 files.append(pycompat.sysexecutable)
162 162 for m in modules:
163 163 try:
164 164 files.append(pycompat.fsencode(inspect.getabsfile(m)))
165 165 except TypeError:
166 166 pass
167 167 return sorted(set(files))
168 168
169 169
170 170 def _mtimehash(paths):
171 171 """return a quick hash for detecting file changes
172 172
173 173 mtimehash calls stat on given paths and calculate a hash based on size and
174 174 mtime of each file. mtimehash does not read file content because reading is
175 175 expensive. therefore it's not 100% reliable for detecting content changes.
176 176 it's possible to return different hashes for same file contents.
177 177 it's also possible to return a same hash for different file contents for
178 178 some carefully crafted situation.
179 179
180 180 for chgserver, it is designed that once mtimehash changes, the server is
181 181 considered outdated immediately and should no longer provide service.
182 182
183 183 mtimehash is not included in confighash because we only know the paths of
184 184 extensions after importing them (there is imp.find_module but that faces
185 185 race conditions). We need to calculate confighash without importing.
186 186 """
187 187
188 188 def trystat(path):
189 189 try:
190 190 st = os.stat(path)
191 191 return (st[stat.ST_MTIME], st.st_size)
192 192 except OSError:
193 193 # could be ENOENT, EPERM etc. not fatal in any case
194 194 pass
195 195
196 196 return _hashlist(pycompat.maplist(trystat, paths))[:12]
197 197
198 198
199 199 class hashstate:
200 200 """a structure storing confighash, mtimehash, paths used for mtimehash"""
201 201
202 202 def __init__(self, confighash, mtimehash, mtimepaths):
203 203 self.confighash = confighash
204 204 self.mtimehash = mtimehash
205 205 self.mtimepaths = mtimepaths
206 206
207 207 @staticmethod
208 208 def fromui(ui, mtimepaths=None):
209 209 if mtimepaths is None:
210 210 mtimepaths = _getmtimepaths(ui)
211 211 confighash = _confighash(ui)
212 212 mtimehash = _mtimehash(mtimepaths)
213 213 ui.log(
214 214 b'cmdserver',
215 215 b'confighash = %s mtimehash = %s\n',
216 216 confighash,
217 217 mtimehash,
218 218 )
219 219 return hashstate(confighash, mtimehash, mtimepaths)
220 220
221 221
222 222 def _newchgui(srcui, csystem, attachio):
223 223 class chgui(srcui.__class__):
224 224 def __init__(self, src=None):
225 225 super(chgui, self).__init__(src)
226 226 if src:
227 227 self._csystem = getattr(src, '_csystem', csystem)
228 228 else:
229 229 self._csystem = csystem
230 230
231 231 def _runsystem(self, cmd, environ, cwd, out):
232 232 # fallback to the original system method if
233 233 # a. the output stream is not stdout (e.g. stderr, cStringIO),
234 234 # b. or stdout is redirected by protectfinout(),
235 235 # because the chg client is not aware of these situations and
236 236 # will behave differently (i.e. write to stdout).
237 237 if (
238 238 out is not self.fout
239 239 or not util.safehasattr(self.fout, b'fileno')
240 240 or self.fout.fileno() != procutil.stdout.fileno()
241 241 or self._finoutredirected
242 242 ):
243 243 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
244 244 self.flush()
245 245 return self._csystem(cmd, procutil.shellenviron(environ), cwd)
246 246
247 247 def _runpager(self, cmd, env=None):
248 248 self._csystem(
249 249 cmd,
250 250 procutil.shellenviron(env),
251 251 type=b'pager',
252 252 cmdtable={b'attachio': attachio},
253 253 )
254 254 return True
255 255
256 256 return chgui(srcui)
257 257
258 258
259 259 def _loadnewui(srcui, args, cdebug):
260 260 from . import dispatch # avoid cycle
261 261
262 262 newui = srcui.__class__.load()
263 263 for a in [b'fin', b'fout', b'ferr', b'environ']:
264 264 setattr(newui, a, getattr(srcui, a))
265 265 if util.safehasattr(srcui, b'_csystem'):
266 266 newui._csystem = srcui._csystem
267 267
268 268 # command line args
269 269 options = dispatch._earlyparseopts(newui, args)
270 270 dispatch._parseconfig(newui, options[b'config'])
271 271
272 272 # stolen from tortoisehg.util.copydynamicconfig()
273 273 for section, name, value in srcui.walkconfig():
274 274 source = srcui.configsource(section, name)
275 275 if b':' in source or source == b'--config' or source.startswith(b'$'):
276 276 # path:line or command line, or environ
277 277 continue
278 278 newui.setconfig(section, name, value, source)
279 279
280 280 # load wd and repo config, copied from dispatch.py
281 281 cwd = options[b'cwd']
282 282 cwd = cwd and os.path.realpath(cwd) or None
283 283 rpath = options[b'repository']
284 284 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
285 285
286 286 extensions.populateui(newui)
287 287 commandserver.setuplogging(newui, fp=cdebug)
288 288 if newui is not newlui:
289 289 extensions.populateui(newlui)
290 290 commandserver.setuplogging(newlui, fp=cdebug)
291 291
292 292 return (newui, newlui)
293 293
294 294
295 295 class channeledsystem:
296 296 """Propagate ui.system() request in the following format:
297 297
298 298 payload length (unsigned int),
299 299 type, '\0',
300 300 cmd, '\0',
301 301 cwd, '\0',
302 302 envkey, '=', val, '\0',
303 303 ...
304 304 envkey, '=', val
305 305
306 306 if type == 'system', waits for:
307 307
308 308 exitcode length (unsigned int),
309 309 exitcode (int)
310 310
311 311 if type == 'pager', repetitively waits for a command name ending with '\n'
312 312 and executes it defined by cmdtable, or exits the loop if the command name
313 313 is empty.
314 314 """
315 315
316 316 def __init__(self, in_, out, channel):
317 317 self.in_ = in_
318 318 self.out = out
319 319 self.channel = channel
320 320
321 321 def __call__(self, cmd, environ, cwd=None, type=b'system', cmdtable=None):
322 322 args = [type, cmd, util.abspath(cwd or b'.')]
323 323 args.extend(b'%s=%s' % (k, v) for k, v in environ.items())
324 324 data = b'\0'.join(args)
325 325 self.out.write(struct.pack(b'>cI', self.channel, len(data)))
326 326 self.out.write(data)
327 327 self.out.flush()
328 328
329 329 if type == b'system':
330 330 length = self.in_.read(4)
331 331 (length,) = struct.unpack(b'>I', length)
332 332 if length != 4:
333 333 raise error.Abort(_(b'invalid response'))
334 334 (rc,) = struct.unpack(b'>i', self.in_.read(4))
335 335 return rc
336 336 elif type == b'pager':
337 337 while True:
338 338 cmd = self.in_.readline()[:-1]
339 339 if not cmd:
340 340 break
341 341 if cmdtable and cmd in cmdtable:
342 342 cmdtable[cmd]()
343 343 else:
344 344 raise error.Abort(_(b'unexpected command: %s') % cmd)
345 345 else:
346 346 raise error.ProgrammingError(b'invalid S channel type: %s' % type)
347 347
348 348
349 349 _iochannels = [
350 350 # server.ch, ui.fp, mode
351 351 (b'cin', b'fin', 'rb'),
352 352 (b'cout', b'fout', 'wb'),
353 353 (b'cerr', b'ferr', 'wb'),
354 354 ]
355 355
356 356
357 357 class chgcmdserver(commandserver.server):
358 358 def __init__(
359 359 self, ui, repo, fin, fout, sock, prereposetups, hashstate, baseaddress
360 360 ):
361 361 super(chgcmdserver, self).__init__(
362 362 _newchgui(ui, channeledsystem(fin, fout, b'S'), self.attachio),
363 363 repo,
364 364 fin,
365 365 fout,
366 366 prereposetups,
367 367 )
368 368 self.clientsock = sock
369 369 self._ioattached = False
370 370 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio"
371 371 self.hashstate = hashstate
372 372 self.baseaddress = baseaddress
373 373 if hashstate is not None:
374 374 self.capabilities = self.capabilities.copy()
375 375 self.capabilities[b'validate'] = chgcmdserver.validate
376 376
377 377 def cleanup(self):
378 378 super(chgcmdserver, self).cleanup()
379 379 # dispatch._runcatch() does not flush outputs if exception is not
380 380 # handled by dispatch._dispatch()
381 381 self.ui.flush()
382 382 self._restoreio()
383 383 self._ioattached = False
384 384
385 385 def attachio(self):
386 386 """Attach to client's stdio passed via unix domain socket; all
387 387 channels except cresult will no longer be used
388 388 """
389 389 # tell client to sendmsg() with 1-byte payload, which makes it
390 390 # distinctive from "attachio\n" command consumed by client.read()
391 391 self.clientsock.sendall(struct.pack(b'>cI', b'I', 1))
392 392 clientfds = util.recvfds(self.clientsock.fileno())
393 393 self.ui.log(b'chgserver', b'received fds: %r\n', clientfds)
394 394
395 395 ui = self.ui
396 396 ui.flush()
397 397 self._saveio()
398 398 for fd, (cn, fn, mode) in zip(clientfds, _iochannels):
399 399 assert fd > 0
400 400 fp = getattr(ui, fn)
401 401 os.dup2(fd, fp.fileno())
402 402 os.close(fd)
403 403 if self._ioattached:
404 404 continue
405 405 # reset buffering mode when client is first attached. as we want
406 406 # to see output immediately on pager, the mode stays unchanged
407 407 # when client re-attached. ferr is unchanged because it should
408 408 # be unbuffered no matter if it is a tty or not.
409 409 if fn == b'ferr':
410 410 newfp = fp
411 411 else:
412 412 # On Python 3, the standard library doesn't offer line-buffered
413 413 # binary streams, so wrap/unwrap it.
414 414 if fp.isatty():
415 415 newfp = procutil.make_line_buffered(fp)
416 416 else:
417 417 newfp = procutil.unwrap_line_buffered(fp)
418 418 if newfp is not fp:
419 419 setattr(ui, fn, newfp)
420 420 setattr(self, cn, newfp)
421 421
422 422 self._ioattached = True
423 423 self.cresult.write(struct.pack(b'>i', len(clientfds)))
424 424
425 425 def _saveio(self):
426 426 if self._oldios:
427 427 return
428 428 ui = self.ui
429 429 for cn, fn, _mode in _iochannels:
430 430 ch = getattr(self, cn)
431 431 fp = getattr(ui, fn)
432 432 fd = os.dup(fp.fileno())
433 433 self._oldios.append((ch, fp, fd))
434 434
435 435 def _restoreio(self):
436 436 if not self._oldios:
437 437 return
438 438 nullfd = os.open(os.devnull, os.O_WRONLY)
439 439 ui = self.ui
440 440 for (ch, fp, fd), (cn, fn, mode) in zip(self._oldios, _iochannels):
441 441 try:
442 442 if 'w' in mode:
443 443 # Discard buffered data which couldn't be flushed because
444 444 # of EPIPE. The data should belong to the current session
445 445 # and should never persist.
446 446 os.dup2(nullfd, fp.fileno())
447 447 fp.flush()
448 448 os.dup2(fd, fp.fileno())
449 os.close(fd)
449 450 except OSError as err:
450 451 # According to issue6330, running chg on heavy loaded systems
451 452 # can lead to EBUSY. [man dup2] indicates that, on Linux,
452 453 # EBUSY comes from a race condition between open() and dup2().
453 454 # However it's not clear why open() race occurred for
454 455 # newfd=stdin/out/err.
455 456 self.ui.log(
456 457 b'chgserver',
457 458 b'got %s while duplicating %s\n',
458 459 stringutil.forcebytestr(err),
459 460 fn,
460 461 )
461 os.close(fd)
462 462 setattr(self, cn, ch)
463 463 setattr(ui, fn, fp)
464 464 os.close(nullfd)
465 465 del self._oldios[:]
466 466
467 467 def validate(self):
468 468 """Reload the config and check if the server is up to date
469 469
470 470 Read a list of '\0' separated arguments.
471 471 Write a non-empty list of '\0' separated instruction strings or '\0'
472 472 if the list is empty.
473 473 An instruction string could be either:
474 474 - "unlink $path", the client should unlink the path to stop the
475 475 outdated server.
476 476 - "redirect $path", the client should attempt to connect to $path
477 477 first. If it does not work, start a new server. It implies
478 478 "reconnect".
479 479 - "exit $n", the client should exit directly with code n.
480 480 This may happen if we cannot parse the config.
481 481 - "reconnect", the client should close the connection and
482 482 reconnect.
483 483 If neither "reconnect" nor "redirect" is included in the instruction
484 484 list, the client can continue with this server after completing all
485 485 the instructions.
486 486 """
487 487 args = self._readlist()
488 488 errorraised = False
489 489 detailed_exit_code = 255
490 490 try:
491 491 self.ui, lui = _loadnewui(self.ui, args, self.cdebug)
492 492 except error.RepoError as inst:
493 493 # RepoError can be raised while trying to read shared source
494 494 # configuration
495 495 self.ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
496 496 if inst.hint:
497 497 self.ui.error(_(b"(%s)\n") % inst.hint)
498 498 errorraised = True
499 499 except error.Error as inst:
500 500 if inst.detailed_exit_code is not None:
501 501 detailed_exit_code = inst.detailed_exit_code
502 502 self.ui.error(inst.format())
503 503 errorraised = True
504 504
505 505 if errorraised:
506 506 self.ui.flush()
507 507 exit_code = 255
508 508 if self.ui.configbool(b'ui', b'detailed-exit-code'):
509 509 exit_code = detailed_exit_code
510 510 self.cresult.write(b'exit %d' % exit_code)
511 511 return
512 512 newhash = hashstate.fromui(lui, self.hashstate.mtimepaths)
513 513 insts = []
514 514 if newhash.mtimehash != self.hashstate.mtimehash:
515 515 addr = _hashaddress(self.baseaddress, self.hashstate.confighash)
516 516 insts.append(b'unlink %s' % addr)
517 517 # mtimehash is empty if one or more extensions fail to load.
518 518 # to be compatible with hg, still serve the client this time.
519 519 if self.hashstate.mtimehash:
520 520 insts.append(b'reconnect')
521 521 if newhash.confighash != self.hashstate.confighash:
522 522 addr = _hashaddress(self.baseaddress, newhash.confighash)
523 523 insts.append(b'redirect %s' % addr)
524 524 self.ui.log(b'chgserver', b'validate: %s\n', stringutil.pprint(insts))
525 525 self.cresult.write(b'\0'.join(insts) or b'\0')
526 526
527 527 def chdir(self):
528 528 """Change current directory
529 529
530 530 Note that the behavior of --cwd option is bit different from this.
531 531 It does not affect --config parameter.
532 532 """
533 533 path = self._readstr()
534 534 if not path:
535 535 return
536 536 self.ui.log(b'chgserver', b"chdir to '%s'\n", path)
537 537 os.chdir(path)
538 538
539 539 def setumask(self):
540 540 """Change umask (DEPRECATED)"""
541 541 # BUG: this does not follow the message frame structure, but kept for
542 542 # backward compatibility with old chg clients for some time
543 543 self._setumask(self._read(4))
544 544
545 545 def setumask2(self):
546 546 """Change umask"""
547 547 data = self._readstr()
548 548 if len(data) != 4:
549 549 raise ValueError(b'invalid mask length in setumask2 request')
550 550 self._setumask(data)
551 551
552 552 def _setumask(self, data):
553 553 mask = struct.unpack(b'>I', data)[0]
554 554 self.ui.log(b'chgserver', b'setumask %r\n', mask)
555 555 util.setumask(mask)
556 556
557 557 def runcommand(self):
558 558 # pager may be attached within the runcommand session, which should
559 559 # be detached at the end of the session. otherwise the pager wouldn't
560 560 # receive EOF.
561 561 globaloldios = self._oldios
562 562 self._oldios = []
563 563 try:
564 564 return super(chgcmdserver, self).runcommand()
565 565 finally:
566 566 self._restoreio()
567 567 self._oldios = globaloldios
568 568
569 569 def setenv(self):
570 570 """Clear and update os.environ
571 571
572 572 Note that not all variables can make an effect on the running process.
573 573 """
574 574 l = self._readlist()
575 575 try:
576 576 newenv = dict(s.split(b'=', 1) for s in l)
577 577 except ValueError:
578 578 raise ValueError(b'unexpected value in setenv request')
579 579 self.ui.log(b'chgserver', b'setenv: %r\n', sorted(newenv.keys()))
580 580
581 581 encoding.environ.clear()
582 582 encoding.environ.update(newenv)
583 583
584 584 capabilities = commandserver.server.capabilities.copy()
585 585 capabilities.update(
586 586 {
587 587 b'attachio': attachio,
588 588 b'chdir': chdir,
589 589 b'runcommand': runcommand,
590 590 b'setenv': setenv,
591 591 b'setumask': setumask,
592 592 b'setumask2': setumask2,
593 593 }
594 594 )
595 595
596 596 if util.safehasattr(procutil, b'setprocname'):
597 597
598 598 def setprocname(self):
599 599 """Change process title"""
600 600 name = self._readstr()
601 601 self.ui.log(b'chgserver', b'setprocname: %r\n', name)
602 602 procutil.setprocname(name)
603 603
604 604 capabilities[b'setprocname'] = setprocname
605 605
606 606
607 607 def _tempaddress(address):
608 608 return b'%s.%d.tmp' % (address, os.getpid())
609 609
610 610
611 611 def _hashaddress(address, hashstr):
612 612 # if the basename of address contains '.', use only the left part. this
613 613 # makes it possible for the client to pass 'server.tmp$PID' and follow by
614 614 # an atomic rename to avoid locking when spawning new servers.
615 615 dirname, basename = os.path.split(address)
616 616 basename = basename.split(b'.', 1)[0]
617 617 return b'%s-%s' % (os.path.join(dirname, basename), hashstr)
618 618
619 619
620 620 class chgunixservicehandler:
621 621 """Set of operations for chg services"""
622 622
623 623 pollinterval = 1 # [sec]
624 624
625 625 def __init__(self, ui):
626 626 self.ui = ui
627 627
628 628 # TODO: use PEP 526 syntax (`_hashstate: hashstate` at the class level)
629 629 # when 3.5 support is dropped.
630 630 self._hashstate = None # type: hashstate
631 631 self._baseaddress = None # type: bytes
632 632 self._realaddress = None # type: bytes
633 633
634 634 self._idletimeout = ui.configint(b'chgserver', b'idletimeout')
635 635 self._lastactive = time.time()
636 636
637 637 def bindsocket(self, sock, address):
638 638 self._inithashstate(address)
639 639 self._checkextensions()
640 640 self._bind(sock)
641 641 self._createsymlink()
642 642 # no "listening at" message should be printed to simulate hg behavior
643 643
644 644 def _inithashstate(self, address):
645 645 self._baseaddress = address
646 646 if self.ui.configbool(b'chgserver', b'skiphash'):
647 647 self._hashstate = None
648 648 self._realaddress = address
649 649 return
650 650 self._hashstate = hashstate.fromui(self.ui)
651 651 self._realaddress = _hashaddress(address, self._hashstate.confighash)
652 652
653 653 def _checkextensions(self):
654 654 if not self._hashstate:
655 655 return
656 656 if extensions.notloaded():
657 657 # one or more extensions failed to load. mtimehash becomes
658 658 # meaningless because we do not know the paths of those extensions.
659 659 # set mtimehash to an illegal hash value to invalidate the server.
660 660 self._hashstate.mtimehash = b''
661 661
662 662 def _bind(self, sock):
663 663 # use a unique temp address so we can stat the file and do ownership
664 664 # check later
665 665 tempaddress = _tempaddress(self._realaddress)
666 666 util.bindunixsocket(sock, tempaddress)
667 667 self._socketstat = os.stat(tempaddress)
668 668 sock.listen(socket.SOMAXCONN)
669 669 # rename will replace the old socket file if exists atomically. the
670 670 # old server will detect ownership change and exit.
671 671 util.rename(tempaddress, self._realaddress)
672 672
673 673 def _createsymlink(self):
674 674 if self._baseaddress == self._realaddress:
675 675 return
676 676 tempaddress = _tempaddress(self._baseaddress)
677 677 os.symlink(os.path.basename(self._realaddress), tempaddress)
678 678 util.rename(tempaddress, self._baseaddress)
679 679
680 680 def _issocketowner(self):
681 681 try:
682 682 st = os.stat(self._realaddress)
683 683 return (
684 684 st.st_ino == self._socketstat.st_ino
685 685 and st[stat.ST_MTIME] == self._socketstat[stat.ST_MTIME]
686 686 )
687 687 except OSError:
688 688 return False
689 689
690 690 def unlinksocket(self, address):
691 691 if not self._issocketowner():
692 692 return
693 693 # it is possible to have a race condition here that we may
694 694 # remove another server's socket file. but that's okay
695 695 # since that server will detect and exit automatically and
696 696 # the client will start a new server on demand.
697 697 util.tryunlink(self._realaddress)
698 698
699 699 def shouldexit(self):
700 700 if not self._issocketowner():
701 701 self.ui.log(
702 702 b'chgserver', b'%s is not owned, exiting.\n', self._realaddress
703 703 )
704 704 return True
705 705 if time.time() - self._lastactive > self._idletimeout:
706 706 self.ui.log(b'chgserver', b'being idle too long. exiting.\n')
707 707 return True
708 708 return False
709 709
710 710 def newconnection(self):
711 711 self._lastactive = time.time()
712 712
713 713 def createcmdserver(self, repo, conn, fin, fout, prereposetups):
714 714 return chgcmdserver(
715 715 self.ui,
716 716 repo,
717 717 fin,
718 718 fout,
719 719 conn,
720 720 prereposetups,
721 721 self._hashstate,
722 722 self._baseaddress,
723 723 )
724 724
725 725
726 726 def chgunixservice(ui, repo, opts):
727 727 # CHGINTERNALMARK is set by chg client. It is an indication of things are
728 728 # started by chg so other code can do things accordingly, like disabling
729 729 # demandimport or detecting chg client started by chg client. When executed
730 730 # here, CHGINTERNALMARK is no longer useful and hence dropped to make
731 731 # environ cleaner.
732 732 if b'CHGINTERNALMARK' in encoding.environ:
733 733 del encoding.environ[b'CHGINTERNALMARK']
734 734 # Python3.7+ "coerces" the LC_CTYPE environment variable to a UTF-8 one if
735 735 # it thinks the current value is "C". This breaks the hash computation and
736 736 # causes chg to restart loop.
737 737 if b'CHGORIG_LC_CTYPE' in encoding.environ:
738 738 encoding.environ[b'LC_CTYPE'] = encoding.environ[b'CHGORIG_LC_CTYPE']
739 739 del encoding.environ[b'CHGORIG_LC_CTYPE']
740 740 elif b'CHG_CLEAR_LC_CTYPE' in encoding.environ:
741 741 if b'LC_CTYPE' in encoding.environ:
742 742 del encoding.environ[b'LC_CTYPE']
743 743 del encoding.environ[b'CHG_CLEAR_LC_CTYPE']
744 744
745 745 if repo:
746 746 # one chgserver can serve multiple repos. drop repo information
747 747 ui.setconfig(b'bundle', b'mainreporoot', b'', b'repo')
748 748 h = chgunixservicehandler(ui)
749 749 return commandserver.unixforkingservice(ui, repo=None, opts=opts, handler=h)
@@ -1,4004 +1,4009 b''
1 1 #!/usr/bin/env python3
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Olivia Mackall <olivia@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 # Modifying this script is tricky because it has many modes:
11 11 # - serial (default) vs parallel (-jN, N > 1)
12 12 # - no coverage (default) vs coverage (-c, -C, -s)
13 13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 14 # - tests are a mix of shell scripts and Python scripts
15 15 #
16 16 # If you change this script, it is recommended that you ensure you
17 17 # haven't broken it by running it in various modes with a representative
18 18 # sample of test scripts. For example:
19 19 #
20 20 # 1) serial, no coverage, temp install:
21 21 # ./run-tests.py test-s*
22 22 # 2) serial, no coverage, local hg:
23 23 # ./run-tests.py --local test-s*
24 24 # 3) serial, coverage, temp install:
25 25 # ./run-tests.py -c test-s*
26 26 # 4) serial, coverage, local hg:
27 27 # ./run-tests.py -c --local test-s* # unsupported
28 28 # 5) parallel, no coverage, temp install:
29 29 # ./run-tests.py -j2 test-s*
30 30 # 6) parallel, no coverage, local hg:
31 31 # ./run-tests.py -j2 --local test-s*
32 32 # 7) parallel, coverage, temp install:
33 33 # ./run-tests.py -j2 -c test-s* # currently broken
34 34 # 8) parallel, coverage, local install:
35 35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 36 # 9) parallel, custom tmp dir:
37 37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 38 # 10) parallel, pure, tests that call run-tests:
39 39 # ./run-tests.py --pure `grep -l run-tests.py *.t`
40 40 #
41 41 # (You could use any subset of the tests: test-s* happens to match
42 42 # enough that it's worth doing parallel runs, few enough that it
43 43 # completes fairly quickly, includes both shell and Python scripts, and
44 44 # includes some scripts that run daemon processes.)
45 45
46 46
47 47 import argparse
48 48 import collections
49 49 import contextlib
50 50 import difflib
51 51 import distutils.version as version
52 52 import errno
53 53 import functools
54 54 import json
55 55 import multiprocessing
56 56 import os
57 57 import platform
58 58 import queue
59 59 import random
60 60 import re
61 61 import shlex
62 62 import shutil
63 63 import signal
64 64 import socket
65 65 import subprocess
66 66 import sys
67 67 import sysconfig
68 68 import tempfile
69 69 import threading
70 70 import time
71 71 import unittest
72 72 import uuid
73 73 import xml.dom.minidom as minidom
74 74
75 75 if sys.version_info < (3, 5, 0):
76 76 print(
77 77 '%s is only supported on Python 3.5+, not %s'
78 78 % (sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3]))
79 79 )
80 80 sys.exit(70) # EX_SOFTWARE from `man 3 sysexit`
81 81
82 82 WINDOWS = os.name == r'nt'
83 83 shellquote = shlex.quote
84 84
85 85
86 86 processlock = threading.Lock()
87 87
88 88 pygmentspresent = False
89 89 try: # is pygments installed
90 90 import pygments
91 91 import pygments.lexers as lexers
92 92 import pygments.lexer as lexer
93 93 import pygments.formatters as formatters
94 94 import pygments.token as token
95 95 import pygments.style as style
96 96
97 97 if WINDOWS:
98 98 hgpath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
99 99 sys.path.append(hgpath)
100 100 try:
101 101 from mercurial import win32 # pytype: disable=import-error
102 102
103 103 # Don't check the result code because it fails on heptapod, but
104 104 # something is able to convert to color anyway.
105 105 win32.enablevtmode()
106 106 finally:
107 107 sys.path = sys.path[:-1]
108 108
109 109 pygmentspresent = True
110 110 difflexer = lexers.DiffLexer()
111 111 terminal256formatter = formatters.Terminal256Formatter()
112 112 except ImportError:
113 113 pass
114 114
115 115 if pygmentspresent:
116 116
117 117 class TestRunnerStyle(style.Style):
118 118 default_style = ""
119 119 skipped = token.string_to_tokentype("Token.Generic.Skipped")
120 120 failed = token.string_to_tokentype("Token.Generic.Failed")
121 121 skippedname = token.string_to_tokentype("Token.Generic.SName")
122 122 failedname = token.string_to_tokentype("Token.Generic.FName")
123 123 styles = {
124 124 skipped: '#e5e5e5',
125 125 skippedname: '#00ffff',
126 126 failed: '#7f0000',
127 127 failedname: '#ff0000',
128 128 }
129 129
130 130 class TestRunnerLexer(lexer.RegexLexer):
131 131 testpattern = r'[\w-]+\.(t|py)(#[a-zA-Z0-9_\-\.]+)?'
132 132 tokens = {
133 133 'root': [
134 134 (r'^Skipped', token.Generic.Skipped, 'skipped'),
135 135 (r'^Failed ', token.Generic.Failed, 'failed'),
136 136 (r'^ERROR: ', token.Generic.Failed, 'failed'),
137 137 ],
138 138 'skipped': [
139 139 (testpattern, token.Generic.SName),
140 140 (r':.*', token.Generic.Skipped),
141 141 ],
142 142 'failed': [
143 143 (testpattern, token.Generic.FName),
144 144 (r'(:| ).*', token.Generic.Failed),
145 145 ],
146 146 }
147 147
148 148 runnerformatter = formatters.Terminal256Formatter(style=TestRunnerStyle)
149 149 runnerlexer = TestRunnerLexer()
150 150
151 151 origenviron = os.environ.copy()
152 152
153 153
154 154 xrange = range # we use xrange in one place, and we'd rather not use range
155 155
156 156
157 157 def _sys2bytes(p):
158 158 if p is None:
159 159 return p
160 160 return p.encode('utf-8')
161 161
162 162
163 163 def _bytes2sys(p):
164 164 if p is None:
165 165 return p
166 166 return p.decode('utf-8')
167 167
168 168
169 169 osenvironb = getattr(os, 'environb', None)
170 170 if osenvironb is None:
171 171 # Windows lacks os.environb, for instance. A proxy over the real thing
172 172 # instead of a copy allows the environment to be updated via bytes on
173 173 # all platforms.
174 174 class environbytes:
175 175 def __init__(self, strenv):
176 176 self.__len__ = strenv.__len__
177 177 self.clear = strenv.clear
178 178 self._strenv = strenv
179 179
180 180 def __getitem__(self, k):
181 181 v = self._strenv.__getitem__(_bytes2sys(k))
182 182 return _sys2bytes(v)
183 183
184 184 def __setitem__(self, k, v):
185 185 self._strenv.__setitem__(_bytes2sys(k), _bytes2sys(v))
186 186
187 187 def __delitem__(self, k):
188 188 self._strenv.__delitem__(_bytes2sys(k))
189 189
190 190 def __contains__(self, k):
191 191 return self._strenv.__contains__(_bytes2sys(k))
192 192
193 193 def __iter__(self):
194 194 return iter([_sys2bytes(k) for k in iter(self._strenv)])
195 195
196 196 def get(self, k, default=None):
197 197 v = self._strenv.get(_bytes2sys(k), _bytes2sys(default))
198 198 return _sys2bytes(v)
199 199
200 200 def pop(self, k, default=None):
201 201 v = self._strenv.pop(_bytes2sys(k), _bytes2sys(default))
202 202 return _sys2bytes(v)
203 203
204 204 osenvironb = environbytes(os.environ)
205 205
206 206 getcwdb = getattr(os, 'getcwdb')
207 207 if not getcwdb or WINDOWS:
208 208 getcwdb = lambda: _sys2bytes(os.getcwd())
209 209
210 210
211 211 if WINDOWS:
212 212 _getcwdb = getcwdb
213 213
214 214 def getcwdb():
215 215 cwd = _getcwdb()
216 216 if re.match(b'^[a-z]:', cwd):
217 217 # os.getcwd() is inconsistent on the capitalization of the drive
218 218 # letter, so adjust it. see https://bugs.python.org/issue40368
219 219 cwd = cwd[0:1].upper() + cwd[1:]
220 220 return cwd
221 221
222 222
223 223 # For Windows support
224 224 wifexited = getattr(os, "WIFEXITED", lambda x: False)
225 225
226 226 # Whether to use IPv6
227 227 def checksocketfamily(name, port=20058):
228 228 """return true if we can listen on localhost using family=name
229 229
230 230 name should be either 'AF_INET', or 'AF_INET6'.
231 231 port being used is okay - EADDRINUSE is considered as successful.
232 232 """
233 233 family = getattr(socket, name, None)
234 234 if family is None:
235 235 return False
236 236 try:
237 237 s = socket.socket(family, socket.SOCK_STREAM)
238 238 s.bind(('localhost', port))
239 239 s.close()
240 240 return True
241 241 except (socket.error, OSError) as exc:
242 242 if exc.errno == errno.EADDRINUSE:
243 243 return True
244 244 elif exc.errno in (
245 245 errno.EADDRNOTAVAIL,
246 246 errno.EPROTONOSUPPORT,
247 247 errno.EAFNOSUPPORT,
248 248 ):
249 249 return False
250 250 else:
251 251 raise
252 252 else:
253 253 return False
254 254
255 255
256 256 # useipv6 will be set by parseargs
257 257 useipv6 = None
258 258
259 259
260 260 def checkportisavailable(port):
261 261 """return true if a port seems free to bind on localhost"""
262 262 if useipv6:
263 263 family = socket.AF_INET6
264 264 else:
265 265 family = socket.AF_INET
266 266 try:
267 267 with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as s:
268 268 s.bind(('localhost', port))
269 269 return True
270 270 except socket.error as exc:
271 271 if WINDOWS and exc.errno == errno.WSAEACCES:
272 272 return False
273 273 # TODO: make a proper exception handler after dropping py2. This
274 274 # works because socket.error is an alias for OSError on py3,
275 275 # which is also the baseclass of PermissionError.
276 276 elif isinstance(exc, PermissionError):
277 277 return False
278 278 if exc.errno not in (
279 279 errno.EADDRINUSE,
280 280 errno.EADDRNOTAVAIL,
281 281 errno.EPROTONOSUPPORT,
282 282 ):
283 283 raise
284 284 return False
285 285
286 286
287 287 closefds = os.name == 'posix'
288 288
289 289
290 290 def Popen4(cmd, wd, timeout, env=None):
291 291 processlock.acquire()
292 292 p = subprocess.Popen(
293 293 _bytes2sys(cmd),
294 294 shell=True,
295 295 bufsize=-1,
296 296 cwd=_bytes2sys(wd),
297 297 env=env,
298 298 close_fds=closefds,
299 299 stdin=subprocess.PIPE,
300 300 stdout=subprocess.PIPE,
301 301 stderr=subprocess.STDOUT,
302 302 )
303 303 processlock.release()
304 304
305 305 p.fromchild = p.stdout
306 306 p.tochild = p.stdin
307 307 p.childerr = p.stderr
308 308
309 309 p.timeout = False
310 310 if timeout:
311 311
312 312 def t():
313 313 start = time.time()
314 314 while time.time() - start < timeout and p.returncode is None:
315 315 time.sleep(0.1)
316 316 p.timeout = True
317 317 vlog('# Timout reached for process %d' % p.pid)
318 318 if p.returncode is None:
319 319 terminate(p)
320 320
321 321 threading.Thread(target=t).start()
322 322
323 323 return p
324 324
325 325
326 326 if sys.executable:
327 327 sysexecutable = sys.executable
328 328 elif os.environ.get('PYTHONEXECUTABLE'):
329 329 sysexecutable = os.environ['PYTHONEXECUTABLE']
330 330 elif os.environ.get('PYTHON'):
331 331 sysexecutable = os.environ['PYTHON']
332 332 else:
333 333 raise AssertionError('Could not find Python interpreter')
334 334
335 335 PYTHON = _sys2bytes(sysexecutable.replace('\\', '/'))
336 336 IMPL_PATH = b'PYTHONPATH'
337 337 if 'java' in sys.platform:
338 338 IMPL_PATH = b'JYTHONPATH'
339 339
340 340 default_defaults = {
341 341 'jobs': ('HGTEST_JOBS', multiprocessing.cpu_count()),
342 342 'timeout': ('HGTEST_TIMEOUT', 360),
343 343 'slowtimeout': ('HGTEST_SLOWTIMEOUT', 1500),
344 344 'port': ('HGTEST_PORT', 20059),
345 345 'shell': ('HGTEST_SHELL', 'sh'),
346 346 }
347 347
348 348 defaults = default_defaults.copy()
349 349
350 350
351 351 def canonpath(path):
352 352 return os.path.realpath(os.path.expanduser(path))
353 353
354 354
355 355 def which(exe):
356 356 # shutil.which only accept bytes from 3.8
357 357 cmd = _bytes2sys(exe)
358 358 real_exec = shutil.which(cmd)
359 359 return _sys2bytes(real_exec)
360 360
361 361
362 362 def parselistfiles(files, listtype, warn=True):
363 363 entries = dict()
364 364 for filename in files:
365 365 try:
366 366 path = os.path.expanduser(os.path.expandvars(filename))
367 367 f = open(path, "rb")
368 368 except IOError as err:
369 369 if err.errno != errno.ENOENT:
370 370 raise
371 371 if warn:
372 372 print("warning: no such %s file: %s" % (listtype, filename))
373 373 continue
374 374
375 375 for line in f.readlines():
376 376 line = line.split(b'#', 1)[0].strip()
377 377 if line:
378 378 # Ensure path entries are compatible with os.path.relpath()
379 379 entries[os.path.normpath(line)] = filename
380 380
381 381 f.close()
382 382 return entries
383 383
384 384
385 385 def parsettestcases(path):
386 386 """read a .t test file, return a set of test case names
387 387
388 388 If path does not exist, return an empty set.
389 389 """
390 390 cases = []
391 391 try:
392 392 with open(path, 'rb') as f:
393 393 for l in f:
394 394 if l.startswith(b'#testcases '):
395 395 cases.append(sorted(l[11:].split()))
396 396 except IOError as ex:
397 397 if ex.errno != errno.ENOENT:
398 398 raise
399 399 return cases
400 400
401 401
402 402 def getparser():
403 403 """Obtain the OptionParser used by the CLI."""
404 404 parser = argparse.ArgumentParser(usage='%(prog)s [options] [tests]')
405 405
406 406 selection = parser.add_argument_group('Test Selection')
407 407 selection.add_argument(
408 408 '--allow-slow-tests',
409 409 action='store_true',
410 410 help='allow extremely slow tests',
411 411 )
412 412 selection.add_argument(
413 413 "--blacklist",
414 414 action="append",
415 415 help="skip tests listed in the specified blacklist file",
416 416 )
417 417 selection.add_argument(
418 418 "--changed",
419 419 help="run tests that are changed in parent rev or working directory",
420 420 )
421 421 selection.add_argument(
422 422 "-k", "--keywords", help="run tests matching keywords"
423 423 )
424 424 selection.add_argument(
425 425 "-r", "--retest", action="store_true", help="retest failed tests"
426 426 )
427 427 selection.add_argument(
428 428 "--test-list",
429 429 action="append",
430 430 help="read tests to run from the specified file",
431 431 )
432 432 selection.add_argument(
433 433 "--whitelist",
434 434 action="append",
435 435 help="always run tests listed in the specified whitelist file",
436 436 )
437 437 selection.add_argument(
438 438 'tests', metavar='TESTS', nargs='*', help='Tests to run'
439 439 )
440 440
441 441 harness = parser.add_argument_group('Test Harness Behavior')
442 442 harness.add_argument(
443 443 '--bisect-repo',
444 444 metavar='bisect_repo',
445 445 help=(
446 446 "Path of a repo to bisect. Use together with " "--known-good-rev"
447 447 ),
448 448 )
449 449 harness.add_argument(
450 450 "-d",
451 451 "--debug",
452 452 action="store_true",
453 453 help="debug mode: write output of test scripts to console"
454 454 " rather than capturing and diffing it (disables timeout)",
455 455 )
456 456 harness.add_argument(
457 457 "-f",
458 458 "--first",
459 459 action="store_true",
460 460 help="exit on the first test failure",
461 461 )
462 462 harness.add_argument(
463 463 "-i",
464 464 "--interactive",
465 465 action="store_true",
466 466 help="prompt to accept changed output",
467 467 )
468 468 harness.add_argument(
469 469 "-j",
470 470 "--jobs",
471 471 type=int,
472 472 help="number of jobs to run in parallel"
473 473 " (default: $%s or %d)" % defaults['jobs'],
474 474 )
475 475 harness.add_argument(
476 476 "--keep-tmpdir",
477 477 action="store_true",
478 478 help="keep temporary directory after running tests",
479 479 )
480 480 harness.add_argument(
481 481 '--known-good-rev',
482 482 metavar="known_good_rev",
483 483 help=(
484 484 "Automatically bisect any failures using this "
485 485 "revision as a known-good revision."
486 486 ),
487 487 )
488 488 harness.add_argument(
489 489 "--list-tests",
490 490 action="store_true",
491 491 help="list tests instead of running them",
492 492 )
493 493 harness.add_argument(
494 494 "--loop", action="store_true", help="loop tests repeatedly"
495 495 )
496 496 harness.add_argument(
497 497 '--random', action="store_true", help='run tests in random order'
498 498 )
499 499 harness.add_argument(
500 500 '--order-by-runtime',
501 501 action="store_true",
502 502 help='run slowest tests first, according to .testtimes',
503 503 )
504 504 harness.add_argument(
505 505 "-p",
506 506 "--port",
507 507 type=int,
508 508 help="port on which servers should listen"
509 509 " (default: $%s or %d)" % defaults['port'],
510 510 )
511 511 harness.add_argument(
512 512 '--profile-runner',
513 513 action='store_true',
514 514 help='run statprof on run-tests',
515 515 )
516 516 harness.add_argument(
517 517 "-R", "--restart", action="store_true", help="restart at last error"
518 518 )
519 519 harness.add_argument(
520 520 "--runs-per-test",
521 521 type=int,
522 522 dest="runs_per_test",
523 523 help="run each test N times (default=1)",
524 524 default=1,
525 525 )
526 526 harness.add_argument(
527 527 "--shell", help="shell to use (default: $%s or %s)" % defaults['shell']
528 528 )
529 529 harness.add_argument(
530 530 '--showchannels', action='store_true', help='show scheduling channels'
531 531 )
532 532 harness.add_argument(
533 533 "--slowtimeout",
534 534 type=int,
535 535 help="kill errant slow tests after SLOWTIMEOUT seconds"
536 536 " (default: $%s or %d)" % defaults['slowtimeout'],
537 537 )
538 538 harness.add_argument(
539 539 "-t",
540 540 "--timeout",
541 541 type=int,
542 542 help="kill errant tests after TIMEOUT seconds"
543 543 " (default: $%s or %d)" % defaults['timeout'],
544 544 )
545 545 harness.add_argument(
546 546 "--tmpdir",
547 547 help="run tests in the given temporary directory"
548 548 " (implies --keep-tmpdir)",
549 549 )
550 550 harness.add_argument(
551 551 "-v", "--verbose", action="store_true", help="output verbose messages"
552 552 )
553 553
554 554 hgconf = parser.add_argument_group('Mercurial Configuration')
555 555 hgconf.add_argument(
556 556 "--chg",
557 557 action="store_true",
558 558 help="install and use chg wrapper in place of hg",
559 559 )
560 560 hgconf.add_argument(
561 561 "--chg-debug",
562 562 action="store_true",
563 563 help="show chg debug logs",
564 564 )
565 565 hgconf.add_argument(
566 566 "--rhg",
567 567 action="store_true",
568 568 help="install and use rhg Rust implementation in place of hg",
569 569 )
570 570 hgconf.add_argument(
571 571 "--pyoxidized",
572 572 action="store_true",
573 573 help="build the hg binary using pyoxidizer",
574 574 )
575 575 hgconf.add_argument("--compiler", help="compiler to build with")
576 576 hgconf.add_argument(
577 577 '--extra-config-opt',
578 578 action="append",
579 579 default=[],
580 580 help='set the given config opt in the test hgrc',
581 581 )
582 582 hgconf.add_argument(
583 583 "-l",
584 584 "--local",
585 585 action="store_true",
586 586 help="shortcut for --with-hg=<testdir>/../hg, "
587 587 "--with-rhg=<testdir>/../rust/target/release/rhg if --rhg is set, "
588 588 "and --with-chg=<testdir>/../contrib/chg/chg if --chg is set",
589 589 )
590 590 hgconf.add_argument(
591 591 "--ipv6",
592 592 action="store_true",
593 593 help="prefer IPv6 to IPv4 for network related tests",
594 594 )
595 595 hgconf.add_argument(
596 596 "--pure",
597 597 action="store_true",
598 598 help="use pure Python code instead of C extensions",
599 599 )
600 600 hgconf.add_argument(
601 601 "--rust",
602 602 action="store_true",
603 603 help="use Rust code alongside C extensions",
604 604 )
605 605 hgconf.add_argument(
606 606 "--no-rust",
607 607 action="store_true",
608 608 help="do not use Rust code even if compiled",
609 609 )
610 610 hgconf.add_argument(
611 611 "--with-chg",
612 612 metavar="CHG",
613 613 help="use specified chg wrapper in place of hg",
614 614 )
615 615 hgconf.add_argument(
616 616 "--with-rhg",
617 617 metavar="RHG",
618 618 help="use specified rhg Rust implementation in place of hg",
619 619 )
620 620 hgconf.add_argument(
621 621 "--with-hg",
622 622 metavar="HG",
623 623 help="test using specified hg script rather than a "
624 624 "temporary installation",
625 625 )
626 626
627 627 reporting = parser.add_argument_group('Results Reporting')
628 628 reporting.add_argument(
629 629 "-C",
630 630 "--annotate",
631 631 action="store_true",
632 632 help="output files annotated with coverage",
633 633 )
634 634 reporting.add_argument(
635 635 "--color",
636 636 choices=["always", "auto", "never"],
637 637 default=os.environ.get('HGRUNTESTSCOLOR', 'auto'),
638 638 help="colorisation: always|auto|never (default: auto)",
639 639 )
640 640 reporting.add_argument(
641 641 "-c",
642 642 "--cover",
643 643 action="store_true",
644 644 help="print a test coverage report",
645 645 )
646 646 reporting.add_argument(
647 647 '--exceptions',
648 648 action='store_true',
649 649 help='log all exceptions and generate an exception report',
650 650 )
651 651 reporting.add_argument(
652 652 "-H",
653 653 "--htmlcov",
654 654 action="store_true",
655 655 help="create an HTML report of the coverage of the files",
656 656 )
657 657 reporting.add_argument(
658 658 "--json",
659 659 action="store_true",
660 660 help="store test result data in 'report.json' file",
661 661 )
662 662 reporting.add_argument(
663 663 "--outputdir",
664 664 help="directory to write error logs to (default=test directory)",
665 665 )
666 666 reporting.add_argument(
667 667 "-n", "--nodiff", action="store_true", help="skip showing test changes"
668 668 )
669 669 reporting.add_argument(
670 670 "-S",
671 671 "--noskips",
672 672 action="store_true",
673 673 help="don't report skip tests verbosely",
674 674 )
675 675 reporting.add_argument(
676 676 "--time", action="store_true", help="time how long each test takes"
677 677 )
678 678 reporting.add_argument("--view", help="external diff viewer")
679 679 reporting.add_argument(
680 680 "--xunit", help="record xunit results at specified path"
681 681 )
682 682
683 683 for option, (envvar, default) in defaults.items():
684 684 defaults[option] = type(default)(os.environ.get(envvar, default))
685 685 parser.set_defaults(**defaults)
686 686
687 687 return parser
688 688
689 689
690 690 def parseargs(args, parser):
691 691 """Parse arguments with our OptionParser and validate results."""
692 692 options = parser.parse_args(args)
693 693
694 694 # jython is always pure
695 695 if 'java' in sys.platform or '__pypy__' in sys.modules:
696 696 options.pure = True
697 697
698 698 if platform.python_implementation() != 'CPython' and options.rust:
699 699 parser.error('Rust extensions are only available with CPython')
700 700
701 701 if options.pure and options.rust:
702 702 parser.error('--rust cannot be used with --pure')
703 703
704 704 if options.rust and options.no_rust:
705 705 parser.error('--rust cannot be used with --no-rust')
706 706
707 707 if options.local:
708 708 if options.with_hg or options.with_rhg or options.with_chg:
709 709 parser.error(
710 710 '--local cannot be used with --with-hg or --with-rhg or --with-chg'
711 711 )
712 712 if options.pyoxidized:
713 713 parser.error('--pyoxidized does not work with --local (yet)')
714 714 testdir = os.path.dirname(_sys2bytes(canonpath(sys.argv[0])))
715 715 reporootdir = os.path.dirname(testdir)
716 716 pathandattrs = [(b'hg', 'with_hg')]
717 717 if options.chg:
718 718 pathandattrs.append((b'contrib/chg/chg', 'with_chg'))
719 719 if options.rhg:
720 720 pathandattrs.append((b'rust/target/release/rhg', 'with_rhg'))
721 721 for relpath, attr in pathandattrs:
722 722 binpath = os.path.join(reporootdir, relpath)
723 723 if not (WINDOWS or os.access(binpath, os.X_OK)):
724 724 parser.error(
725 725 '--local specified, but %r not found or '
726 726 'not executable' % binpath
727 727 )
728 728 setattr(options, attr, _bytes2sys(binpath))
729 729
730 730 if options.with_hg:
731 731 options.with_hg = canonpath(_sys2bytes(options.with_hg))
732 732 if not (
733 733 os.path.isfile(options.with_hg)
734 734 and os.access(options.with_hg, os.X_OK)
735 735 ):
736 736 parser.error('--with-hg must specify an executable hg script')
737 737 if os.path.basename(options.with_hg) not in [b'hg', b'hg.exe']:
738 738 msg = 'warning: --with-hg should specify an hg script, not: %s\n'
739 739 msg %= _bytes2sys(os.path.basename(options.with_hg))
740 740 sys.stderr.write(msg)
741 741 sys.stderr.flush()
742 742
743 743 if (options.chg or options.with_chg) and WINDOWS:
744 744 parser.error('chg does not work on %s' % os.name)
745 745 if (options.rhg or options.with_rhg) and WINDOWS:
746 746 parser.error('rhg does not work on %s' % os.name)
747 747 if options.pyoxidized and not WINDOWS:
748 748 parser.error('--pyoxidized is currently Windows only')
749 749 if options.with_chg:
750 750 options.chg = False # no installation to temporary location
751 751 options.with_chg = canonpath(_sys2bytes(options.with_chg))
752 752 if not (
753 753 os.path.isfile(options.with_chg)
754 754 and os.access(options.with_chg, os.X_OK)
755 755 ):
756 756 parser.error('--with-chg must specify a chg executable')
757 757 if options.with_rhg:
758 758 options.rhg = False # no installation to temporary location
759 759 options.with_rhg = canonpath(_sys2bytes(options.with_rhg))
760 760 if not (
761 761 os.path.isfile(options.with_rhg)
762 762 and os.access(options.with_rhg, os.X_OK)
763 763 ):
764 764 parser.error('--with-rhg must specify a rhg executable')
765 765 if options.chg and options.with_hg:
766 766 # chg shares installation location with hg
767 767 parser.error(
768 768 '--chg does not work when --with-hg is specified '
769 769 '(use --with-chg instead)'
770 770 )
771 771 if options.rhg and options.with_hg:
772 772 # rhg shares installation location with hg
773 773 parser.error(
774 774 '--rhg does not work when --with-hg is specified '
775 775 '(use --with-rhg instead)'
776 776 )
777 777 if options.rhg and options.chg:
778 778 parser.error('--rhg and --chg do not work together')
779 779
780 780 if options.color == 'always' and not pygmentspresent:
781 781 sys.stderr.write(
782 782 'warning: --color=always ignored because '
783 783 'pygments is not installed\n'
784 784 )
785 785
786 786 if options.bisect_repo and not options.known_good_rev:
787 787 parser.error("--bisect-repo cannot be used without --known-good-rev")
788 788
789 789 global useipv6
790 790 if options.ipv6:
791 791 useipv6 = checksocketfamily('AF_INET6')
792 792 else:
793 793 # only use IPv6 if IPv4 is unavailable and IPv6 is available
794 794 useipv6 = (not checksocketfamily('AF_INET')) and checksocketfamily(
795 795 'AF_INET6'
796 796 )
797 797
798 798 options.anycoverage = options.cover or options.annotate or options.htmlcov
799 799 if options.anycoverage:
800 800 try:
801 801 import coverage
802 802
803 803 covver = version.StrictVersion(coverage.__version__).version
804 804 if covver < (3, 3):
805 805 parser.error('coverage options require coverage 3.3 or later')
806 806 except ImportError:
807 807 parser.error('coverage options now require the coverage package')
808 808
809 809 if options.anycoverage and options.local:
810 810 # this needs some path mangling somewhere, I guess
811 811 parser.error(
812 812 "sorry, coverage options do not work when --local " "is specified"
813 813 )
814 814
815 815 if options.anycoverage and options.with_hg:
816 816 parser.error(
817 817 "sorry, coverage options do not work when --with-hg " "is specified"
818 818 )
819 819
820 820 global verbose
821 821 if options.verbose:
822 822 verbose = ''
823 823
824 824 if options.tmpdir:
825 825 options.tmpdir = canonpath(options.tmpdir)
826 826
827 827 if options.jobs < 1:
828 828 parser.error('--jobs must be positive')
829 829 if options.interactive and options.debug:
830 830 parser.error("-i/--interactive and -d/--debug are incompatible")
831 831 if options.debug:
832 832 if options.timeout != defaults['timeout']:
833 833 sys.stderr.write('warning: --timeout option ignored with --debug\n')
834 834 if options.slowtimeout != defaults['slowtimeout']:
835 835 sys.stderr.write(
836 836 'warning: --slowtimeout option ignored with --debug\n'
837 837 )
838 838 options.timeout = 0
839 839 options.slowtimeout = 0
840 840
841 841 if options.blacklist:
842 842 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
843 843 if options.whitelist:
844 844 options.whitelisted = parselistfiles(options.whitelist, 'whitelist')
845 845 else:
846 846 options.whitelisted = {}
847 847
848 848 if options.showchannels:
849 849 options.nodiff = True
850 850
851 851 return options
852 852
853 853
854 854 def rename(src, dst):
855 855 """Like os.rename(), trade atomicity and opened files friendliness
856 856 for existing destination support.
857 857 """
858 858 shutil.copy(src, dst)
859 859 os.remove(src)
860 860
861 861
862 862 def makecleanable(path):
863 863 """Try to fix directory permission recursively so that the entire tree
864 864 can be deleted"""
865 865 for dirpath, dirnames, _filenames in os.walk(path, topdown=True):
866 866 for d in dirnames:
867 867 p = os.path.join(dirpath, d)
868 868 try:
869 869 os.chmod(p, os.stat(p).st_mode & 0o777 | 0o700) # chmod u+rwx
870 870 except OSError:
871 871 pass
872 872
873 873
874 874 _unified_diff = functools.partial(difflib.diff_bytes, difflib.unified_diff)
875 875
876 876
877 877 def getdiff(expected, output, ref, err):
878 878 servefail = False
879 879 lines = []
880 880 for line in _unified_diff(expected, output, ref, err):
881 881 if line.startswith(b'+++') or line.startswith(b'---'):
882 882 line = line.replace(b'\\', b'/')
883 883 if line.endswith(b' \n'):
884 884 line = line[:-2] + b'\n'
885 885 lines.append(line)
886 886 if not servefail and line.startswith(
887 887 b'+ abort: child process failed to start'
888 888 ):
889 889 servefail = True
890 890
891 891 return servefail, lines
892 892
893 893
894 894 verbose = False
895 895
896 896
897 897 def vlog(*msg):
898 898 """Log only when in verbose mode."""
899 899 if verbose is False:
900 900 return
901 901
902 902 return log(*msg)
903 903
904 904
905 905 # Bytes that break XML even in a CDATA block: control characters 0-31
906 906 # sans \t, \n and \r
907 907 CDATA_EVIL = re.compile(br"[\000-\010\013\014\016-\037]")
908 908
909 909 # Match feature conditionalized output lines in the form, capturing the feature
910 910 # list in group 2, and the preceeding line output in group 1:
911 911 #
912 912 # output..output (feature !)\n
913 913 optline = re.compile(br'(.*) \((.+?) !\)\n$')
914 914
915 915
916 916 def cdatasafe(data):
917 917 """Make a string safe to include in a CDATA block.
918 918
919 919 Certain control characters are illegal in a CDATA block, and
920 920 there's no way to include a ]]> in a CDATA either. This function
921 921 replaces illegal bytes with ? and adds a space between the ]] so
922 922 that it won't break the CDATA block.
923 923 """
924 924 return CDATA_EVIL.sub(b'?', data).replace(b']]>', b'] ]>')
925 925
926 926
927 927 def log(*msg):
928 928 """Log something to stdout.
929 929
930 930 Arguments are strings to print.
931 931 """
932 932 with iolock:
933 933 if verbose:
934 934 print(verbose, end=' ')
935 935 for m in msg:
936 936 print(m, end=' ')
937 937 print()
938 938 sys.stdout.flush()
939 939
940 940
941 941 def highlightdiff(line, color):
942 942 if not color:
943 943 return line
944 944 assert pygmentspresent
945 945 return pygments.highlight(
946 946 line.decode('latin1'), difflexer, terminal256formatter
947 947 ).encode('latin1')
948 948
949 949
950 950 def highlightmsg(msg, color):
951 951 if not color:
952 952 return msg
953 953 assert pygmentspresent
954 954 return pygments.highlight(msg, runnerlexer, runnerformatter)
955 955
956 956
957 957 def terminate(proc):
958 958 """Terminate subprocess"""
959 959 vlog('# Terminating process %d' % proc.pid)
960 960 try:
961 961 proc.terminate()
962 962 except OSError:
963 963 pass
964 964
965 965
966 966 def killdaemons(pidfile):
967 967 import killdaemons as killmod
968 968
969 969 return killmod.killdaemons(pidfile, tryhard=False, remove=True, logfn=vlog)
970 970
971 971
972 # sysconfig is not thread-safe (https://github.com/python/cpython/issues/92452)
973 sysconfiglock = threading.Lock()
974
975
972 976 class Test(unittest.TestCase):
973 977 """Encapsulates a single, runnable test.
974 978
975 979 While this class conforms to the unittest.TestCase API, it differs in that
976 980 instances need to be instantiated manually. (Typically, unittest.TestCase
977 981 classes are instantiated automatically by scanning modules.)
978 982 """
979 983
980 984 # Status code reserved for skipped tests (used by hghave).
981 985 SKIPPED_STATUS = 80
982 986
983 987 def __init__(
984 988 self,
985 989 path,
986 990 outputdir,
987 991 tmpdir,
988 992 keeptmpdir=False,
989 993 debug=False,
990 994 first=False,
991 995 timeout=None,
992 996 startport=None,
993 997 extraconfigopts=None,
994 998 shell=None,
995 999 hgcommand=None,
996 1000 slowtimeout=None,
997 1001 usechg=False,
998 1002 chgdebug=False,
999 1003 useipv6=False,
1000 1004 ):
1001 1005 """Create a test from parameters.
1002 1006
1003 1007 path is the full path to the file defining the test.
1004 1008
1005 1009 tmpdir is the main temporary directory to use for this test.
1006 1010
1007 1011 keeptmpdir determines whether to keep the test's temporary directory
1008 1012 after execution. It defaults to removal (False).
1009 1013
1010 1014 debug mode will make the test execute verbosely, with unfiltered
1011 1015 output.
1012 1016
1013 1017 timeout controls the maximum run time of the test. It is ignored when
1014 1018 debug is True. See slowtimeout for tests with #require slow.
1015 1019
1016 1020 slowtimeout overrides timeout if the test has #require slow.
1017 1021
1018 1022 startport controls the starting port number to use for this test. Each
1019 1023 test will reserve 3 port numbers for execution. It is the caller's
1020 1024 responsibility to allocate a non-overlapping port range to Test
1021 1025 instances.
1022 1026
1023 1027 extraconfigopts is an iterable of extra hgrc config options. Values
1024 1028 must have the form "key=value" (something understood by hgrc). Values
1025 1029 of the form "foo.key=value" will result in "[foo] key=value".
1026 1030
1027 1031 shell is the shell to execute tests in.
1028 1032 """
1029 1033 if timeout is None:
1030 1034 timeout = defaults['timeout']
1031 1035 if startport is None:
1032 1036 startport = defaults['port']
1033 1037 if slowtimeout is None:
1034 1038 slowtimeout = defaults['slowtimeout']
1035 1039 self.path = path
1036 1040 self.relpath = os.path.relpath(path)
1037 1041 self.bname = os.path.basename(path)
1038 1042 self.name = _bytes2sys(self.bname)
1039 1043 self._testdir = os.path.dirname(path)
1040 1044 self._outputdir = outputdir
1041 1045 self._tmpname = os.path.basename(path)
1042 1046 self.errpath = os.path.join(self._outputdir, b'%s.err' % self.bname)
1043 1047
1044 1048 self._threadtmp = tmpdir
1045 1049 self._keeptmpdir = keeptmpdir
1046 1050 self._debug = debug
1047 1051 self._first = first
1048 1052 self._timeout = timeout
1049 1053 self._slowtimeout = slowtimeout
1050 1054 self._startport = startport
1051 1055 self._extraconfigopts = extraconfigopts or []
1052 1056 self._shell = _sys2bytes(shell)
1053 1057 self._hgcommand = hgcommand or b'hg'
1054 1058 self._usechg = usechg
1055 1059 self._chgdebug = chgdebug
1056 1060 self._useipv6 = useipv6
1057 1061
1058 1062 self._aborted = False
1059 1063 self._daemonpids = []
1060 1064 self._finished = None
1061 1065 self._ret = None
1062 1066 self._out = None
1063 1067 self._skipped = None
1064 1068 self._testtmp = None
1065 1069 self._chgsockdir = None
1066 1070
1067 1071 self._refout = self.readrefout()
1068 1072
1069 1073 def readrefout(self):
1070 1074 """read reference output"""
1071 1075 # If we're not in --debug mode and reference output file exists,
1072 1076 # check test output against it.
1073 1077 if self._debug:
1074 1078 return None # to match "out is None"
1075 1079 elif os.path.exists(self.refpath):
1076 1080 with open(self.refpath, 'rb') as f:
1077 1081 return f.read().splitlines(True)
1078 1082 else:
1079 1083 return []
1080 1084
1081 1085 # needed to get base class __repr__ running
1082 1086 @property
1083 1087 def _testMethodName(self):
1084 1088 return self.name
1085 1089
1086 1090 def __str__(self):
1087 1091 return self.name
1088 1092
1089 1093 def shortDescription(self):
1090 1094 return self.name
1091 1095
1092 1096 def setUp(self):
1093 1097 """Tasks to perform before run()."""
1094 1098 self._finished = False
1095 1099 self._ret = None
1096 1100 self._out = None
1097 1101 self._skipped = None
1098 1102
1099 1103 try:
1100 1104 os.mkdir(self._threadtmp)
1101 1105 except OSError as e:
1102 1106 if e.errno != errno.EEXIST:
1103 1107 raise
1104 1108
1105 1109 name = self._tmpname
1106 1110 self._testtmp = os.path.join(self._threadtmp, name)
1107 1111 os.mkdir(self._testtmp)
1108 1112
1109 1113 # Remove any previous output files.
1110 1114 if os.path.exists(self.errpath):
1111 1115 try:
1112 1116 os.remove(self.errpath)
1113 1117 except OSError as e:
1114 1118 # We might have raced another test to clean up a .err
1115 1119 # file, so ignore ENOENT when removing a previous .err
1116 1120 # file.
1117 1121 if e.errno != errno.ENOENT:
1118 1122 raise
1119 1123
1120 1124 if self._usechg:
1121 1125 self._chgsockdir = os.path.join(
1122 1126 self._threadtmp, b'%s.chgsock' % name
1123 1127 )
1124 1128 os.mkdir(self._chgsockdir)
1125 1129
1126 1130 def run(self, result):
1127 1131 """Run this test and report results against a TestResult instance."""
1128 1132 # This function is extremely similar to unittest.TestCase.run(). Once
1129 1133 # we require Python 2.7 (or at least its version of unittest), this
1130 1134 # function can largely go away.
1131 1135 self._result = result
1132 1136 result.startTest(self)
1133 1137 try:
1134 1138 try:
1135 1139 self.setUp()
1136 1140 except (KeyboardInterrupt, SystemExit):
1137 1141 self._aborted = True
1138 1142 raise
1139 1143 except Exception:
1140 1144 result.addError(self, sys.exc_info())
1141 1145 return
1142 1146
1143 1147 success = False
1144 1148 try:
1145 1149 self.runTest()
1146 1150 except KeyboardInterrupt:
1147 1151 self._aborted = True
1148 1152 raise
1149 1153 except unittest.SkipTest as e:
1150 1154 result.addSkip(self, str(e))
1151 1155 # The base class will have already counted this as a
1152 1156 # test we "ran", but we want to exclude skipped tests
1153 1157 # from those we count towards those run.
1154 1158 result.testsRun -= 1
1155 1159 except self.failureException as e:
1156 1160 # This differs from unittest in that we don't capture
1157 1161 # the stack trace. This is for historical reasons and
1158 1162 # this decision could be revisited in the future,
1159 1163 # especially for PythonTest instances.
1160 1164 if result.addFailure(self, str(e)):
1161 1165 success = True
1162 1166 except Exception:
1163 1167 result.addError(self, sys.exc_info())
1164 1168 else:
1165 1169 success = True
1166 1170
1167 1171 try:
1168 1172 self.tearDown()
1169 1173 except (KeyboardInterrupt, SystemExit):
1170 1174 self._aborted = True
1171 1175 raise
1172 1176 except Exception:
1173 1177 result.addError(self, sys.exc_info())
1174 1178 success = False
1175 1179
1176 1180 if success:
1177 1181 result.addSuccess(self)
1178 1182 finally:
1179 1183 result.stopTest(self, interrupted=self._aborted)
1180 1184
1181 1185 def runTest(self):
1182 1186 """Run this test instance.
1183 1187
1184 1188 This will return a tuple describing the result of the test.
1185 1189 """
1186 1190 env = self._getenv()
1187 1191 self._genrestoreenv(env)
1188 1192 self._daemonpids.append(env['DAEMON_PIDS'])
1189 1193 self._createhgrc(env['HGRCPATH'])
1190 1194
1191 1195 vlog('# Test', self.name)
1192 1196
1193 1197 ret, out = self._run(env)
1194 1198 self._finished = True
1195 1199 self._ret = ret
1196 1200 self._out = out
1197 1201
1198 1202 def describe(ret):
1199 1203 if ret < 0:
1200 1204 return 'killed by signal: %d' % -ret
1201 1205 return 'returned error code %d' % ret
1202 1206
1203 1207 self._skipped = False
1204 1208
1205 1209 if ret == self.SKIPPED_STATUS:
1206 1210 if out is None: # Debug mode, nothing to parse.
1207 1211 missing = ['unknown']
1208 1212 failed = None
1209 1213 else:
1210 1214 missing, failed = TTest.parsehghaveoutput(out)
1211 1215
1212 1216 if not missing:
1213 1217 missing = ['skipped']
1214 1218
1215 1219 if failed:
1216 1220 self.fail('hg have failed checking for %s' % failed[-1])
1217 1221 else:
1218 1222 self._skipped = True
1219 1223 raise unittest.SkipTest(missing[-1])
1220 1224 elif ret == 'timeout':
1221 1225 self.fail('timed out')
1222 1226 elif ret is False:
1223 1227 self.fail('no result code from test')
1224 1228 elif out != self._refout:
1225 1229 # Diff generation may rely on written .err file.
1226 1230 if (
1227 1231 (ret != 0 or out != self._refout)
1228 1232 and not self._skipped
1229 1233 and not self._debug
1230 1234 ):
1231 1235 with open(self.errpath, 'wb') as f:
1232 1236 for line in out:
1233 1237 f.write(line)
1234 1238
1235 1239 # The result object handles diff calculation for us.
1236 1240 with firstlock:
1237 1241 if self._result.addOutputMismatch(self, ret, out, self._refout):
1238 1242 # change was accepted, skip failing
1239 1243 return
1240 1244 if self._first:
1241 1245 global firsterror
1242 1246 firsterror = True
1243 1247
1244 1248 if ret:
1245 1249 msg = 'output changed and ' + describe(ret)
1246 1250 else:
1247 1251 msg = 'output changed'
1248 1252
1249 1253 self.fail(msg)
1250 1254 elif ret:
1251 1255 self.fail(describe(ret))
1252 1256
1253 1257 def tearDown(self):
1254 1258 """Tasks to perform after run()."""
1255 1259 for entry in self._daemonpids:
1256 1260 killdaemons(entry)
1257 1261 self._daemonpids = []
1258 1262
1259 1263 if self._keeptmpdir:
1260 1264 log(
1261 1265 '\nKeeping testtmp dir: %s\nKeeping threadtmp dir: %s'
1262 1266 % (
1263 1267 _bytes2sys(self._testtmp),
1264 1268 _bytes2sys(self._threadtmp),
1265 1269 )
1266 1270 )
1267 1271 else:
1268 1272 try:
1269 1273 shutil.rmtree(self._testtmp)
1270 1274 except OSError:
1271 1275 # unreadable directory may be left in $TESTTMP; fix permission
1272 1276 # and try again
1273 1277 makecleanable(self._testtmp)
1274 1278 shutil.rmtree(self._testtmp, True)
1275 1279 shutil.rmtree(self._threadtmp, True)
1276 1280
1277 1281 if self._usechg:
1278 1282 # chgservers will stop automatically after they find the socket
1279 1283 # files are deleted
1280 1284 shutil.rmtree(self._chgsockdir, True)
1281 1285
1282 1286 if (
1283 1287 (self._ret != 0 or self._out != self._refout)
1284 1288 and not self._skipped
1285 1289 and not self._debug
1286 1290 and self._out
1287 1291 ):
1288 1292 with open(self.errpath, 'wb') as f:
1289 1293 for line in self._out:
1290 1294 f.write(line)
1291 1295
1292 1296 vlog("# Ret was:", self._ret, '(%s)' % self.name)
1293 1297
1294 1298 def _run(self, env):
1295 1299 # This should be implemented in child classes to run tests.
1296 1300 raise unittest.SkipTest('unknown test type')
1297 1301
1298 1302 def abort(self):
1299 1303 """Terminate execution of this test."""
1300 1304 self._aborted = True
1301 1305
1302 1306 def _portmap(self, i):
1303 1307 offset = b'' if i == 0 else b'%d' % i
1304 1308 return (br':%d\b' % (self._startport + i), b':$HGPORT%s' % offset)
1305 1309
1306 1310 def _getreplacements(self):
1307 1311 """Obtain a mapping of text replacements to apply to test output.
1308 1312
1309 1313 Test output needs to be normalized so it can be compared to expected
1310 1314 output. This function defines how some of that normalization will
1311 1315 occur.
1312 1316 """
1313 1317 r = [
1314 1318 # This list should be parallel to defineport in _getenv
1315 1319 self._portmap(0),
1316 1320 self._portmap(1),
1317 1321 self._portmap(2),
1318 1322 (br'([^0-9])%s' % re.escape(self._localip()), br'\1$LOCALIP'),
1319 1323 (br'\bHG_TXNID=TXN:[a-f0-9]{40}\b', br'HG_TXNID=TXN:$ID$'),
1320 1324 ]
1321 1325 r.append((self._escapepath(self._testtmp), b'$TESTTMP'))
1322 1326 if WINDOWS:
1323 1327 # JSON output escapes backslashes in Windows paths, so also catch a
1324 1328 # double-escape.
1325 1329 replaced = self._testtmp.replace(b'\\', br'\\')
1326 1330 r.append((self._escapepath(replaced), b'$STR_REPR_TESTTMP'))
1327 1331
1328 1332 replacementfile = os.path.join(self._testdir, b'common-pattern.py')
1329 1333
1330 1334 if os.path.exists(replacementfile):
1331 1335 data = {}
1332 1336 with open(replacementfile, mode='rb') as source:
1333 1337 # the intermediate 'compile' step help with debugging
1334 1338 code = compile(source.read(), replacementfile, 'exec')
1335 1339 exec(code, data)
1336 1340 for value in data.get('substitutions', ()):
1337 1341 if len(value) != 2:
1338 1342 msg = 'malformatted substitution in %s: %r'
1339 1343 msg %= (replacementfile, value)
1340 1344 raise ValueError(msg)
1341 1345 r.append(value)
1342 1346 return r
1343 1347
1344 1348 def _escapepath(self, p):
1345 1349 if WINDOWS:
1346 1350 return b''.join(
1347 1351 c.isalpha()
1348 1352 and b'[%s%s]' % (c.lower(), c.upper())
1349 1353 or c in b'/\\'
1350 1354 and br'[/\\]'
1351 1355 or c.isdigit()
1352 1356 and c
1353 1357 or b'\\' + c
1354 1358 for c in [p[i : i + 1] for i in range(len(p))]
1355 1359 )
1356 1360 else:
1357 1361 return re.escape(p)
1358 1362
1359 1363 def _localip(self):
1360 1364 if self._useipv6:
1361 1365 return b'::1'
1362 1366 else:
1363 1367 return b'127.0.0.1'
1364 1368
1365 1369 def _genrestoreenv(self, testenv):
1366 1370 """Generate a script that can be used by tests to restore the original
1367 1371 environment."""
1368 1372 # Put the restoreenv script inside self._threadtmp
1369 1373 scriptpath = os.path.join(self._threadtmp, b'restoreenv.sh')
1370 1374 testenv['HGTEST_RESTOREENV'] = _bytes2sys(scriptpath)
1371 1375
1372 1376 # Only restore environment variable names that the shell allows
1373 1377 # us to export.
1374 1378 name_regex = re.compile('^[a-zA-Z][a-zA-Z0-9_]*$')
1375 1379
1376 1380 # Do not restore these variables; otherwise tests would fail.
1377 1381 reqnames = {'PYTHON', 'TESTDIR', 'TESTTMP'}
1378 1382
1379 1383 with open(scriptpath, 'w') as envf:
1380 1384 for name, value in origenviron.items():
1381 1385 if not name_regex.match(name):
1382 1386 # Skip environment variables with unusual names not
1383 1387 # allowed by most shells.
1384 1388 continue
1385 1389 if name in reqnames:
1386 1390 continue
1387 1391 envf.write('%s=%s\n' % (name, shellquote(value)))
1388 1392
1389 1393 for name in testenv:
1390 1394 if name in origenviron or name in reqnames:
1391 1395 continue
1392 1396 envf.write('unset %s\n' % (name,))
1393 1397
1394 1398 def _getenv(self):
1395 1399 """Obtain environment variables to use during test execution."""
1396 1400
1397 1401 def defineport(i):
1398 1402 offset = '' if i == 0 else '%s' % i
1399 1403 env["HGPORT%s" % offset] = '%s' % (self._startport + i)
1400 1404
1401 1405 env = os.environ.copy()
1402 env['PYTHONUSERBASE'] = sysconfig.get_config_var('userbase') or ''
1406 with sysconfiglock:
1407 env['PYTHONUSERBASE'] = sysconfig.get_config_var('userbase') or ''
1403 1408 env['HGEMITWARNINGS'] = '1'
1404 1409 env['TESTTMP'] = _bytes2sys(self._testtmp)
1405 1410 # the FORWARD_SLASH version is useful when running `sh` on non unix
1406 1411 # system (e.g. Windows)
1407 1412 env['TESTTMP_FORWARD_SLASH'] = env['TESTTMP'].replace(os.sep, '/')
1408 1413 uid_file = os.path.join(_bytes2sys(self._testtmp), 'UID')
1409 1414 env['HGTEST_UUIDFILE'] = uid_file
1410 1415 env['TESTNAME'] = self.name
1411 1416 env['HOME'] = _bytes2sys(self._testtmp)
1412 1417 if WINDOWS:
1413 1418 env['REALUSERPROFILE'] = env['USERPROFILE']
1414 1419 # py3.8+ ignores HOME: https://bugs.python.org/issue36264
1415 1420 env['USERPROFILE'] = env['HOME']
1416 1421 formated_timeout = _bytes2sys(b"%d" % default_defaults['timeout'][1])
1417 1422 env['HGTEST_TIMEOUT_DEFAULT'] = formated_timeout
1418 1423 env['HGTEST_TIMEOUT'] = _bytes2sys(b"%d" % self._timeout)
1419 1424 # This number should match portneeded in _getport
1420 1425 for port in xrange(3):
1421 1426 # This list should be parallel to _portmap in _getreplacements
1422 1427 defineport(port)
1423 1428 env["HGRCPATH"] = _bytes2sys(os.path.join(self._threadtmp, b'.hgrc'))
1424 1429 env["DAEMON_PIDS"] = _bytes2sys(
1425 1430 os.path.join(self._threadtmp, b'daemon.pids')
1426 1431 )
1427 1432 env["HGEDITOR"] = (
1428 1433 '"' + sysexecutable + '"' + ' -c "import sys; sys.exit(0)"'
1429 1434 )
1430 1435 env["HGUSER"] = "test"
1431 1436 env["HGENCODING"] = "ascii"
1432 1437 env["HGENCODINGMODE"] = "strict"
1433 1438 env["HGHOSTNAME"] = "test-hostname"
1434 1439 env['HGIPV6'] = str(int(self._useipv6))
1435 1440 # See contrib/catapipe.py for how to use this functionality.
1436 1441 if 'HGTESTCATAPULTSERVERPIPE' not in env:
1437 1442 # If we don't have HGTESTCATAPULTSERVERPIPE explicitly set, pull the
1438 1443 # non-test one in as a default, otherwise set to devnull
1439 1444 env['HGTESTCATAPULTSERVERPIPE'] = env.get(
1440 1445 'HGCATAPULTSERVERPIPE', os.devnull
1441 1446 )
1442 1447
1443 1448 extraextensions = []
1444 1449 for opt in self._extraconfigopts:
1445 1450 section, key = opt.split('.', 1)
1446 1451 if section != 'extensions':
1447 1452 continue
1448 1453 name = key.split('=', 1)[0]
1449 1454 extraextensions.append(name)
1450 1455
1451 1456 if extraextensions:
1452 1457 env['HGTESTEXTRAEXTENSIONS'] = ' '.join(extraextensions)
1453 1458
1454 1459 # LOCALIP could be ::1 or 127.0.0.1. Useful for tests that require raw
1455 1460 # IP addresses.
1456 1461 env['LOCALIP'] = _bytes2sys(self._localip())
1457 1462
1458 1463 # This has the same effect as Py_LegacyWindowsStdioFlag in exewrapper.c,
1459 1464 # but this is needed for testing python instances like dummyssh,
1460 1465 # dummysmtpd.py, and dumbhttp.py.
1461 1466 if WINDOWS:
1462 1467 env['PYTHONLEGACYWINDOWSSTDIO'] = '1'
1463 1468
1464 1469 # Modified HOME in test environment can confuse Rust tools. So set
1465 1470 # CARGO_HOME and RUSTUP_HOME automatically if a Rust toolchain is
1466 1471 # present and these variables aren't already defined.
1467 1472 cargo_home_path = os.path.expanduser('~/.cargo')
1468 1473 rustup_home_path = os.path.expanduser('~/.rustup')
1469 1474
1470 1475 if os.path.exists(cargo_home_path) and b'CARGO_HOME' not in osenvironb:
1471 1476 env['CARGO_HOME'] = cargo_home_path
1472 1477 if (
1473 1478 os.path.exists(rustup_home_path)
1474 1479 and b'RUSTUP_HOME' not in osenvironb
1475 1480 ):
1476 1481 env['RUSTUP_HOME'] = rustup_home_path
1477 1482
1478 1483 # Reset some environment variables to well-known values so that
1479 1484 # the tests produce repeatable output.
1480 1485 env['LANG'] = env['LC_ALL'] = env['LANGUAGE'] = 'C'
1481 1486 env['TZ'] = 'GMT'
1482 1487 env["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1483 1488 env['COLUMNS'] = '80'
1484 1489 env['TERM'] = 'xterm'
1485 1490
1486 1491 dropped = [
1487 1492 'CDPATH',
1488 1493 'CHGDEBUG',
1489 1494 'EDITOR',
1490 1495 'GREP_OPTIONS',
1491 1496 'HG',
1492 1497 'HGMERGE',
1493 1498 'HGPLAIN',
1494 1499 'HGPLAINEXCEPT',
1495 1500 'HGPROF',
1496 1501 'http_proxy',
1497 1502 'no_proxy',
1498 1503 'NO_PROXY',
1499 1504 'PAGER',
1500 1505 'VISUAL',
1501 1506 ]
1502 1507
1503 1508 for k in dropped:
1504 1509 if k in env:
1505 1510 del env[k]
1506 1511
1507 1512 # unset env related to hooks
1508 1513 for k in list(env):
1509 1514 if k.startswith('HG_'):
1510 1515 del env[k]
1511 1516
1512 1517 if self._usechg:
1513 1518 env['CHGSOCKNAME'] = os.path.join(self._chgsockdir, b'server')
1514 1519 if self._chgdebug:
1515 1520 env['CHGDEBUG'] = 'true'
1516 1521
1517 1522 return env
1518 1523
1519 1524 def _createhgrc(self, path):
1520 1525 """Create an hgrc file for this test."""
1521 1526 with open(path, 'wb') as hgrc:
1522 1527 hgrc.write(b'[ui]\n')
1523 1528 hgrc.write(b'slash = True\n')
1524 1529 hgrc.write(b'interactive = False\n')
1525 1530 hgrc.write(b'detailed-exit-code = True\n')
1526 1531 hgrc.write(b'merge = internal:merge\n')
1527 1532 hgrc.write(b'mergemarkers = detailed\n')
1528 1533 hgrc.write(b'promptecho = True\n')
1529 1534 dummyssh = os.path.join(self._testdir, b'dummyssh')
1530 1535 hgrc.write(b'ssh = "%s" "%s"\n' % (PYTHON, dummyssh))
1531 1536 hgrc.write(b'timeout.warn=15\n')
1532 1537 hgrc.write(b'[chgserver]\n')
1533 1538 hgrc.write(b'idletimeout=60\n')
1534 1539 hgrc.write(b'[defaults]\n')
1535 1540 hgrc.write(b'[devel]\n')
1536 1541 hgrc.write(b'all-warnings = true\n')
1537 1542 hgrc.write(b'default-date = 0 0\n')
1538 1543 hgrc.write(b'[largefiles]\n')
1539 1544 hgrc.write(
1540 1545 b'usercache = %s\n'
1541 1546 % (os.path.join(self._testtmp, b'.cache/largefiles'))
1542 1547 )
1543 1548 hgrc.write(b'[lfs]\n')
1544 1549 hgrc.write(
1545 1550 b'usercache = %s\n'
1546 1551 % (os.path.join(self._testtmp, b'.cache/lfs'))
1547 1552 )
1548 1553 hgrc.write(b'[web]\n')
1549 1554 hgrc.write(b'address = localhost\n')
1550 1555 hgrc.write(b'ipv6 = %r\n' % self._useipv6)
1551 1556 hgrc.write(b'server-header = testing stub value\n')
1552 1557
1553 1558 for opt in self._extraconfigopts:
1554 1559 section, key = _sys2bytes(opt).split(b'.', 1)
1555 1560 assert b'=' in key, (
1556 1561 'extra config opt %s must ' 'have an = for assignment' % opt
1557 1562 )
1558 1563 hgrc.write(b'[%s]\n%s\n' % (section, key))
1559 1564
1560 1565 def fail(self, msg):
1561 1566 # unittest differentiates between errored and failed.
1562 1567 # Failed is denoted by AssertionError (by default at least).
1563 1568 raise AssertionError(msg)
1564 1569
1565 1570 def _runcommand(self, cmd, env, normalizenewlines=False):
1566 1571 """Run command in a sub-process, capturing the output (stdout and
1567 1572 stderr).
1568 1573
1569 1574 Return a tuple (exitcode, output). output is None in debug mode.
1570 1575 """
1571 1576 if self._debug:
1572 1577 proc = subprocess.Popen(
1573 1578 _bytes2sys(cmd),
1574 1579 shell=True,
1575 1580 close_fds=closefds,
1576 1581 cwd=_bytes2sys(self._testtmp),
1577 1582 env=env,
1578 1583 )
1579 1584 ret = proc.wait()
1580 1585 return (ret, None)
1581 1586
1582 1587 proc = Popen4(cmd, self._testtmp, self._timeout, env)
1583 1588
1584 1589 def cleanup():
1585 1590 terminate(proc)
1586 1591 ret = proc.wait()
1587 1592 if ret == 0:
1588 1593 ret = signal.SIGTERM << 8
1589 1594 killdaemons(env['DAEMON_PIDS'])
1590 1595 return ret
1591 1596
1592 1597 proc.tochild.close()
1593 1598
1594 1599 try:
1595 1600 output = proc.fromchild.read()
1596 1601 except KeyboardInterrupt:
1597 1602 vlog('# Handling keyboard interrupt')
1598 1603 cleanup()
1599 1604 raise
1600 1605
1601 1606 ret = proc.wait()
1602 1607 if wifexited(ret):
1603 1608 ret = os.WEXITSTATUS(ret)
1604 1609
1605 1610 if proc.timeout:
1606 1611 ret = 'timeout'
1607 1612
1608 1613 if ret:
1609 1614 killdaemons(env['DAEMON_PIDS'])
1610 1615
1611 1616 for s, r in self._getreplacements():
1612 1617 output = re.sub(s, r, output)
1613 1618
1614 1619 if normalizenewlines:
1615 1620 output = output.replace(b'\r\n', b'\n')
1616 1621
1617 1622 return ret, output.splitlines(True)
1618 1623
1619 1624
1620 1625 class PythonTest(Test):
1621 1626 """A Python-based test."""
1622 1627
1623 1628 @property
1624 1629 def refpath(self):
1625 1630 return os.path.join(self._testdir, b'%s.out' % self.bname)
1626 1631
1627 1632 def _run(self, env):
1628 1633 # Quote the python(3) executable for Windows
1629 1634 cmd = b'"%s" "%s"' % (PYTHON, self.path)
1630 1635 vlog("# Running", cmd.decode("utf-8"))
1631 1636 result = self._runcommand(cmd, env, normalizenewlines=WINDOWS)
1632 1637 if self._aborted:
1633 1638 raise KeyboardInterrupt()
1634 1639
1635 1640 return result
1636 1641
1637 1642
1638 1643 # Some glob patterns apply only in some circumstances, so the script
1639 1644 # might want to remove (glob) annotations that otherwise should be
1640 1645 # retained.
1641 1646 checkcodeglobpats = [
1642 1647 # On Windows it looks like \ doesn't require a (glob), but we know
1643 1648 # better.
1644 1649 re.compile(br'^pushing to \$TESTTMP/.*[^)]$'),
1645 1650 re.compile(br'^moving \S+/.*[^)]$'),
1646 1651 re.compile(br'^pulling from \$TESTTMP/.*[^)]$'),
1647 1652 # Not all platforms have 127.0.0.1 as loopback (though most do),
1648 1653 # so we always glob that too.
1649 1654 re.compile(br'.*\$LOCALIP.*$'),
1650 1655 ]
1651 1656
1652 1657 bchr = lambda x: bytes([x])
1653 1658
1654 1659 WARN_UNDEFINED = 1
1655 1660 WARN_YES = 2
1656 1661 WARN_NO = 3
1657 1662
1658 1663 MARK_OPTIONAL = b" (?)\n"
1659 1664
1660 1665
1661 1666 def isoptional(line):
1662 1667 return line.endswith(MARK_OPTIONAL)
1663 1668
1664 1669
1665 1670 class TTest(Test):
1666 1671 """A "t test" is a test backed by a .t file."""
1667 1672
1668 1673 SKIPPED_PREFIX = b'skipped: '
1669 1674 FAILED_PREFIX = b'hghave check failed: '
1670 1675 NEEDESCAPE = re.compile(br'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
1671 1676
1672 1677 ESCAPESUB = re.compile(br'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
1673 1678 ESCAPEMAP = {bchr(i): br'\x%02x' % i for i in range(256)}
1674 1679 ESCAPEMAP.update({b'\\': b'\\\\', b'\r': br'\r'})
1675 1680
1676 1681 def __init__(self, path, *args, **kwds):
1677 1682 # accept an extra "case" parameter
1678 1683 case = kwds.pop('case', [])
1679 1684 self._case = case
1680 1685 self._allcases = {x for y in parsettestcases(path) for x in y}
1681 1686 super(TTest, self).__init__(path, *args, **kwds)
1682 1687 if case:
1683 1688 casepath = b'#'.join(case)
1684 1689 self.name = '%s#%s' % (self.name, _bytes2sys(casepath))
1685 1690 self.errpath = b'%s#%s.err' % (self.errpath[:-4], casepath)
1686 1691 self._tmpname += b'-%s' % casepath.replace(b'#', b'-')
1687 1692 self._have = {}
1688 1693
1689 1694 @property
1690 1695 def refpath(self):
1691 1696 return os.path.join(self._testdir, self.bname)
1692 1697
1693 1698 def _run(self, env):
1694 1699 with open(self.path, 'rb') as f:
1695 1700 lines = f.readlines()
1696 1701
1697 1702 # .t file is both reference output and the test input, keep reference
1698 1703 # output updated with the the test input. This avoids some race
1699 1704 # conditions where the reference output does not match the actual test.
1700 1705 if self._refout is not None:
1701 1706 self._refout = lines
1702 1707
1703 1708 salt, script, after, expected = self._parsetest(lines)
1704 1709
1705 1710 # Write out the generated script.
1706 1711 fname = b'%s.sh' % self._testtmp
1707 1712 with open(fname, 'wb') as f:
1708 1713 for l in script:
1709 1714 f.write(l)
1710 1715
1711 1716 cmd = b'%s "%s"' % (self._shell, fname)
1712 1717 vlog("# Running", cmd.decode("utf-8"))
1713 1718
1714 1719 exitcode, output = self._runcommand(cmd, env)
1715 1720
1716 1721 if self._aborted:
1717 1722 raise KeyboardInterrupt()
1718 1723
1719 1724 # Do not merge output if skipped. Return hghave message instead.
1720 1725 # Similarly, with --debug, output is None.
1721 1726 if exitcode == self.SKIPPED_STATUS or output is None:
1722 1727 return exitcode, output
1723 1728
1724 1729 return self._processoutput(exitcode, output, salt, after, expected)
1725 1730
1726 1731 def _hghave(self, reqs):
1727 1732 allreqs = b' '.join(reqs)
1728 1733
1729 1734 self._detectslow(reqs)
1730 1735
1731 1736 if allreqs in self._have:
1732 1737 return self._have.get(allreqs)
1733 1738
1734 1739 # TODO do something smarter when all other uses of hghave are gone.
1735 1740 runtestdir = osenvironb[b'RUNTESTDIR']
1736 1741 tdir = runtestdir.replace(b'\\', b'/')
1737 1742 proc = Popen4(
1738 1743 b'%s -c "%s/hghave %s"' % (self._shell, tdir, allreqs),
1739 1744 self._testtmp,
1740 1745 0,
1741 1746 self._getenv(),
1742 1747 )
1743 1748 stdout, stderr = proc.communicate()
1744 1749 ret = proc.wait()
1745 1750 if wifexited(ret):
1746 1751 ret = os.WEXITSTATUS(ret)
1747 1752 if ret == 2:
1748 1753 print(stdout.decode('utf-8'))
1749 1754 sys.exit(1)
1750 1755
1751 1756 if ret != 0:
1752 1757 self._have[allreqs] = (False, stdout)
1753 1758 return False, stdout
1754 1759
1755 1760 self._have[allreqs] = (True, None)
1756 1761 return True, None
1757 1762
1758 1763 def _detectslow(self, reqs):
1759 1764 """update the timeout of slow test when appropriate"""
1760 1765 if b'slow' in reqs:
1761 1766 self._timeout = self._slowtimeout
1762 1767
1763 1768 def _iftest(self, args):
1764 1769 # implements "#if"
1765 1770 reqs = []
1766 1771 for arg in args:
1767 1772 if arg.startswith(b'no-') and arg[3:] in self._allcases:
1768 1773 if arg[3:] in self._case:
1769 1774 return False
1770 1775 elif arg in self._allcases:
1771 1776 if arg not in self._case:
1772 1777 return False
1773 1778 else:
1774 1779 reqs.append(arg)
1775 1780 self._detectslow(reqs)
1776 1781 return self._hghave(reqs)[0]
1777 1782
1778 1783 def _parsetest(self, lines):
1779 1784 # We generate a shell script which outputs unique markers to line
1780 1785 # up script results with our source. These markers include input
1781 1786 # line number and the last return code.
1782 1787 salt = b"SALT%d" % time.time()
1783 1788
1784 1789 def addsalt(line, inpython):
1785 1790 if inpython:
1786 1791 script.append(b'%s %d 0\n' % (salt, line))
1787 1792 else:
1788 1793 script.append(b'echo %s %d $?\n' % (salt, line))
1789 1794
1790 1795 activetrace = []
1791 1796 session = str(uuid.uuid4()).encode('ascii')
1792 1797 hgcatapult = os.getenv('HGTESTCATAPULTSERVERPIPE') or os.getenv(
1793 1798 'HGCATAPULTSERVERPIPE'
1794 1799 )
1795 1800
1796 1801 def toggletrace(cmd=None):
1797 1802 if not hgcatapult or hgcatapult == os.devnull:
1798 1803 return
1799 1804
1800 1805 if activetrace:
1801 1806 script.append(
1802 1807 b'echo END %s %s >> "$HGTESTCATAPULTSERVERPIPE"\n'
1803 1808 % (session, activetrace[0])
1804 1809 )
1805 1810 if cmd is None:
1806 1811 return
1807 1812
1808 1813 if isinstance(cmd, str):
1809 1814 quoted = shellquote(cmd.strip())
1810 1815 else:
1811 1816 quoted = shellquote(cmd.strip().decode('utf8')).encode('utf8')
1812 1817 quoted = quoted.replace(b'\\', b'\\\\')
1813 1818 script.append(
1814 1819 b'echo START %s %s >> "$HGTESTCATAPULTSERVERPIPE"\n'
1815 1820 % (session, quoted)
1816 1821 )
1817 1822 activetrace[0:] = [quoted]
1818 1823
1819 1824 script = []
1820 1825
1821 1826 # After we run the shell script, we re-unify the script output
1822 1827 # with non-active parts of the source, with synchronization by our
1823 1828 # SALT line number markers. The after table contains the non-active
1824 1829 # components, ordered by line number.
1825 1830 after = {}
1826 1831
1827 1832 # Expected shell script output.
1828 1833 expected = {}
1829 1834
1830 1835 pos = prepos = -1
1831 1836
1832 1837 # True or False when in a true or false conditional section
1833 1838 skipping = None
1834 1839
1835 1840 # We keep track of whether or not we're in a Python block so we
1836 1841 # can generate the surrounding doctest magic.
1837 1842 inpython = False
1838 1843
1839 1844 if self._debug:
1840 1845 script.append(b'set -x\n')
1841 1846 if os.getenv('MSYSTEM'):
1842 1847 script.append(b'alias pwd="pwd -W"\n')
1843 1848
1844 1849 if hgcatapult and hgcatapult != os.devnull:
1845 1850 hgcatapult = hgcatapult.encode('utf8')
1846 1851 cataname = self.name.encode('utf8')
1847 1852
1848 1853 # Kludge: use a while loop to keep the pipe from getting
1849 1854 # closed by our echo commands. The still-running file gets
1850 1855 # reaped at the end of the script, which causes the while
1851 1856 # loop to exit and closes the pipe. Sigh.
1852 1857 script.append(
1853 1858 b'rtendtracing() {\n'
1854 1859 b' echo END %(session)s %(name)s >> %(catapult)s\n'
1855 1860 b' rm -f "$TESTTMP/.still-running"\n'
1856 1861 b'}\n'
1857 1862 b'trap "rtendtracing" 0\n'
1858 1863 b'touch "$TESTTMP/.still-running"\n'
1859 1864 b'while [ -f "$TESTTMP/.still-running" ]; do sleep 1; done '
1860 1865 b'> %(catapult)s &\n'
1861 1866 b'HGCATAPULTSESSION=%(session)s ; export HGCATAPULTSESSION\n'
1862 1867 b'echo START %(session)s %(name)s >> %(catapult)s\n'
1863 1868 % {
1864 1869 b'name': cataname,
1865 1870 b'session': session,
1866 1871 b'catapult': hgcatapult,
1867 1872 }
1868 1873 )
1869 1874
1870 1875 if self._case:
1871 1876 casestr = b'#'.join(self._case)
1872 1877 if isinstance(casestr, str):
1873 1878 quoted = shellquote(casestr)
1874 1879 else:
1875 1880 quoted = shellquote(casestr.decode('utf8')).encode('utf8')
1876 1881 script.append(b'TESTCASE=%s\n' % quoted)
1877 1882 script.append(b'export TESTCASE\n')
1878 1883
1879 1884 n = 0
1880 1885 for n, l in enumerate(lines):
1881 1886 if not l.endswith(b'\n'):
1882 1887 l += b'\n'
1883 1888 if l.startswith(b'#require'):
1884 1889 lsplit = l.split()
1885 1890 if len(lsplit) < 2 or lsplit[0] != b'#require':
1886 1891 after.setdefault(pos, []).append(
1887 1892 b' !!! invalid #require\n'
1888 1893 )
1889 1894 if not skipping:
1890 1895 haveresult, message = self._hghave(lsplit[1:])
1891 1896 if not haveresult:
1892 1897 script = [b'echo "%s"\nexit 80\n' % message]
1893 1898 break
1894 1899 after.setdefault(pos, []).append(l)
1895 1900 elif l.startswith(b'#if'):
1896 1901 lsplit = l.split()
1897 1902 if len(lsplit) < 2 or lsplit[0] != b'#if':
1898 1903 after.setdefault(pos, []).append(b' !!! invalid #if\n')
1899 1904 if skipping is not None:
1900 1905 after.setdefault(pos, []).append(b' !!! nested #if\n')
1901 1906 skipping = not self._iftest(lsplit[1:])
1902 1907 after.setdefault(pos, []).append(l)
1903 1908 elif l.startswith(b'#else'):
1904 1909 if skipping is None:
1905 1910 after.setdefault(pos, []).append(b' !!! missing #if\n')
1906 1911 skipping = not skipping
1907 1912 after.setdefault(pos, []).append(l)
1908 1913 elif l.startswith(b'#endif'):
1909 1914 if skipping is None:
1910 1915 after.setdefault(pos, []).append(b' !!! missing #if\n')
1911 1916 skipping = None
1912 1917 after.setdefault(pos, []).append(l)
1913 1918 elif skipping:
1914 1919 after.setdefault(pos, []).append(l)
1915 1920 elif l.startswith(b' >>> '): # python inlines
1916 1921 after.setdefault(pos, []).append(l)
1917 1922 prepos = pos
1918 1923 pos = n
1919 1924 if not inpython:
1920 1925 # We've just entered a Python block. Add the header.
1921 1926 inpython = True
1922 1927 addsalt(prepos, False) # Make sure we report the exit code.
1923 1928 script.append(b'"%s" -m heredoctest <<EOF\n' % PYTHON)
1924 1929 addsalt(n, True)
1925 1930 script.append(l[2:])
1926 1931 elif l.startswith(b' ... '): # python inlines
1927 1932 after.setdefault(prepos, []).append(l)
1928 1933 script.append(l[2:])
1929 1934 elif l.startswith(b' $ '): # commands
1930 1935 if inpython:
1931 1936 script.append(b'EOF\n')
1932 1937 inpython = False
1933 1938 after.setdefault(pos, []).append(l)
1934 1939 prepos = pos
1935 1940 pos = n
1936 1941 addsalt(n, False)
1937 1942 rawcmd = l[4:]
1938 1943 cmd = rawcmd.split()
1939 1944 toggletrace(rawcmd)
1940 1945 if len(cmd) == 2 and cmd[0] == b'cd':
1941 1946 rawcmd = b'cd %s || exit 1\n' % cmd[1]
1942 1947 script.append(rawcmd)
1943 1948 elif l.startswith(b' > '): # continuations
1944 1949 after.setdefault(prepos, []).append(l)
1945 1950 script.append(l[4:])
1946 1951 elif l.startswith(b' '): # results
1947 1952 # Queue up a list of expected results.
1948 1953 expected.setdefault(pos, []).append(l[2:])
1949 1954 else:
1950 1955 if inpython:
1951 1956 script.append(b'EOF\n')
1952 1957 inpython = False
1953 1958 # Non-command/result. Queue up for merged output.
1954 1959 after.setdefault(pos, []).append(l)
1955 1960
1956 1961 if inpython:
1957 1962 script.append(b'EOF\n')
1958 1963 if skipping is not None:
1959 1964 after.setdefault(pos, []).append(b' !!! missing #endif\n')
1960 1965 addsalt(n + 1, False)
1961 1966 # Need to end any current per-command trace
1962 1967 if activetrace:
1963 1968 toggletrace()
1964 1969 return salt, script, after, expected
1965 1970
1966 1971 def _processoutput(self, exitcode, output, salt, after, expected):
1967 1972 # Merge the script output back into a unified test.
1968 1973 warnonly = WARN_UNDEFINED # 1: not yet; 2: yes; 3: for sure not
1969 1974 if exitcode != 0:
1970 1975 warnonly = WARN_NO
1971 1976
1972 1977 pos = -1
1973 1978 postout = []
1974 1979 for out_rawline in output:
1975 1980 out_line, cmd_line = out_rawline, None
1976 1981 if salt in out_rawline:
1977 1982 out_line, cmd_line = out_rawline.split(salt, 1)
1978 1983
1979 1984 pos, postout, warnonly = self._process_out_line(
1980 1985 out_line, pos, postout, expected, warnonly
1981 1986 )
1982 1987 pos, postout = self._process_cmd_line(cmd_line, pos, postout, after)
1983 1988
1984 1989 if pos in after:
1985 1990 postout += after.pop(pos)
1986 1991
1987 1992 if warnonly == WARN_YES:
1988 1993 exitcode = False # Set exitcode to warned.
1989 1994
1990 1995 return exitcode, postout
1991 1996
1992 1997 def _process_out_line(self, out_line, pos, postout, expected, warnonly):
1993 1998 while out_line:
1994 1999 if not out_line.endswith(b'\n'):
1995 2000 out_line += b' (no-eol)\n'
1996 2001
1997 2002 # Find the expected output at the current position.
1998 2003 els = [None]
1999 2004 if expected.get(pos, None):
2000 2005 els = expected[pos]
2001 2006
2002 2007 optional = []
2003 2008 for i, el in enumerate(els):
2004 2009 r = False
2005 2010 if el:
2006 2011 r, exact = self.linematch(el, out_line)
2007 2012 if isinstance(r, str):
2008 2013 if r == '-glob':
2009 2014 out_line = ''.join(el.rsplit(' (glob)', 1))
2010 2015 r = '' # Warn only this line.
2011 2016 elif r == "retry":
2012 2017 postout.append(b' ' + el)
2013 2018 else:
2014 2019 log('\ninfo, unknown linematch result: %r\n' % r)
2015 2020 r = False
2016 2021 if r:
2017 2022 els.pop(i)
2018 2023 break
2019 2024 if el:
2020 2025 if isoptional(el):
2021 2026 optional.append(i)
2022 2027 else:
2023 2028 m = optline.match(el)
2024 2029 if m:
2025 2030 conditions = [c for c in m.group(2).split(b' ')]
2026 2031
2027 2032 if not self._iftest(conditions):
2028 2033 optional.append(i)
2029 2034 if exact:
2030 2035 # Don't allow line to be matches against a later
2031 2036 # line in the output
2032 2037 els.pop(i)
2033 2038 break
2034 2039
2035 2040 if r:
2036 2041 if r == "retry":
2037 2042 continue
2038 2043 # clean up any optional leftovers
2039 2044 for i in optional:
2040 2045 postout.append(b' ' + els[i])
2041 2046 for i in reversed(optional):
2042 2047 del els[i]
2043 2048 postout.append(b' ' + el)
2044 2049 else:
2045 2050 if self.NEEDESCAPE(out_line):
2046 2051 out_line = TTest._stringescape(
2047 2052 b'%s (esc)\n' % out_line.rstrip(b'\n')
2048 2053 )
2049 2054 postout.append(b' ' + out_line) # Let diff deal with it.
2050 2055 if r != '': # If line failed.
2051 2056 warnonly = WARN_NO
2052 2057 elif warnonly == WARN_UNDEFINED:
2053 2058 warnonly = WARN_YES
2054 2059 break
2055 2060 else:
2056 2061 # clean up any optional leftovers
2057 2062 while expected.get(pos, None):
2058 2063 el = expected[pos].pop(0)
2059 2064 if el:
2060 2065 if not isoptional(el):
2061 2066 m = optline.match(el)
2062 2067 if m:
2063 2068 conditions = [c for c in m.group(2).split(b' ')]
2064 2069
2065 2070 if self._iftest(conditions):
2066 2071 # Don't append as optional line
2067 2072 continue
2068 2073 else:
2069 2074 continue
2070 2075 postout.append(b' ' + el)
2071 2076 return pos, postout, warnonly
2072 2077
2073 2078 def _process_cmd_line(self, cmd_line, pos, postout, after):
2074 2079 """process a "command" part of a line from unified test output"""
2075 2080 if cmd_line:
2076 2081 # Add on last return code.
2077 2082 ret = int(cmd_line.split()[1])
2078 2083 if ret != 0:
2079 2084 postout.append(b' [%d]\n' % ret)
2080 2085 if pos in after:
2081 2086 # Merge in non-active test bits.
2082 2087 postout += after.pop(pos)
2083 2088 pos = int(cmd_line.split()[0])
2084 2089 return pos, postout
2085 2090
2086 2091 @staticmethod
2087 2092 def rematch(el, l):
2088 2093 try:
2089 2094 # parse any flags at the beginning of the regex. Only 'i' is
2090 2095 # supported right now, but this should be easy to extend.
2091 2096 flags, el = re.match(br'^(\(\?i\))?(.*)', el).groups()[0:2]
2092 2097 flags = flags or b''
2093 2098 el = flags + b'(?:' + el + b')'
2094 2099 # use \Z to ensure that the regex matches to the end of the string
2095 2100 if WINDOWS:
2096 2101 return re.match(el + br'\r?\n\Z', l)
2097 2102 return re.match(el + br'\n\Z', l)
2098 2103 except re.error:
2099 2104 # el is an invalid regex
2100 2105 return False
2101 2106
2102 2107 @staticmethod
2103 2108 def globmatch(el, l):
2104 2109 # The only supported special characters are * and ? plus / which also
2105 2110 # matches \ on windows. Escaping of these characters is supported.
2106 2111 if el + b'\n' == l:
2107 2112 if os.altsep:
2108 2113 # matching on "/" is not needed for this line
2109 2114 for pat in checkcodeglobpats:
2110 2115 if pat.match(el):
2111 2116 return True
2112 2117 return b'-glob'
2113 2118 return True
2114 2119 el = el.replace(b'$LOCALIP', b'*')
2115 2120 i, n = 0, len(el)
2116 2121 res = b''
2117 2122 while i < n:
2118 2123 c = el[i : i + 1]
2119 2124 i += 1
2120 2125 if c == b'\\' and i < n and el[i : i + 1] in b'*?\\/':
2121 2126 res += el[i - 1 : i + 1]
2122 2127 i += 1
2123 2128 elif c == b'*':
2124 2129 res += b'.*'
2125 2130 elif c == b'?':
2126 2131 res += b'.'
2127 2132 elif c == b'/' and os.altsep:
2128 2133 res += b'[/\\\\]'
2129 2134 else:
2130 2135 res += re.escape(c)
2131 2136 return TTest.rematch(res, l)
2132 2137
2133 2138 def linematch(self, el, l):
2134 2139 if el == l: # perfect match (fast)
2135 2140 return True, True
2136 2141 retry = False
2137 2142 if isoptional(el):
2138 2143 retry = "retry"
2139 2144 el = el[: -len(MARK_OPTIONAL)] + b"\n"
2140 2145 else:
2141 2146 m = optline.match(el)
2142 2147 if m:
2143 2148 conditions = [c for c in m.group(2).split(b' ')]
2144 2149
2145 2150 el = m.group(1) + b"\n"
2146 2151 if not self._iftest(conditions):
2147 2152 # listed feature missing, should not match
2148 2153 return "retry", False
2149 2154
2150 2155 if el.endswith(b" (esc)\n"):
2151 2156 el = el[:-7].decode('unicode_escape') + '\n'
2152 2157 el = el.encode('latin-1')
2153 2158 if el == l or WINDOWS and el[:-1] + b'\r\n' == l:
2154 2159 return True, True
2155 2160 if el.endswith(b" (re)\n"):
2156 2161 return (TTest.rematch(el[:-6], l) or retry), False
2157 2162 if el.endswith(b" (glob)\n"):
2158 2163 # ignore '(glob)' added to l by 'replacements'
2159 2164 if l.endswith(b" (glob)\n"):
2160 2165 l = l[:-8] + b"\n"
2161 2166 return (TTest.globmatch(el[:-8], l) or retry), False
2162 2167 if os.altsep:
2163 2168 _l = l.replace(b'\\', b'/')
2164 2169 if el == _l or WINDOWS and el[:-1] + b'\r\n' == _l:
2165 2170 return True, True
2166 2171 return retry, True
2167 2172
2168 2173 @staticmethod
2169 2174 def parsehghaveoutput(lines):
2170 2175 """Parse hghave log lines.
2171 2176
2172 2177 Return tuple of lists (missing, failed):
2173 2178 * the missing/unknown features
2174 2179 * the features for which existence check failed"""
2175 2180 missing = []
2176 2181 failed = []
2177 2182 for line in lines:
2178 2183 if line.startswith(TTest.SKIPPED_PREFIX):
2179 2184 line = line.splitlines()[0]
2180 2185 missing.append(_bytes2sys(line[len(TTest.SKIPPED_PREFIX) :]))
2181 2186 elif line.startswith(TTest.FAILED_PREFIX):
2182 2187 line = line.splitlines()[0]
2183 2188 failed.append(_bytes2sys(line[len(TTest.FAILED_PREFIX) :]))
2184 2189
2185 2190 return missing, failed
2186 2191
2187 2192 @staticmethod
2188 2193 def _escapef(m):
2189 2194 return TTest.ESCAPEMAP[m.group(0)]
2190 2195
2191 2196 @staticmethod
2192 2197 def _stringescape(s):
2193 2198 return TTest.ESCAPESUB(TTest._escapef, s)
2194 2199
2195 2200
2196 2201 iolock = threading.RLock()
2197 2202 firstlock = threading.RLock()
2198 2203 firsterror = False
2199 2204
2200 2205 base_class = unittest.TextTestResult
2201 2206
2202 2207
2203 2208 class TestResult(base_class):
2204 2209 """Holds results when executing via unittest."""
2205 2210
2206 2211 def __init__(self, options, *args, **kwargs):
2207 2212 super(TestResult, self).__init__(*args, **kwargs)
2208 2213
2209 2214 self._options = options
2210 2215
2211 2216 # unittest.TestResult didn't have skipped until 2.7. We need to
2212 2217 # polyfill it.
2213 2218 self.skipped = []
2214 2219
2215 2220 # We have a custom "ignored" result that isn't present in any Python
2216 2221 # unittest implementation. It is very similar to skipped. It may make
2217 2222 # sense to map it into skip some day.
2218 2223 self.ignored = []
2219 2224
2220 2225 self.times = []
2221 2226 self._firststarttime = None
2222 2227 # Data stored for the benefit of generating xunit reports.
2223 2228 self.successes = []
2224 2229 self.faildata = {}
2225 2230
2226 2231 if options.color == 'auto':
2227 2232 isatty = self.stream.isatty()
2228 2233 # For some reason, redirecting stdout on Windows disables the ANSI
2229 2234 # color processing of stderr, which is what is used to print the
2230 2235 # output. Therefore, both must be tty on Windows to enable color.
2231 2236 if WINDOWS:
2232 2237 isatty = isatty and sys.stdout.isatty()
2233 2238 self.color = pygmentspresent and isatty
2234 2239 elif options.color == 'never':
2235 2240 self.color = False
2236 2241 else: # 'always', for testing purposes
2237 2242 self.color = pygmentspresent
2238 2243
2239 2244 def onStart(self, test):
2240 2245 """Can be overriden by custom TestResult"""
2241 2246
2242 2247 def onEnd(self):
2243 2248 """Can be overriden by custom TestResult"""
2244 2249
2245 2250 def addFailure(self, test, reason):
2246 2251 self.failures.append((test, reason))
2247 2252
2248 2253 if self._options.first:
2249 2254 self.stop()
2250 2255 else:
2251 2256 with iolock:
2252 2257 if reason == "timed out":
2253 2258 self.stream.write('t')
2254 2259 else:
2255 2260 if not self._options.nodiff:
2256 2261 self.stream.write('\n')
2257 2262 # Exclude the '\n' from highlighting to lex correctly
2258 2263 formatted = 'ERROR: %s output changed\n' % test
2259 2264 self.stream.write(highlightmsg(formatted, self.color))
2260 2265 self.stream.write('!')
2261 2266
2262 2267 self.stream.flush()
2263 2268
2264 2269 def addSuccess(self, test):
2265 2270 with iolock:
2266 2271 super(TestResult, self).addSuccess(test)
2267 2272 self.successes.append(test)
2268 2273
2269 2274 def addError(self, test, err):
2270 2275 super(TestResult, self).addError(test, err)
2271 2276 if self._options.first:
2272 2277 self.stop()
2273 2278
2274 2279 # Polyfill.
2275 2280 def addSkip(self, test, reason):
2276 2281 self.skipped.append((test, reason))
2277 2282 with iolock:
2278 2283 if self.showAll:
2279 2284 self.stream.writeln('skipped %s' % reason)
2280 2285 else:
2281 2286 self.stream.write('s')
2282 2287 self.stream.flush()
2283 2288
2284 2289 def addIgnore(self, test, reason):
2285 2290 self.ignored.append((test, reason))
2286 2291 with iolock:
2287 2292 if self.showAll:
2288 2293 self.stream.writeln('ignored %s' % reason)
2289 2294 else:
2290 2295 if reason not in ('not retesting', "doesn't match keyword"):
2291 2296 self.stream.write('i')
2292 2297 else:
2293 2298 self.testsRun += 1
2294 2299 self.stream.flush()
2295 2300
2296 2301 def addOutputMismatch(self, test, ret, got, expected):
2297 2302 """Record a mismatch in test output for a particular test."""
2298 2303 if self.shouldStop or firsterror:
2299 2304 # don't print, some other test case already failed and
2300 2305 # printed, we're just stale and probably failed due to our
2301 2306 # temp dir getting cleaned up.
2302 2307 return
2303 2308
2304 2309 accepted = False
2305 2310 lines = []
2306 2311
2307 2312 with iolock:
2308 2313 if self._options.nodiff:
2309 2314 pass
2310 2315 elif self._options.view:
2311 2316 v = self._options.view
2312 2317 subprocess.call(
2313 2318 r'"%s" "%s" "%s"'
2314 2319 % (v, _bytes2sys(test.refpath), _bytes2sys(test.errpath)),
2315 2320 shell=True,
2316 2321 )
2317 2322 else:
2318 2323 servefail, lines = getdiff(
2319 2324 expected, got, test.refpath, test.errpath
2320 2325 )
2321 2326 self.stream.write('\n')
2322 2327 for line in lines:
2323 2328 line = highlightdiff(line, self.color)
2324 2329 self.stream.flush()
2325 2330 self.stream.buffer.write(line)
2326 2331 self.stream.buffer.flush()
2327 2332
2328 2333 if servefail:
2329 2334 raise test.failureException(
2330 2335 'server failed to start (HGPORT=%s)' % test._startport
2331 2336 )
2332 2337
2333 2338 # handle interactive prompt without releasing iolock
2334 2339 if self._options.interactive:
2335 2340 if test.readrefout() != expected:
2336 2341 self.stream.write(
2337 2342 'Reference output has changed (run again to prompt '
2338 2343 'changes)'
2339 2344 )
2340 2345 else:
2341 2346 self.stream.write('Accept this change? [y/N] ')
2342 2347 self.stream.flush()
2343 2348 answer = sys.stdin.readline().strip()
2344 2349 if answer.lower() in ('y', 'yes'):
2345 2350 if test.path.endswith(b'.t'):
2346 2351 rename(test.errpath, test.path)
2347 2352 else:
2348 2353 rename(test.errpath, b'%s.out' % test.path)
2349 2354 accepted = True
2350 2355 if not accepted:
2351 2356 self.faildata[test.name] = b''.join(lines)
2352 2357
2353 2358 return accepted
2354 2359
2355 2360 def startTest(self, test):
2356 2361 super(TestResult, self).startTest(test)
2357 2362
2358 2363 # os.times module computes the user time and system time spent by
2359 2364 # child's processes along with real elapsed time taken by a process.
2360 2365 # This module has one limitation. It can only work for Linux user
2361 2366 # and not for Windows. Hence why we fall back to another function
2362 2367 # for wall time calculations.
2363 2368 test.started_times = os.times()
2364 2369 # TODO use a monotonic clock once support for Python 2.7 is dropped.
2365 2370 test.started_time = time.time()
2366 2371 if self._firststarttime is None: # thread racy but irrelevant
2367 2372 self._firststarttime = test.started_time
2368 2373
2369 2374 def stopTest(self, test, interrupted=False):
2370 2375 super(TestResult, self).stopTest(test)
2371 2376
2372 2377 test.stopped_times = os.times()
2373 2378 stopped_time = time.time()
2374 2379
2375 2380 starttime = test.started_times
2376 2381 endtime = test.stopped_times
2377 2382 origin = self._firststarttime
2378 2383 self.times.append(
2379 2384 (
2380 2385 test.name,
2381 2386 endtime[2] - starttime[2], # user space CPU time
2382 2387 endtime[3] - starttime[3], # sys space CPU time
2383 2388 stopped_time - test.started_time, # real time
2384 2389 test.started_time - origin, # start date in run context
2385 2390 stopped_time - origin, # end date in run context
2386 2391 )
2387 2392 )
2388 2393
2389 2394 if interrupted:
2390 2395 with iolock:
2391 2396 self.stream.writeln(
2392 2397 'INTERRUPTED: %s (after %d seconds)'
2393 2398 % (test.name, self.times[-1][3])
2394 2399 )
2395 2400
2396 2401
2397 2402 def getTestResult():
2398 2403 """
2399 2404 Returns the relevant test result
2400 2405 """
2401 2406 if "CUSTOM_TEST_RESULT" in os.environ:
2402 2407 testresultmodule = __import__(os.environ["CUSTOM_TEST_RESULT"])
2403 2408 return testresultmodule.TestResult
2404 2409 else:
2405 2410 return TestResult
2406 2411
2407 2412
2408 2413 class TestSuite(unittest.TestSuite):
2409 2414 """Custom unittest TestSuite that knows how to execute Mercurial tests."""
2410 2415
2411 2416 def __init__(
2412 2417 self,
2413 2418 testdir,
2414 2419 jobs=1,
2415 2420 whitelist=None,
2416 2421 blacklist=None,
2417 2422 keywords=None,
2418 2423 loop=False,
2419 2424 runs_per_test=1,
2420 2425 loadtest=None,
2421 2426 showchannels=False,
2422 2427 *args,
2423 2428 **kwargs
2424 2429 ):
2425 2430 """Create a new instance that can run tests with a configuration.
2426 2431
2427 2432 testdir specifies the directory where tests are executed from. This
2428 2433 is typically the ``tests`` directory from Mercurial's source
2429 2434 repository.
2430 2435
2431 2436 jobs specifies the number of jobs to run concurrently. Each test
2432 2437 executes on its own thread. Tests actually spawn new processes, so
2433 2438 state mutation should not be an issue.
2434 2439
2435 2440 If there is only one job, it will use the main thread.
2436 2441
2437 2442 whitelist and blacklist denote tests that have been whitelisted and
2438 2443 blacklisted, respectively. These arguments don't belong in TestSuite.
2439 2444 Instead, whitelist and blacklist should be handled by the thing that
2440 2445 populates the TestSuite with tests. They are present to preserve
2441 2446 backwards compatible behavior which reports skipped tests as part
2442 2447 of the results.
2443 2448
2444 2449 keywords denotes key words that will be used to filter which tests
2445 2450 to execute. This arguably belongs outside of TestSuite.
2446 2451
2447 2452 loop denotes whether to loop over tests forever.
2448 2453 """
2449 2454 super(TestSuite, self).__init__(*args, **kwargs)
2450 2455
2451 2456 self._jobs = jobs
2452 2457 self._whitelist = whitelist
2453 2458 self._blacklist = blacklist
2454 2459 self._keywords = keywords
2455 2460 self._loop = loop
2456 2461 self._runs_per_test = runs_per_test
2457 2462 self._loadtest = loadtest
2458 2463 self._showchannels = showchannels
2459 2464
2460 2465 def run(self, result):
2461 2466 # We have a number of filters that need to be applied. We do this
2462 2467 # here instead of inside Test because it makes the running logic for
2463 2468 # Test simpler.
2464 2469 tests = []
2465 2470 num_tests = [0]
2466 2471 for test in self._tests:
2467 2472
2468 2473 def get():
2469 2474 num_tests[0] += 1
2470 2475 if getattr(test, 'should_reload', False):
2471 2476 return self._loadtest(test, num_tests[0])
2472 2477 return test
2473 2478
2474 2479 if not os.path.exists(test.path):
2475 2480 result.addSkip(test, "Doesn't exist")
2476 2481 continue
2477 2482
2478 2483 is_whitelisted = self._whitelist and (
2479 2484 test.relpath in self._whitelist or test.bname in self._whitelist
2480 2485 )
2481 2486 if not is_whitelisted:
2482 2487 is_blacklisted = self._blacklist and (
2483 2488 test.relpath in self._blacklist
2484 2489 or test.bname in self._blacklist
2485 2490 )
2486 2491 if is_blacklisted:
2487 2492 result.addSkip(test, 'blacklisted')
2488 2493 continue
2489 2494 if self._keywords:
2490 2495 with open(test.path, 'rb') as f:
2491 2496 t = f.read().lower() + test.bname.lower()
2492 2497 ignored = False
2493 2498 for k in self._keywords.lower().split():
2494 2499 if k not in t:
2495 2500 result.addIgnore(test, "doesn't match keyword")
2496 2501 ignored = True
2497 2502 break
2498 2503
2499 2504 if ignored:
2500 2505 continue
2501 2506 for _ in xrange(self._runs_per_test):
2502 2507 tests.append(get())
2503 2508
2504 2509 runtests = list(tests)
2505 2510 done = queue.Queue()
2506 2511 running = 0
2507 2512
2508 2513 channels_lock = threading.Lock()
2509 2514 channels = [""] * self._jobs
2510 2515
2511 2516 def job(test, result):
2512 2517 with channels_lock:
2513 2518 for n, v in enumerate(channels):
2514 2519 if not v:
2515 2520 channel = n
2516 2521 break
2517 2522 else:
2518 2523 raise ValueError('Could not find output channel')
2519 2524 channels[channel] = "=" + test.name[5:].split(".")[0]
2520 2525
2521 2526 r = None
2522 2527 try:
2523 2528 test(result)
2524 2529 except KeyboardInterrupt:
2525 2530 pass
2526 2531 except: # re-raises
2527 2532 r = ('!', test, 'run-test raised an error, see traceback')
2528 2533 raise
2529 2534 finally:
2530 2535 try:
2531 2536 channels[channel] = ''
2532 2537 except IndexError:
2533 2538 pass
2534 2539 done.put(r)
2535 2540
2536 2541 def stat():
2537 2542 count = 0
2538 2543 while channels:
2539 2544 d = '\n%03s ' % count
2540 2545 for n, v in enumerate(channels):
2541 2546 if v:
2542 2547 d += v[0]
2543 2548 channels[n] = v[1:] or '.'
2544 2549 else:
2545 2550 d += ' '
2546 2551 d += ' '
2547 2552 with iolock:
2548 2553 sys.stdout.write(d + ' ')
2549 2554 sys.stdout.flush()
2550 2555 for x in xrange(10):
2551 2556 if channels:
2552 2557 time.sleep(0.1)
2553 2558 count += 1
2554 2559
2555 2560 stoppedearly = False
2556 2561
2557 2562 if self._showchannels:
2558 2563 statthread = threading.Thread(target=stat, name="stat")
2559 2564 statthread.start()
2560 2565
2561 2566 try:
2562 2567 while tests or running:
2563 2568 if not done.empty() or running == self._jobs or not tests:
2564 2569 try:
2565 2570 done.get(True, 1)
2566 2571 running -= 1
2567 2572 if result and result.shouldStop:
2568 2573 stoppedearly = True
2569 2574 break
2570 2575 except queue.Empty:
2571 2576 continue
2572 2577 if tests and not running == self._jobs:
2573 2578 test = tests.pop(0)
2574 2579 if self._loop:
2575 2580 if getattr(test, 'should_reload', False):
2576 2581 num_tests[0] += 1
2577 2582 tests.append(self._loadtest(test, num_tests[0]))
2578 2583 else:
2579 2584 tests.append(test)
2580 2585 if self._jobs == 1:
2581 2586 job(test, result)
2582 2587 else:
2583 2588 t = threading.Thread(
2584 2589 target=job, name=test.name, args=(test, result)
2585 2590 )
2586 2591 t.start()
2587 2592 running += 1
2588 2593
2589 2594 # If we stop early we still need to wait on started tests to
2590 2595 # finish. Otherwise, there is a race between the test completing
2591 2596 # and the test's cleanup code running. This could result in the
2592 2597 # test reporting incorrect.
2593 2598 if stoppedearly:
2594 2599 while running:
2595 2600 try:
2596 2601 done.get(True, 1)
2597 2602 running -= 1
2598 2603 except queue.Empty:
2599 2604 continue
2600 2605 except KeyboardInterrupt:
2601 2606 for test in runtests:
2602 2607 test.abort()
2603 2608
2604 2609 channels = []
2605 2610
2606 2611 return result
2607 2612
2608 2613
2609 2614 # Save the most recent 5 wall-clock runtimes of each test to a
2610 2615 # human-readable text file named .testtimes. Tests are sorted
2611 2616 # alphabetically, while times for each test are listed from oldest to
2612 2617 # newest.
2613 2618
2614 2619
2615 2620 def loadtimes(outputdir):
2616 2621 times = []
2617 2622 try:
2618 2623 with open(os.path.join(outputdir, b'.testtimes')) as fp:
2619 2624 for line in fp:
2620 2625 m = re.match('(.*?) ([0-9. ]+)', line)
2621 2626 times.append(
2622 2627 (m.group(1), [float(t) for t in m.group(2).split()])
2623 2628 )
2624 2629 except IOError as err:
2625 2630 if err.errno != errno.ENOENT:
2626 2631 raise
2627 2632 return times
2628 2633
2629 2634
2630 2635 def savetimes(outputdir, result):
2631 2636 saved = dict(loadtimes(outputdir))
2632 2637 maxruns = 5
2633 2638 skipped = {str(t[0]) for t in result.skipped}
2634 2639 for tdata in result.times:
2635 2640 test, real = tdata[0], tdata[3]
2636 2641 if test not in skipped:
2637 2642 ts = saved.setdefault(test, [])
2638 2643 ts.append(real)
2639 2644 ts[:] = ts[-maxruns:]
2640 2645
2641 2646 fd, tmpname = tempfile.mkstemp(
2642 2647 prefix=b'.testtimes', dir=outputdir, text=True
2643 2648 )
2644 2649 with os.fdopen(fd, 'w') as fp:
2645 2650 for name, ts in sorted(saved.items()):
2646 2651 fp.write('%s %s\n' % (name, ' '.join(['%.3f' % (t,) for t in ts])))
2647 2652 timepath = os.path.join(outputdir, b'.testtimes')
2648 2653 try:
2649 2654 os.unlink(timepath)
2650 2655 except OSError:
2651 2656 pass
2652 2657 try:
2653 2658 os.rename(tmpname, timepath)
2654 2659 except OSError:
2655 2660 pass
2656 2661
2657 2662
2658 2663 class TextTestRunner(unittest.TextTestRunner):
2659 2664 """Custom unittest test runner that uses appropriate settings."""
2660 2665
2661 2666 def __init__(self, runner, *args, **kwargs):
2662 2667 super(TextTestRunner, self).__init__(*args, **kwargs)
2663 2668
2664 2669 self._runner = runner
2665 2670
2666 2671 self._result = getTestResult()(
2667 2672 self._runner.options, self.stream, self.descriptions, self.verbosity
2668 2673 )
2669 2674
2670 2675 def listtests(self, test):
2671 2676 test = sorted(test, key=lambda t: t.name)
2672 2677
2673 2678 self._result.onStart(test)
2674 2679
2675 2680 for t in test:
2676 2681 print(t.name)
2677 2682 self._result.addSuccess(t)
2678 2683
2679 2684 if self._runner.options.xunit:
2680 2685 with open(self._runner.options.xunit, "wb") as xuf:
2681 2686 self._writexunit(self._result, xuf)
2682 2687
2683 2688 if self._runner.options.json:
2684 2689 jsonpath = os.path.join(self._runner._outputdir, b'report.json')
2685 2690 with open(jsonpath, 'w') as fp:
2686 2691 self._writejson(self._result, fp)
2687 2692
2688 2693 return self._result
2689 2694
2690 2695 def run(self, test):
2691 2696 self._result.onStart(test)
2692 2697 test(self._result)
2693 2698
2694 2699 failed = len(self._result.failures)
2695 2700 skipped = len(self._result.skipped)
2696 2701 ignored = len(self._result.ignored)
2697 2702
2698 2703 with iolock:
2699 2704 self.stream.writeln('')
2700 2705
2701 2706 if not self._runner.options.noskips:
2702 2707 for test, msg in sorted(
2703 2708 self._result.skipped, key=lambda s: s[0].name
2704 2709 ):
2705 2710 formatted = 'Skipped %s: %s\n' % (test.name, msg)
2706 2711 msg = highlightmsg(formatted, self._result.color)
2707 2712 self.stream.write(msg)
2708 2713 for test, msg in sorted(
2709 2714 self._result.failures, key=lambda f: f[0].name
2710 2715 ):
2711 2716 formatted = 'Failed %s: %s\n' % (test.name, msg)
2712 2717 self.stream.write(highlightmsg(formatted, self._result.color))
2713 2718 for test, msg in sorted(
2714 2719 self._result.errors, key=lambda e: e[0].name
2715 2720 ):
2716 2721 self.stream.writeln('Errored %s: %s' % (test.name, msg))
2717 2722
2718 2723 if self._runner.options.xunit:
2719 2724 with open(self._runner.options.xunit, "wb") as xuf:
2720 2725 self._writexunit(self._result, xuf)
2721 2726
2722 2727 if self._runner.options.json:
2723 2728 jsonpath = os.path.join(self._runner._outputdir, b'report.json')
2724 2729 with open(jsonpath, 'w') as fp:
2725 2730 self._writejson(self._result, fp)
2726 2731
2727 2732 self._runner._checkhglib('Tested')
2728 2733
2729 2734 savetimes(self._runner._outputdir, self._result)
2730 2735
2731 2736 if failed and self._runner.options.known_good_rev:
2732 2737 self._bisecttests(t for t, m in self._result.failures)
2733 2738 self.stream.writeln(
2734 2739 '# Ran %d tests, %d skipped, %d failed.'
2735 2740 % (self._result.testsRun, skipped + ignored, failed)
2736 2741 )
2737 2742 if failed:
2738 2743 self.stream.writeln(
2739 2744 'python hash seed: %s' % os.environ['PYTHONHASHSEED']
2740 2745 )
2741 2746 if self._runner.options.time:
2742 2747 self.printtimes(self._result.times)
2743 2748
2744 2749 if self._runner.options.exceptions:
2745 2750 exceptions = aggregateexceptions(
2746 2751 os.path.join(self._runner._outputdir, b'exceptions')
2747 2752 )
2748 2753
2749 2754 self.stream.writeln('Exceptions Report:')
2750 2755 self.stream.writeln(
2751 2756 '%d total from %d frames'
2752 2757 % (exceptions['total'], len(exceptions['exceptioncounts']))
2753 2758 )
2754 2759 combined = exceptions['combined']
2755 2760 for key in sorted(combined, key=combined.get, reverse=True):
2756 2761 frame, line, exc = key
2757 2762 totalcount, testcount, leastcount, leasttest = combined[key]
2758 2763
2759 2764 self.stream.writeln(
2760 2765 '%d (%d tests)\t%s: %s (%s - %d total)'
2761 2766 % (
2762 2767 totalcount,
2763 2768 testcount,
2764 2769 frame,
2765 2770 exc,
2766 2771 leasttest,
2767 2772 leastcount,
2768 2773 )
2769 2774 )
2770 2775
2771 2776 self.stream.flush()
2772 2777
2773 2778 return self._result
2774 2779
2775 2780 def _bisecttests(self, tests):
2776 2781 bisectcmd = ['hg', 'bisect']
2777 2782 bisectrepo = self._runner.options.bisect_repo
2778 2783 if bisectrepo:
2779 2784 bisectcmd.extend(['-R', os.path.abspath(bisectrepo)])
2780 2785
2781 2786 def pread(args):
2782 2787 env = os.environ.copy()
2783 2788 env['HGPLAIN'] = '1'
2784 2789 p = subprocess.Popen(
2785 2790 args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=env
2786 2791 )
2787 2792 data = p.stdout.read()
2788 2793 p.wait()
2789 2794 return data
2790 2795
2791 2796 for test in tests:
2792 2797 pread(bisectcmd + ['--reset']),
2793 2798 pread(bisectcmd + ['--bad', '.'])
2794 2799 pread(bisectcmd + ['--good', self._runner.options.known_good_rev])
2795 2800 # TODO: we probably need to forward more options
2796 2801 # that alter hg's behavior inside the tests.
2797 2802 opts = ''
2798 2803 withhg = self._runner.options.with_hg
2799 2804 if withhg:
2800 2805 opts += ' --with-hg=%s ' % shellquote(_bytes2sys(withhg))
2801 2806 rtc = '%s %s %s %s' % (sysexecutable, sys.argv[0], opts, test)
2802 2807 data = pread(bisectcmd + ['--command', rtc])
2803 2808 m = re.search(
2804 2809 (
2805 2810 br'\nThe first (?P<goodbad>bad|good) revision '
2806 2811 br'is:\nchangeset: +\d+:(?P<node>[a-f0-9]+)\n.*\n'
2807 2812 br'summary: +(?P<summary>[^\n]+)\n'
2808 2813 ),
2809 2814 data,
2810 2815 (re.MULTILINE | re.DOTALL),
2811 2816 )
2812 2817 if m is None:
2813 2818 self.stream.writeln(
2814 2819 'Failed to identify failure point for %s' % test
2815 2820 )
2816 2821 continue
2817 2822 dat = m.groupdict()
2818 2823 verb = 'broken' if dat['goodbad'] == b'bad' else 'fixed'
2819 2824 self.stream.writeln(
2820 2825 '%s %s by %s (%s)'
2821 2826 % (
2822 2827 test,
2823 2828 verb,
2824 2829 dat['node'].decode('ascii'),
2825 2830 dat['summary'].decode('utf8', 'ignore'),
2826 2831 )
2827 2832 )
2828 2833
2829 2834 def printtimes(self, times):
2830 2835 # iolock held by run
2831 2836 self.stream.writeln('# Producing time report')
2832 2837 times.sort(key=lambda t: (t[3]))
2833 2838 cols = '%7.3f %7.3f %7.3f %7.3f %7.3f %s'
2834 2839 self.stream.writeln(
2835 2840 '%-7s %-7s %-7s %-7s %-7s %s'
2836 2841 % ('start', 'end', 'cuser', 'csys', 'real', 'Test')
2837 2842 )
2838 2843 for tdata in times:
2839 2844 test = tdata[0]
2840 2845 cuser, csys, real, start, end = tdata[1:6]
2841 2846 self.stream.writeln(cols % (start, end, cuser, csys, real, test))
2842 2847
2843 2848 @staticmethod
2844 2849 def _writexunit(result, outf):
2845 2850 # See http://llg.cubic.org/docs/junit/ for a reference.
2846 2851 timesd = {t[0]: t[3] for t in result.times}
2847 2852 doc = minidom.Document()
2848 2853 s = doc.createElement('testsuite')
2849 2854 s.setAttribute('errors', "0") # TODO
2850 2855 s.setAttribute('failures', str(len(result.failures)))
2851 2856 s.setAttribute('name', 'run-tests')
2852 2857 s.setAttribute(
2853 2858 'skipped', str(len(result.skipped) + len(result.ignored))
2854 2859 )
2855 2860 s.setAttribute('tests', str(result.testsRun))
2856 2861 doc.appendChild(s)
2857 2862 for tc in result.successes:
2858 2863 t = doc.createElement('testcase')
2859 2864 t.setAttribute('name', tc.name)
2860 2865 tctime = timesd.get(tc.name)
2861 2866 if tctime is not None:
2862 2867 t.setAttribute('time', '%.3f' % tctime)
2863 2868 s.appendChild(t)
2864 2869 for tc, err in sorted(result.faildata.items()):
2865 2870 t = doc.createElement('testcase')
2866 2871 t.setAttribute('name', tc)
2867 2872 tctime = timesd.get(tc)
2868 2873 if tctime is not None:
2869 2874 t.setAttribute('time', '%.3f' % tctime)
2870 2875 # createCDATASection expects a unicode or it will
2871 2876 # convert using default conversion rules, which will
2872 2877 # fail if string isn't ASCII.
2873 2878 err = cdatasafe(err).decode('utf-8', 'replace')
2874 2879 cd = doc.createCDATASection(err)
2875 2880 # Use 'failure' here instead of 'error' to match errors = 0,
2876 2881 # failures = len(result.failures) in the testsuite element.
2877 2882 failelem = doc.createElement('failure')
2878 2883 failelem.setAttribute('message', 'output changed')
2879 2884 failelem.setAttribute('type', 'output-mismatch')
2880 2885 failelem.appendChild(cd)
2881 2886 t.appendChild(failelem)
2882 2887 s.appendChild(t)
2883 2888 for tc, message in result.skipped:
2884 2889 # According to the schema, 'skipped' has no attributes. So store
2885 2890 # the skip message as a text node instead.
2886 2891 t = doc.createElement('testcase')
2887 2892 t.setAttribute('name', tc.name)
2888 2893 binmessage = message.encode('utf-8')
2889 2894 message = cdatasafe(binmessage).decode('utf-8', 'replace')
2890 2895 cd = doc.createCDATASection(message)
2891 2896 skipelem = doc.createElement('skipped')
2892 2897 skipelem.appendChild(cd)
2893 2898 t.appendChild(skipelem)
2894 2899 s.appendChild(t)
2895 2900 outf.write(doc.toprettyxml(indent=' ', encoding='utf-8'))
2896 2901
2897 2902 @staticmethod
2898 2903 def _writejson(result, outf):
2899 2904 timesd = {}
2900 2905 for tdata in result.times:
2901 2906 test = tdata[0]
2902 2907 timesd[test] = tdata[1:]
2903 2908
2904 2909 outcome = {}
2905 2910 groups = [
2906 2911 ('success', ((tc, None) for tc in result.successes)),
2907 2912 ('failure', result.failures),
2908 2913 ('skip', result.skipped),
2909 2914 ]
2910 2915 for res, testcases in groups:
2911 2916 for tc, __ in testcases:
2912 2917 if tc.name in timesd:
2913 2918 diff = result.faildata.get(tc.name, b'')
2914 2919 try:
2915 2920 diff = diff.decode('unicode_escape')
2916 2921 except UnicodeDecodeError as e:
2917 2922 diff = '%r decoding diff, sorry' % e
2918 2923 tres = {
2919 2924 'result': res,
2920 2925 'time': ('%0.3f' % timesd[tc.name][2]),
2921 2926 'cuser': ('%0.3f' % timesd[tc.name][0]),
2922 2927 'csys': ('%0.3f' % timesd[tc.name][1]),
2923 2928 'start': ('%0.3f' % timesd[tc.name][3]),
2924 2929 'end': ('%0.3f' % timesd[tc.name][4]),
2925 2930 'diff': diff,
2926 2931 }
2927 2932 else:
2928 2933 # blacklisted test
2929 2934 tres = {'result': res}
2930 2935
2931 2936 outcome[tc.name] = tres
2932 2937 jsonout = json.dumps(
2933 2938 outcome, sort_keys=True, indent=4, separators=(',', ': ')
2934 2939 )
2935 2940 outf.writelines(("testreport =", jsonout))
2936 2941
2937 2942
2938 2943 def sorttests(testdescs, previoustimes, shuffle=False):
2939 2944 """Do an in-place sort of tests."""
2940 2945 if shuffle:
2941 2946 random.shuffle(testdescs)
2942 2947 return
2943 2948
2944 2949 if previoustimes:
2945 2950
2946 2951 def sortkey(f):
2947 2952 f = f['path']
2948 2953 if f in previoustimes:
2949 2954 # Use most recent time as estimate
2950 2955 return -(previoustimes[f][-1])
2951 2956 else:
2952 2957 # Default to a rather arbitrary value of 1 second for new tests
2953 2958 return -1.0
2954 2959
2955 2960 else:
2956 2961 # keywords for slow tests
2957 2962 slow = {
2958 2963 b'svn': 10,
2959 2964 b'cvs': 10,
2960 2965 b'hghave': 10,
2961 2966 b'largefiles-update': 10,
2962 2967 b'run-tests': 10,
2963 2968 b'corruption': 10,
2964 2969 b'race': 10,
2965 2970 b'i18n': 10,
2966 2971 b'check': 100,
2967 2972 b'gendoc': 100,
2968 2973 b'contrib-perf': 200,
2969 2974 b'merge-combination': 100,
2970 2975 }
2971 2976 perf = {}
2972 2977
2973 2978 def sortkey(f):
2974 2979 # run largest tests first, as they tend to take the longest
2975 2980 f = f['path']
2976 2981 try:
2977 2982 return perf[f]
2978 2983 except KeyError:
2979 2984 try:
2980 2985 val = -os.stat(f).st_size
2981 2986 except OSError as e:
2982 2987 if e.errno != errno.ENOENT:
2983 2988 raise
2984 2989 perf[f] = -1e9 # file does not exist, tell early
2985 2990 return -1e9
2986 2991 for kw, mul in slow.items():
2987 2992 if kw in f:
2988 2993 val *= mul
2989 2994 if f.endswith(b'.py'):
2990 2995 val /= 10.0
2991 2996 perf[f] = val / 1000.0
2992 2997 return perf[f]
2993 2998
2994 2999 testdescs.sort(key=sortkey)
2995 3000
2996 3001
2997 3002 class TestRunner:
2998 3003 """Holds context for executing tests.
2999 3004
3000 3005 Tests rely on a lot of state. This object holds it for them.
3001 3006 """
3002 3007
3003 3008 # Programs required to run tests.
3004 3009 REQUIREDTOOLS = [
3005 3010 b'diff',
3006 3011 b'grep',
3007 3012 b'unzip',
3008 3013 b'gunzip',
3009 3014 b'bunzip2',
3010 3015 b'sed',
3011 3016 ]
3012 3017
3013 3018 # Maps file extensions to test class.
3014 3019 TESTTYPES = [
3015 3020 (b'.py', PythonTest),
3016 3021 (b'.t', TTest),
3017 3022 ]
3018 3023
3019 3024 def __init__(self):
3020 3025 self.options = None
3021 3026 self._hgroot = None
3022 3027 self._testdir = None
3023 3028 self._outputdir = None
3024 3029 self._hgtmp = None
3025 3030 self._installdir = None
3026 3031 self._bindir = None
3027 3032 # a place for run-tests.py to generate executable it needs
3028 3033 self._custom_bin_dir = None
3029 3034 self._pythondir = None
3030 3035 # True if we had to infer the pythondir from --with-hg
3031 3036 self._pythondir_inferred = False
3032 3037 self._coveragefile = None
3033 3038 self._createdfiles = []
3034 3039 self._hgcommand = None
3035 3040 self._hgpath = None
3036 3041 self._portoffset = 0
3037 3042 self._ports = {}
3038 3043
3039 3044 def run(self, args, parser=None):
3040 3045 """Run the test suite."""
3041 3046 oldmask = os.umask(0o22)
3042 3047 try:
3043 3048 parser = parser or getparser()
3044 3049 options = parseargs(args, parser)
3045 3050 tests = [_sys2bytes(a) for a in options.tests]
3046 3051 if options.test_list is not None:
3047 3052 for listfile in options.test_list:
3048 3053 with open(listfile, 'rb') as f:
3049 3054 tests.extend(t for t in f.read().splitlines() if t)
3050 3055 self.options = options
3051 3056
3052 3057 self._checktools()
3053 3058 testdescs = self.findtests(tests)
3054 3059 if options.profile_runner:
3055 3060 import statprof
3056 3061
3057 3062 statprof.start()
3058 3063 result = self._run(testdescs)
3059 3064 if options.profile_runner:
3060 3065 statprof.stop()
3061 3066 statprof.display()
3062 3067 return result
3063 3068
3064 3069 finally:
3065 3070 os.umask(oldmask)
3066 3071
3067 3072 def _run(self, testdescs):
3068 3073 testdir = getcwdb()
3069 3074 # assume all tests in same folder for now
3070 3075 if testdescs:
3071 3076 pathname = os.path.dirname(testdescs[0]['path'])
3072 3077 if pathname:
3073 3078 testdir = os.path.join(testdir, pathname)
3074 3079 self._testdir = osenvironb[b'TESTDIR'] = testdir
3075 3080 osenvironb[b'TESTDIR_FORWARD_SLASH'] = osenvironb[b'TESTDIR'].replace(
3076 3081 os.sep.encode('ascii'), b'/'
3077 3082 )
3078 3083
3079 3084 if self.options.outputdir:
3080 3085 self._outputdir = canonpath(_sys2bytes(self.options.outputdir))
3081 3086 else:
3082 3087 self._outputdir = getcwdb()
3083 3088 if testdescs and pathname:
3084 3089 self._outputdir = os.path.join(self._outputdir, pathname)
3085 3090 previoustimes = {}
3086 3091 if self.options.order_by_runtime:
3087 3092 previoustimes = dict(loadtimes(self._outputdir))
3088 3093 sorttests(testdescs, previoustimes, shuffle=self.options.random)
3089 3094
3090 3095 if 'PYTHONHASHSEED' not in os.environ:
3091 3096 # use a random python hash seed all the time
3092 3097 # we do the randomness ourself to know what seed is used
3093 3098 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
3094 3099
3095 3100 # Rayon (Rust crate for multi-threading) will use all logical CPU cores
3096 3101 # by default, causing thrashing on high-cpu-count systems.
3097 3102 # Setting its limit to 3 during tests should still let us uncover
3098 3103 # multi-threading bugs while keeping the thrashing reasonable.
3099 3104 os.environ.setdefault("RAYON_NUM_THREADS", "3")
3100 3105
3101 3106 if self.options.tmpdir:
3102 3107 self.options.keep_tmpdir = True
3103 3108 tmpdir = _sys2bytes(self.options.tmpdir)
3104 3109 if os.path.exists(tmpdir):
3105 3110 # Meaning of tmpdir has changed since 1.3: we used to create
3106 3111 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
3107 3112 # tmpdir already exists.
3108 3113 print("error: temp dir %r already exists" % tmpdir)
3109 3114 return 1
3110 3115
3111 3116 os.makedirs(tmpdir)
3112 3117 else:
3113 3118 d = None
3114 3119 if WINDOWS:
3115 3120 # without this, we get the default temp dir location, but
3116 3121 # in all lowercase, which causes troubles with paths (issue3490)
3117 3122 d = osenvironb.get(b'TMP', None)
3118 3123 tmpdir = tempfile.mkdtemp(b'', b'hgtests.', d)
3119 3124
3120 3125 self._hgtmp = osenvironb[b'HGTMP'] = os.path.realpath(tmpdir)
3121 3126
3122 3127 self._custom_bin_dir = os.path.join(self._hgtmp, b'custom-bin')
3123 3128 os.makedirs(self._custom_bin_dir)
3124 3129
3125 3130 if self.options.with_hg:
3126 3131 self._installdir = None
3127 3132 whg = self.options.with_hg
3128 3133 self._bindir = os.path.dirname(os.path.realpath(whg))
3129 3134 assert isinstance(self._bindir, bytes)
3130 3135 self._hgcommand = os.path.basename(whg)
3131 3136
3132 3137 normbin = os.path.normpath(os.path.abspath(whg))
3133 3138 normbin = normbin.replace(_sys2bytes(os.sep), b'/')
3134 3139
3135 3140 # Other Python scripts in the test harness need to
3136 3141 # `import mercurial`. If `hg` is a Python script, we assume
3137 3142 # the Mercurial modules are relative to its path and tell the tests
3138 3143 # to load Python modules from its directory.
3139 3144 with open(whg, 'rb') as fh:
3140 3145 initial = fh.read(1024)
3141 3146
3142 3147 if re.match(b'#!.*python', initial):
3143 3148 self._pythondir = self._bindir
3144 3149 # If it looks like our in-repo Rust binary, use the source root.
3145 3150 # This is a bit hacky. But rhg is still not supported outside the
3146 3151 # source directory. So until it is, do the simple thing.
3147 3152 elif re.search(b'/rust/target/[^/]+/hg', normbin):
3148 3153 self._pythondir = os.path.dirname(self._testdir)
3149 3154 # Fall back to the legacy behavior.
3150 3155 else:
3151 3156 self._pythondir = self._bindir
3152 3157 self._pythondir_inferred = True
3153 3158
3154 3159 else:
3155 3160 self._installdir = os.path.join(self._hgtmp, b"install")
3156 3161 self._bindir = os.path.join(self._installdir, b"bin")
3157 3162 self._hgcommand = b'hg'
3158 3163 self._pythondir = os.path.join(self._installdir, b"lib", b"python")
3159 3164
3160 3165 # Force the use of hg.exe instead of relying on MSYS to recognize hg is
3161 3166 # a python script and feed it to python.exe. Legacy stdio is force
3162 3167 # enabled by hg.exe, and this is a more realistic way to launch hg
3163 3168 # anyway.
3164 3169 if WINDOWS and not self._hgcommand.endswith(b'.exe'):
3165 3170 self._hgcommand += b'.exe'
3166 3171
3167 3172 real_hg = os.path.join(self._bindir, self._hgcommand)
3168 3173 osenvironb[b'HGTEST_REAL_HG'] = real_hg
3169 3174 # set CHGHG, then replace "hg" command by "chg"
3170 3175 chgbindir = self._bindir
3171 3176 if self.options.chg or self.options.with_chg:
3172 3177 osenvironb[b'CHG_INSTALLED_AS_HG'] = b'1'
3173 3178 osenvironb[b'CHGHG'] = real_hg
3174 3179 else:
3175 3180 # drop flag for hghave
3176 3181 osenvironb.pop(b'CHG_INSTALLED_AS_HG', None)
3177 3182 if self.options.chg:
3178 3183 self._hgcommand = b'chg'
3179 3184 elif self.options.with_chg:
3180 3185 chgbindir = os.path.dirname(os.path.realpath(self.options.with_chg))
3181 3186 self._hgcommand = os.path.basename(self.options.with_chg)
3182 3187
3183 3188 # configure fallback and replace "hg" command by "rhg"
3184 3189 rhgbindir = self._bindir
3185 3190 if self.options.rhg or self.options.with_rhg:
3186 3191 # Affects hghave.py
3187 3192 osenvironb[b'RHG_INSTALLED_AS_HG'] = b'1'
3188 3193 # Affects configuration. Alternatives would be setting configuration through
3189 3194 # `$HGRCPATH` but some tests override that, or changing `_hgcommand` to include
3190 3195 # `--config` but that disrupts tests that print command lines and check expected
3191 3196 # output.
3192 3197 osenvironb[b'RHG_ON_UNSUPPORTED'] = b'fallback'
3193 3198 osenvironb[b'RHG_FALLBACK_EXECUTABLE'] = real_hg
3194 3199 else:
3195 3200 # drop flag for hghave
3196 3201 osenvironb.pop(b'RHG_INSTALLED_AS_HG', None)
3197 3202 if self.options.rhg:
3198 3203 self._hgcommand = b'rhg'
3199 3204 elif self.options.with_rhg:
3200 3205 rhgbindir = os.path.dirname(os.path.realpath(self.options.with_rhg))
3201 3206 self._hgcommand = os.path.basename(self.options.with_rhg)
3202 3207
3203 3208 if self.options.pyoxidized:
3204 3209 testdir = os.path.dirname(_sys2bytes(canonpath(sys.argv[0])))
3205 3210 reporootdir = os.path.dirname(testdir)
3206 3211 # XXX we should ideally install stuff instead of using the local build
3207 3212 bin_path = (
3208 3213 b'build/pyoxidizer/x86_64-pc-windows-msvc/release/app/hg.exe'
3209 3214 )
3210 3215 full_path = os.path.join(reporootdir, bin_path)
3211 3216 self._hgcommand = full_path
3212 3217 # Affects hghave.py
3213 3218 osenvironb[b'PYOXIDIZED_INSTALLED_AS_HG'] = b'1'
3214 3219 else:
3215 3220 osenvironb.pop(b'PYOXIDIZED_INSTALLED_AS_HG', None)
3216 3221
3217 3222 osenvironb[b"BINDIR"] = self._bindir
3218 3223 osenvironb[b"PYTHON"] = PYTHON
3219 3224
3220 3225 fileb = _sys2bytes(__file__)
3221 3226 runtestdir = os.path.abspath(os.path.dirname(fileb))
3222 3227 osenvironb[b'RUNTESTDIR'] = runtestdir
3223 3228 osenvironb[b'RUNTESTDIR_FORWARD_SLASH'] = runtestdir.replace(
3224 3229 os.sep.encode('ascii'), b'/'
3225 3230 )
3226 3231 sepb = _sys2bytes(os.pathsep)
3227 3232 path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)
3228 3233 if os.path.islink(__file__):
3229 3234 # test helper will likely be at the end of the symlink
3230 3235 realfile = os.path.realpath(fileb)
3231 3236 realdir = os.path.abspath(os.path.dirname(realfile))
3232 3237 path.insert(2, realdir)
3233 3238 if chgbindir != self._bindir:
3234 3239 path.insert(1, chgbindir)
3235 3240 if rhgbindir != self._bindir:
3236 3241 path.insert(1, rhgbindir)
3237 3242 if self._testdir != runtestdir:
3238 3243 path = [self._testdir] + path
3239 3244 path = [self._custom_bin_dir] + path
3240 3245 osenvironb[b"PATH"] = sepb.join(path)
3241 3246
3242 3247 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
3243 3248 # can run .../tests/run-tests.py test-foo where test-foo
3244 3249 # adds an extension to HGRC. Also include run-test.py directory to
3245 3250 # import modules like heredoctest.
3246 3251 pypath = [self._pythondir, self._testdir, runtestdir]
3247 3252 # We have to augment PYTHONPATH, rather than simply replacing
3248 3253 # it, in case external libraries are only available via current
3249 3254 # PYTHONPATH. (In particular, the Subversion bindings on OS X
3250 3255 # are in /opt/subversion.)
3251 3256 oldpypath = osenvironb.get(IMPL_PATH)
3252 3257 if oldpypath:
3253 3258 pypath.append(oldpypath)
3254 3259 osenvironb[IMPL_PATH] = sepb.join(pypath)
3255 3260
3256 3261 if self.options.pure:
3257 3262 os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure"
3258 3263 os.environ["HGMODULEPOLICY"] = "py"
3259 3264 if self.options.rust:
3260 3265 os.environ["HGMODULEPOLICY"] = "rust+c"
3261 3266 if self.options.no_rust:
3262 3267 current_policy = os.environ.get("HGMODULEPOLICY", "")
3263 3268 if current_policy.startswith("rust+"):
3264 3269 os.environ["HGMODULEPOLICY"] = current_policy[len("rust+") :]
3265 3270 os.environ.pop("HGWITHRUSTEXT", None)
3266 3271
3267 3272 if self.options.allow_slow_tests:
3268 3273 os.environ["HGTEST_SLOW"] = "slow"
3269 3274 elif 'HGTEST_SLOW' in os.environ:
3270 3275 del os.environ['HGTEST_SLOW']
3271 3276
3272 3277 self._coveragefile = os.path.join(self._testdir, b'.coverage')
3273 3278
3274 3279 if self.options.exceptions:
3275 3280 exceptionsdir = os.path.join(self._outputdir, b'exceptions')
3276 3281 try:
3277 3282 os.makedirs(exceptionsdir)
3278 3283 except OSError as e:
3279 3284 if e.errno != errno.EEXIST:
3280 3285 raise
3281 3286
3282 3287 # Remove all existing exception reports.
3283 3288 for f in os.listdir(exceptionsdir):
3284 3289 os.unlink(os.path.join(exceptionsdir, f))
3285 3290
3286 3291 osenvironb[b'HGEXCEPTIONSDIR'] = exceptionsdir
3287 3292 logexceptions = os.path.join(self._testdir, b'logexceptions.py')
3288 3293 self.options.extra_config_opt.append(
3289 3294 'extensions.logexceptions=%s' % logexceptions.decode('utf-8')
3290 3295 )
3291 3296
3292 3297 vlog("# Using TESTDIR", _bytes2sys(self._testdir))
3293 3298 vlog("# Using RUNTESTDIR", _bytes2sys(osenvironb[b'RUNTESTDIR']))
3294 3299 vlog("# Using HGTMP", _bytes2sys(self._hgtmp))
3295 3300 vlog("# Using PATH", os.environ["PATH"])
3296 3301 vlog(
3297 3302 "# Using",
3298 3303 _bytes2sys(IMPL_PATH),
3299 3304 _bytes2sys(osenvironb[IMPL_PATH]),
3300 3305 )
3301 3306 vlog("# Writing to directory", _bytes2sys(self._outputdir))
3302 3307
3303 3308 try:
3304 3309 return self._runtests(testdescs) or 0
3305 3310 finally:
3306 3311 time.sleep(0.1)
3307 3312 self._cleanup()
3308 3313
3309 3314 def findtests(self, args):
3310 3315 """Finds possible test files from arguments.
3311 3316
3312 3317 If you wish to inject custom tests into the test harness, this would
3313 3318 be a good function to monkeypatch or override in a derived class.
3314 3319 """
3315 3320 if not args:
3316 3321 if self.options.changed:
3317 3322 proc = Popen4(
3318 3323 b'hg st --rev "%s" -man0 .'
3319 3324 % _sys2bytes(self.options.changed),
3320 3325 None,
3321 3326 0,
3322 3327 )
3323 3328 stdout, stderr = proc.communicate()
3324 3329 args = stdout.strip(b'\0').split(b'\0')
3325 3330 else:
3326 3331 args = os.listdir(b'.')
3327 3332
3328 3333 expanded_args = []
3329 3334 for arg in args:
3330 3335 if os.path.isdir(arg):
3331 3336 if not arg.endswith(b'/'):
3332 3337 arg += b'/'
3333 3338 expanded_args.extend([arg + a for a in os.listdir(arg)])
3334 3339 else:
3335 3340 expanded_args.append(arg)
3336 3341 args = expanded_args
3337 3342
3338 3343 testcasepattern = re.compile(br'([\w-]+\.t|py)(?:#([a-zA-Z0-9_\-.#]+))')
3339 3344 tests = []
3340 3345 for t in args:
3341 3346 case = []
3342 3347
3343 3348 if not (
3344 3349 os.path.basename(t).startswith(b'test-')
3345 3350 and (t.endswith(b'.py') or t.endswith(b'.t'))
3346 3351 ):
3347 3352
3348 3353 m = testcasepattern.match(os.path.basename(t))
3349 3354 if m is not None:
3350 3355 t_basename, casestr = m.groups()
3351 3356 t = os.path.join(os.path.dirname(t), t_basename)
3352 3357 if casestr:
3353 3358 case = casestr.split(b'#')
3354 3359 else:
3355 3360 continue
3356 3361
3357 3362 if t.endswith(b'.t'):
3358 3363 # .t file may contain multiple test cases
3359 3364 casedimensions = parsettestcases(t)
3360 3365 if casedimensions:
3361 3366 cases = []
3362 3367
3363 3368 def addcases(case, casedimensions):
3364 3369 if not casedimensions:
3365 3370 cases.append(case)
3366 3371 else:
3367 3372 for c in casedimensions[0]:
3368 3373 addcases(case + [c], casedimensions[1:])
3369 3374
3370 3375 addcases([], casedimensions)
3371 3376 if case and case in cases:
3372 3377 cases = [case]
3373 3378 elif case:
3374 3379 # Ignore invalid cases
3375 3380 cases = []
3376 3381 else:
3377 3382 pass
3378 3383 tests += [{'path': t, 'case': c} for c in sorted(cases)]
3379 3384 else:
3380 3385 tests.append({'path': t})
3381 3386 else:
3382 3387 tests.append({'path': t})
3383 3388
3384 3389 if self.options.retest:
3385 3390 retest_args = []
3386 3391 for test in tests:
3387 3392 errpath = self._geterrpath(test)
3388 3393 if os.path.exists(errpath):
3389 3394 retest_args.append(test)
3390 3395 tests = retest_args
3391 3396 return tests
3392 3397
3393 3398 def _runtests(self, testdescs):
3394 3399 def _reloadtest(test, i):
3395 3400 # convert a test back to its description dict
3396 3401 desc = {'path': test.path}
3397 3402 case = getattr(test, '_case', [])
3398 3403 if case:
3399 3404 desc['case'] = case
3400 3405 return self._gettest(desc, i)
3401 3406
3402 3407 try:
3403 3408 if self.options.restart:
3404 3409 orig = list(testdescs)
3405 3410 while testdescs:
3406 3411 desc = testdescs[0]
3407 3412 errpath = self._geterrpath(desc)
3408 3413 if os.path.exists(errpath):
3409 3414 break
3410 3415 testdescs.pop(0)
3411 3416 if not testdescs:
3412 3417 print("running all tests")
3413 3418 testdescs = orig
3414 3419
3415 3420 tests = [self._gettest(d, i) for i, d in enumerate(testdescs)]
3416 3421 num_tests = len(tests) * self.options.runs_per_test
3417 3422
3418 3423 jobs = min(num_tests, self.options.jobs)
3419 3424
3420 3425 failed = False
3421 3426 kws = self.options.keywords
3422 3427 if kws is not None:
3423 3428 kws = kws.encode('utf-8')
3424 3429
3425 3430 suite = TestSuite(
3426 3431 self._testdir,
3427 3432 jobs=jobs,
3428 3433 whitelist=self.options.whitelisted,
3429 3434 blacklist=self.options.blacklist,
3430 3435 keywords=kws,
3431 3436 loop=self.options.loop,
3432 3437 runs_per_test=self.options.runs_per_test,
3433 3438 showchannels=self.options.showchannels,
3434 3439 tests=tests,
3435 3440 loadtest=_reloadtest,
3436 3441 )
3437 3442 verbosity = 1
3438 3443 if self.options.list_tests:
3439 3444 verbosity = 0
3440 3445 elif self.options.verbose:
3441 3446 verbosity = 2
3442 3447 runner = TextTestRunner(self, verbosity=verbosity)
3443 3448
3444 3449 if self.options.list_tests:
3445 3450 result = runner.listtests(suite)
3446 3451 else:
3447 3452 self._usecorrectpython()
3448 3453 if self._installdir:
3449 3454 self._installhg()
3450 3455 self._checkhglib("Testing")
3451 3456 if self.options.chg:
3452 3457 assert self._installdir
3453 3458 self._installchg()
3454 3459 if self.options.rhg:
3455 3460 assert self._installdir
3456 3461 self._installrhg()
3457 3462 elif self.options.pyoxidized:
3458 3463 self._build_pyoxidized()
3459 3464 self._use_correct_mercurial()
3460 3465
3461 3466 log(
3462 3467 'running %d tests using %d parallel processes'
3463 3468 % (num_tests, jobs)
3464 3469 )
3465 3470
3466 3471 result = runner.run(suite)
3467 3472
3468 3473 if result.failures or result.errors:
3469 3474 failed = True
3470 3475
3471 3476 result.onEnd()
3472 3477
3473 3478 if self.options.anycoverage:
3474 3479 self._outputcoverage()
3475 3480 except KeyboardInterrupt:
3476 3481 failed = True
3477 3482 print("\ninterrupted!")
3478 3483
3479 3484 if failed:
3480 3485 return 1
3481 3486
3482 3487 def _geterrpath(self, test):
3483 3488 # test['path'] is a relative path
3484 3489 if 'case' in test:
3485 3490 # for multiple dimensions test cases
3486 3491 casestr = b'#'.join(test['case'])
3487 3492 errpath = b'%s#%s.err' % (test['path'], casestr)
3488 3493 else:
3489 3494 errpath = b'%s.err' % test['path']
3490 3495 if self.options.outputdir:
3491 3496 self._outputdir = canonpath(_sys2bytes(self.options.outputdir))
3492 3497 errpath = os.path.join(self._outputdir, errpath)
3493 3498 return errpath
3494 3499
3495 3500 def _getport(self, count):
3496 3501 port = self._ports.get(count) # do we have a cached entry?
3497 3502 if port is None:
3498 3503 portneeded = 3
3499 3504 # above 100 tries we just give up and let test reports failure
3500 3505 for tries in xrange(100):
3501 3506 allfree = True
3502 3507 port = self.options.port + self._portoffset
3503 3508 for idx in xrange(portneeded):
3504 3509 if not checkportisavailable(port + idx):
3505 3510 allfree = False
3506 3511 break
3507 3512 self._portoffset += portneeded
3508 3513 if allfree:
3509 3514 break
3510 3515 self._ports[count] = port
3511 3516 return port
3512 3517
3513 3518 def _gettest(self, testdesc, count):
3514 3519 """Obtain a Test by looking at its filename.
3515 3520
3516 3521 Returns a Test instance. The Test may not be runnable if it doesn't
3517 3522 map to a known type.
3518 3523 """
3519 3524 path = testdesc['path']
3520 3525 lctest = path.lower()
3521 3526 testcls = Test
3522 3527
3523 3528 for ext, cls in self.TESTTYPES:
3524 3529 if lctest.endswith(ext):
3525 3530 testcls = cls
3526 3531 break
3527 3532
3528 3533 refpath = os.path.join(getcwdb(), path)
3529 3534 tmpdir = os.path.join(self._hgtmp, b'child%d' % count)
3530 3535
3531 3536 # extra keyword parameters. 'case' is used by .t tests
3532 3537 kwds = {k: testdesc[k] for k in ['case'] if k in testdesc}
3533 3538
3534 3539 t = testcls(
3535 3540 refpath,
3536 3541 self._outputdir,
3537 3542 tmpdir,
3538 3543 keeptmpdir=self.options.keep_tmpdir,
3539 3544 debug=self.options.debug,
3540 3545 first=self.options.first,
3541 3546 timeout=self.options.timeout,
3542 3547 startport=self._getport(count),
3543 3548 extraconfigopts=self.options.extra_config_opt,
3544 3549 shell=self.options.shell,
3545 3550 hgcommand=self._hgcommand,
3546 3551 usechg=bool(self.options.with_chg or self.options.chg),
3547 3552 chgdebug=self.options.chg_debug,
3548 3553 useipv6=useipv6,
3549 3554 **kwds
3550 3555 )
3551 3556 t.should_reload = True
3552 3557 return t
3553 3558
3554 3559 def _cleanup(self):
3555 3560 """Clean up state from this test invocation."""
3556 3561 if self.options.keep_tmpdir:
3557 3562 return
3558 3563
3559 3564 vlog("# Cleaning up HGTMP", _bytes2sys(self._hgtmp))
3560 3565 shutil.rmtree(self._hgtmp, True)
3561 3566 for f in self._createdfiles:
3562 3567 try:
3563 3568 os.remove(f)
3564 3569 except OSError:
3565 3570 pass
3566 3571
3567 3572 def _usecorrectpython(self):
3568 3573 """Configure the environment to use the appropriate Python in tests."""
3569 3574 # Tests must use the same interpreter as us or bad things will happen.
3570 3575 if WINDOWS:
3571 3576 pyexe_names = [b'python', b'python3', b'python.exe']
3572 3577 else:
3573 3578 pyexe_names = [b'python', b'python3']
3574 3579
3575 3580 # os.symlink() is a thing with py3 on Windows, but it requires
3576 3581 # Administrator rights.
3577 3582 if not WINDOWS and getattr(os, 'symlink', None):
3578 3583 msg = "# Making python executable in test path a symlink to '%s'"
3579 3584 msg %= sysexecutable
3580 3585 vlog(msg)
3581 3586 for pyexename in pyexe_names:
3582 3587 mypython = os.path.join(self._custom_bin_dir, pyexename)
3583 3588 try:
3584 3589 if os.readlink(mypython) == sysexecutable:
3585 3590 continue
3586 3591 os.unlink(mypython)
3587 3592 except OSError as err:
3588 3593 if err.errno != errno.ENOENT:
3589 3594 raise
3590 3595 if self._findprogram(pyexename) != sysexecutable:
3591 3596 try:
3592 3597 os.symlink(sysexecutable, mypython)
3593 3598 self._createdfiles.append(mypython)
3594 3599 except OSError as err:
3595 3600 # child processes may race, which is harmless
3596 3601 if err.errno != errno.EEXIST:
3597 3602 raise
3598 3603 elif WINDOWS and not os.getenv('MSYSTEM'):
3599 3604 raise AssertionError('cannot run test on Windows without MSYSTEM')
3600 3605 else:
3601 3606 # Generate explicit file instead of symlink
3602 3607 #
3603 3608 # This is especially important as Windows doesn't have
3604 3609 # `python3.exe`, and MSYS cannot understand the reparse point with
3605 3610 # that name provided by Microsoft. Create a simple script on PATH
3606 3611 # with that name that delegates to the py3 launcher so the shebang
3607 3612 # lines work.
3608 3613 esc_executable = _sys2bytes(shellquote(sysexecutable))
3609 3614 for pyexename in pyexe_names:
3610 3615 stub_exec_path = os.path.join(self._custom_bin_dir, pyexename)
3611 3616 with open(stub_exec_path, 'wb') as f:
3612 3617 f.write(b'#!/bin/sh\n')
3613 3618 f.write(b'%s "$@"\n' % esc_executable)
3614 3619
3615 3620 if WINDOWS:
3616 3621 # adjust the path to make sur the main python finds it own dll
3617 3622 path = os.environ['PATH'].split(os.pathsep)
3618 3623 main_exec_dir = os.path.dirname(sysexecutable)
3619 3624 extra_paths = [_bytes2sys(self._custom_bin_dir), main_exec_dir]
3620 3625
3621 3626 # Binaries installed by pip into the user area like pylint.exe may
3622 3627 # not be in PATH by default.
3623 3628 appdata = os.environ.get('APPDATA')
3624 3629 vi = sys.version_info
3625 3630 if appdata is not None:
3626 3631 python_dir = 'Python%d%d' % (vi[0], vi[1])
3627 3632 scripts_path = [appdata, 'Python', python_dir, 'Scripts']
3628 3633 scripts_dir = os.path.join(*scripts_path)
3629 3634 extra_paths.append(scripts_dir)
3630 3635
3631 3636 os.environ['PATH'] = os.pathsep.join(extra_paths + path)
3632 3637
3633 3638 def _use_correct_mercurial(self):
3634 3639 target_exec = os.path.join(self._custom_bin_dir, b'hg')
3635 3640 if self._hgcommand != b'hg':
3636 3641 # shutil.which only accept bytes from 3.8
3637 3642 real_exec = which(self._hgcommand)
3638 3643 if real_exec is None:
3639 3644 raise ValueError('could not find exec path for "%s"', real_exec)
3640 3645 if real_exec == target_exec:
3641 3646 # do not overwrite something with itself
3642 3647 return
3643 3648 if WINDOWS:
3644 3649 with open(target_exec, 'wb') as f:
3645 3650 f.write(b'#!/bin/sh\n')
3646 3651 escaped_exec = shellquote(_bytes2sys(real_exec))
3647 3652 f.write(b'%s "$@"\n' % _sys2bytes(escaped_exec))
3648 3653 else:
3649 3654 os.symlink(real_exec, target_exec)
3650 3655 self._createdfiles.append(target_exec)
3651 3656
3652 3657 def _installhg(self):
3653 3658 """Install hg into the test environment.
3654 3659
3655 3660 This will also configure hg with the appropriate testing settings.
3656 3661 """
3657 3662 vlog("# Performing temporary installation of HG")
3658 3663 installerrs = os.path.join(self._hgtmp, b"install.err")
3659 3664 compiler = ''
3660 3665 if self.options.compiler:
3661 3666 compiler = '--compiler ' + self.options.compiler
3662 3667 setup_opts = b""
3663 3668 if self.options.pure:
3664 3669 setup_opts = b"--pure"
3665 3670 elif self.options.rust:
3666 3671 setup_opts = b"--rust"
3667 3672 elif self.options.no_rust:
3668 3673 setup_opts = b"--no-rust"
3669 3674
3670 3675 # Run installer in hg root
3671 3676 compiler = _sys2bytes(compiler)
3672 3677 script = _sys2bytes(os.path.realpath(sys.argv[0]))
3673 3678 exe = _sys2bytes(sysexecutable)
3674 3679 hgroot = os.path.dirname(os.path.dirname(script))
3675 3680 self._hgroot = hgroot
3676 3681 os.chdir(hgroot)
3677 3682 nohome = b'--home=""'
3678 3683 if WINDOWS:
3679 3684 # The --home="" trick works only on OS where os.sep == '/'
3680 3685 # because of a distutils convert_path() fast-path. Avoid it at
3681 3686 # least on Windows for now, deal with .pydistutils.cfg bugs
3682 3687 # when they happen.
3683 3688 nohome = b''
3684 3689 cmd = (
3685 3690 b'"%(exe)s" setup.py %(setup_opts)s clean --all'
3686 3691 b' build %(compiler)s --build-base="%(base)s"'
3687 3692 b' install --force --prefix="%(prefix)s"'
3688 3693 b' --install-lib="%(libdir)s"'
3689 3694 b' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
3690 3695 % {
3691 3696 b'exe': exe,
3692 3697 b'setup_opts': setup_opts,
3693 3698 b'compiler': compiler,
3694 3699 b'base': os.path.join(self._hgtmp, b"build"),
3695 3700 b'prefix': self._installdir,
3696 3701 b'libdir': self._pythondir,
3697 3702 b'bindir': self._bindir,
3698 3703 b'nohome': nohome,
3699 3704 b'logfile': installerrs,
3700 3705 }
3701 3706 )
3702 3707
3703 3708 # setuptools requires install directories to exist.
3704 3709 def makedirs(p):
3705 3710 try:
3706 3711 os.makedirs(p)
3707 3712 except OSError as e:
3708 3713 if e.errno != errno.EEXIST:
3709 3714 raise
3710 3715
3711 3716 makedirs(self._pythondir)
3712 3717 makedirs(self._bindir)
3713 3718
3714 3719 vlog("# Running", cmd.decode("utf-8"))
3715 3720 if subprocess.call(_bytes2sys(cmd), shell=True) == 0:
3716 3721 if not self.options.verbose:
3717 3722 try:
3718 3723 os.remove(installerrs)
3719 3724 except OSError as e:
3720 3725 if e.errno != errno.ENOENT:
3721 3726 raise
3722 3727 else:
3723 3728 with open(installerrs, 'rb') as f:
3724 3729 for line in f:
3725 3730 sys.stdout.buffer.write(line)
3726 3731 sys.exit(1)
3727 3732 os.chdir(self._testdir)
3728 3733
3729 3734 hgbat = os.path.join(self._bindir, b'hg.bat')
3730 3735 if os.path.isfile(hgbat):
3731 3736 # hg.bat expects to be put in bin/scripts while run-tests.py
3732 3737 # installation layout put it in bin/ directly. Fix it
3733 3738 with open(hgbat, 'rb') as f:
3734 3739 data = f.read()
3735 3740 if br'"%~dp0..\python" "%~dp0hg" %*' in data:
3736 3741 data = data.replace(
3737 3742 br'"%~dp0..\python" "%~dp0hg" %*',
3738 3743 b'"%~dp0python" "%~dp0hg" %*',
3739 3744 )
3740 3745 with open(hgbat, 'wb') as f:
3741 3746 f.write(data)
3742 3747 else:
3743 3748 print('WARNING: cannot fix hg.bat reference to python.exe')
3744 3749
3745 3750 if self.options.anycoverage:
3746 3751 custom = os.path.join(
3747 3752 osenvironb[b'RUNTESTDIR'], b'sitecustomize.py'
3748 3753 )
3749 3754 target = os.path.join(self._pythondir, b'sitecustomize.py')
3750 3755 vlog('# Installing coverage trigger to %s' % target)
3751 3756 shutil.copyfile(custom, target)
3752 3757 rc = os.path.join(self._testdir, b'.coveragerc')
3753 3758 vlog('# Installing coverage rc to %s' % rc)
3754 3759 osenvironb[b'COVERAGE_PROCESS_START'] = rc
3755 3760 covdir = os.path.join(self._installdir, b'..', b'coverage')
3756 3761 try:
3757 3762 os.mkdir(covdir)
3758 3763 except OSError as e:
3759 3764 if e.errno != errno.EEXIST:
3760 3765 raise
3761 3766
3762 3767 osenvironb[b'COVERAGE_DIR'] = covdir
3763 3768
3764 3769 def _checkhglib(self, verb):
3765 3770 """Ensure that the 'mercurial' package imported by python is
3766 3771 the one we expect it to be. If not, print a warning to stderr."""
3767 3772 if self._pythondir_inferred:
3768 3773 # The pythondir has been inferred from --with-hg flag.
3769 3774 # We cannot expect anything sensible here.
3770 3775 return
3771 3776 expecthg = os.path.join(self._pythondir, b'mercurial')
3772 3777 actualhg = self._gethgpath()
3773 3778 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
3774 3779 sys.stderr.write(
3775 3780 'warning: %s with unexpected mercurial lib: %s\n'
3776 3781 ' (expected %s)\n' % (verb, actualhg, expecthg)
3777 3782 )
3778 3783
3779 3784 def _gethgpath(self):
3780 3785 """Return the path to the mercurial package that is actually found by
3781 3786 the current Python interpreter."""
3782 3787 if self._hgpath is not None:
3783 3788 return self._hgpath
3784 3789
3785 3790 cmd = b'"%s" -c "import mercurial; print (mercurial.__path__[0])"'
3786 3791 cmd = _bytes2sys(cmd % PYTHON)
3787 3792
3788 3793 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
3789 3794 out, err = p.communicate()
3790 3795
3791 3796 self._hgpath = out.strip()
3792 3797
3793 3798 return self._hgpath
3794 3799
3795 3800 def _installchg(self):
3796 3801 """Install chg into the test environment"""
3797 3802 vlog('# Performing temporary installation of CHG')
3798 3803 assert os.path.dirname(self._bindir) == self._installdir
3799 3804 assert self._hgroot, 'must be called after _installhg()'
3800 3805 cmd = b'"%(make)s" clean install PREFIX="%(prefix)s"' % {
3801 3806 b'make': b'make', # TODO: switch by option or environment?
3802 3807 b'prefix': self._installdir,
3803 3808 }
3804 3809 cwd = os.path.join(self._hgroot, b'contrib', b'chg')
3805 3810 vlog("# Running", cmd)
3806 3811 proc = subprocess.Popen(
3807 3812 cmd,
3808 3813 shell=True,
3809 3814 cwd=cwd,
3810 3815 stdin=subprocess.PIPE,
3811 3816 stdout=subprocess.PIPE,
3812 3817 stderr=subprocess.STDOUT,
3813 3818 )
3814 3819 out, _err = proc.communicate()
3815 3820 if proc.returncode != 0:
3816 3821 sys.stdout.buffer.write(out)
3817 3822 sys.exit(1)
3818 3823
3819 3824 def _installrhg(self):
3820 3825 """Install rhg into the test environment"""
3821 3826 vlog('# Performing temporary installation of rhg')
3822 3827 assert os.path.dirname(self._bindir) == self._installdir
3823 3828 assert self._hgroot, 'must be called after _installhg()'
3824 3829 cmd = b'"%(make)s" install-rhg PREFIX="%(prefix)s"' % {
3825 3830 b'make': b'make', # TODO: switch by option or environment?
3826 3831 b'prefix': self._installdir,
3827 3832 }
3828 3833 cwd = self._hgroot
3829 3834 vlog("# Running", cmd)
3830 3835 proc = subprocess.Popen(
3831 3836 cmd,
3832 3837 shell=True,
3833 3838 cwd=cwd,
3834 3839 stdin=subprocess.PIPE,
3835 3840 stdout=subprocess.PIPE,
3836 3841 stderr=subprocess.STDOUT,
3837 3842 )
3838 3843 out, _err = proc.communicate()
3839 3844 if proc.returncode != 0:
3840 3845 sys.stdout.buffer.write(out)
3841 3846 sys.exit(1)
3842 3847
3843 3848 def _build_pyoxidized(self):
3844 3849 """build a pyoxidized version of mercurial into the test environment
3845 3850
3846 3851 Ideally this function would be `install_pyoxidier` and would both build
3847 3852 and install pyoxidier. However we are starting small to get pyoxidizer
3848 3853 build binary to testing quickly.
3849 3854 """
3850 3855 vlog('# build a pyoxidized version of Mercurial')
3851 3856 assert os.path.dirname(self._bindir) == self._installdir
3852 3857 assert self._hgroot, 'must be called after _installhg()'
3853 3858 cmd = b'"%(make)s" pyoxidizer-windows-tests' % {
3854 3859 b'make': b'make',
3855 3860 }
3856 3861 cwd = self._hgroot
3857 3862 vlog("# Running", cmd)
3858 3863 proc = subprocess.Popen(
3859 3864 _bytes2sys(cmd),
3860 3865 shell=True,
3861 3866 cwd=_bytes2sys(cwd),
3862 3867 stdin=subprocess.PIPE,
3863 3868 stdout=subprocess.PIPE,
3864 3869 stderr=subprocess.STDOUT,
3865 3870 )
3866 3871 out, _err = proc.communicate()
3867 3872 if proc.returncode != 0:
3868 3873 sys.stdout.buffer.write(out)
3869 3874 sys.exit(1)
3870 3875
3871 3876 def _outputcoverage(self):
3872 3877 """Produce code coverage output."""
3873 3878 import coverage
3874 3879
3875 3880 coverage = coverage.coverage
3876 3881
3877 3882 vlog('# Producing coverage report')
3878 3883 # chdir is the easiest way to get short, relative paths in the
3879 3884 # output.
3880 3885 os.chdir(self._hgroot)
3881 3886 covdir = os.path.join(_bytes2sys(self._installdir), '..', 'coverage')
3882 3887 cov = coverage(data_file=os.path.join(covdir, 'cov'))
3883 3888
3884 3889 # Map install directory paths back to source directory.
3885 3890 cov.config.paths['srcdir'] = ['.', _bytes2sys(self._pythondir)]
3886 3891
3887 3892 cov.combine()
3888 3893
3889 3894 omit = [
3890 3895 _bytes2sys(os.path.join(x, b'*'))
3891 3896 for x in [self._bindir, self._testdir]
3892 3897 ]
3893 3898 cov.report(ignore_errors=True, omit=omit)
3894 3899
3895 3900 if self.options.htmlcov:
3896 3901 htmldir = os.path.join(_bytes2sys(self._outputdir), 'htmlcov')
3897 3902 cov.html_report(directory=htmldir, omit=omit)
3898 3903 if self.options.annotate:
3899 3904 adir = os.path.join(_bytes2sys(self._outputdir), 'annotated')
3900 3905 if not os.path.isdir(adir):
3901 3906 os.mkdir(adir)
3902 3907 cov.annotate(directory=adir, omit=omit)
3903 3908
3904 3909 def _findprogram(self, program):
3905 3910 """Search PATH for a executable program"""
3906 3911 dpb = _sys2bytes(os.defpath)
3907 3912 sepb = _sys2bytes(os.pathsep)
3908 3913 for p in osenvironb.get(b'PATH', dpb).split(sepb):
3909 3914 name = os.path.join(p, program)
3910 3915 if WINDOWS or os.access(name, os.X_OK):
3911 3916 return _bytes2sys(name)
3912 3917 return None
3913 3918
3914 3919 def _checktools(self):
3915 3920 """Ensure tools required to run tests are present."""
3916 3921 for p in self.REQUIREDTOOLS:
3917 3922 if WINDOWS and not p.endswith(b'.exe'):
3918 3923 p += b'.exe'
3919 3924 found = self._findprogram(p)
3920 3925 p = p.decode("utf-8")
3921 3926 if found:
3922 3927 vlog("# Found prerequisite", p, "at", found)
3923 3928 else:
3924 3929 print("WARNING: Did not find prerequisite tool: %s " % p)
3925 3930
3926 3931
3927 3932 def aggregateexceptions(path):
3928 3933 exceptioncounts = collections.Counter()
3929 3934 testsbyfailure = collections.defaultdict(set)
3930 3935 failuresbytest = collections.defaultdict(set)
3931 3936
3932 3937 for f in os.listdir(path):
3933 3938 with open(os.path.join(path, f), 'rb') as fh:
3934 3939 data = fh.read().split(b'\0')
3935 3940 if len(data) != 5:
3936 3941 continue
3937 3942
3938 3943 exc, mainframe, hgframe, hgline, testname = data
3939 3944 exc = exc.decode('utf-8')
3940 3945 mainframe = mainframe.decode('utf-8')
3941 3946 hgframe = hgframe.decode('utf-8')
3942 3947 hgline = hgline.decode('utf-8')
3943 3948 testname = testname.decode('utf-8')
3944 3949
3945 3950 key = (hgframe, hgline, exc)
3946 3951 exceptioncounts[key] += 1
3947 3952 testsbyfailure[key].add(testname)
3948 3953 failuresbytest[testname].add(key)
3949 3954
3950 3955 # Find test having fewest failures for each failure.
3951 3956 leastfailing = {}
3952 3957 for key, tests in testsbyfailure.items():
3953 3958 fewesttest = None
3954 3959 fewestcount = 99999999
3955 3960 for test in sorted(tests):
3956 3961 if len(failuresbytest[test]) < fewestcount:
3957 3962 fewesttest = test
3958 3963 fewestcount = len(failuresbytest[test])
3959 3964
3960 3965 leastfailing[key] = (fewestcount, fewesttest)
3961 3966
3962 3967 # Create a combined counter so we can sort by total occurrences and
3963 3968 # impacted tests.
3964 3969 combined = {}
3965 3970 for key in exceptioncounts:
3966 3971 combined[key] = (
3967 3972 exceptioncounts[key],
3968 3973 len(testsbyfailure[key]),
3969 3974 leastfailing[key][0],
3970 3975 leastfailing[key][1],
3971 3976 )
3972 3977
3973 3978 return {
3974 3979 'exceptioncounts': exceptioncounts,
3975 3980 'total': sum(exceptioncounts.values()),
3976 3981 'combined': combined,
3977 3982 'leastfailing': leastfailing,
3978 3983 'byfailure': testsbyfailure,
3979 3984 'bytest': failuresbytest,
3980 3985 }
3981 3986
3982 3987
3983 3988 if __name__ == '__main__':
3984 3989 if WINDOWS and not os.getenv('MSYSTEM'):
3985 3990 print('cannot run test on Windows without MSYSTEM', file=sys.stderr)
3986 3991 print(
3987 3992 '(if you need to do so contact the mercurial devs: '
3988 3993 'mercurial@mercurial-scm.org)',
3989 3994 file=sys.stderr,
3990 3995 )
3991 3996 sys.exit(255)
3992 3997
3993 3998 runner = TestRunner()
3994 3999
3995 4000 try:
3996 4001 import msvcrt
3997 4002
3998 4003 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
3999 4004 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4000 4005 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
4001 4006 except ImportError:
4002 4007 pass
4003 4008
4004 4009 sys.exit(runner.run(sys.argv[1:]))
@@ -1,43 +1,79 b''
1 1 Make a narrow clone then archive it
2 2 $ . "$TESTDIR/narrow-library.sh"
3 3
4 4 $ hg init master
5 5 $ cd master
6 6
7 7 $ for x in `$TESTDIR/seq.py 3`; do
8 8 > echo $x > "f$x"
9 9 > hg add "f$x"
10 10 > hg commit -m "Add $x"
11 11 > done
12 12 $ cat >> .hg/hgrc << EOF
13 13 > [narrowacl]
14 14 > default.includes=f1 f2
15 15 > EOF
16 16 $ hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid
17 17 $ cat hg.pid >> "$DAEMON_PIDS"
18 18
19 19 $ cd ..
20 20 $ hg clone http://localhost:$HGPORT1 narrowclone1
21 21 requesting all changes
22 22 adding changesets
23 23 adding manifests
24 24 adding file changes
25 25 added 3 changesets with 2 changes to 2 files
26 26 new changesets * (glob)
27 27 updating to branch default
28 28 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 29
30 30 The clone directory should only contain f1 and f2
31 31 $ ls -A -1 narrowclone1 | sort
32 32 .hg
33 33 f1
34 34 f2
35 35
36 36 Requirements should contain narrowhg
37 37 $ hg debugrequires -R narrowclone1 | grep narrowhg
38 38 narrowhg-experimental
39 39
40 40 NarrowHG should track f1 and f2
41 41 $ hg -R narrowclone1 tracked
42 42 I path:f1
43 43 I path:f2
44
45 Narrow should not be able to widen to include f3
46 $ hg -R narrowclone1 tracked --addinclude f3
47 comparing with http://localhost:$HGPORT1/
48 searching for changes
49 abort: The following includes are not accessible for test: ['path:f3']
50 [255]
51 $ ls -A -1 narrowclone1 | sort
52 .hg
53 f1
54 f2
55 $ hg -R narrowclone1 tracked
56 I path:f1
57 I path:f2
58
59 Narrow should allow widen to include f2
60 $ hg -R narrowclone1 tracked --removeinclude f2 > /dev/null
61 $ hg -R narrowclone1 tracked
62 I path:f1
63 $ ls -A -1 narrowclone1 | sort
64 .hg
65 f1
66 $ hg -R narrowclone1 tracked --addinclude f2
67 comparing with http://localhost:$HGPORT1/
68 searching for changes
69 adding changesets
70 adding manifests
71 adding file changes
72 added 0 changesets with 1 changes to 1 files
73 $ hg -R narrowclone1 tracked
74 I path:f1
75 I path:f2
76 $ ls -A -1 narrowclone1 | sort
77 .hg
78 f1
79 f2
General Comments 0
You need to be logged in to leave comments. Login now