Coverage for libs/sm/refcounter.py: 64%

416 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2026-07-10 07:59 +0000

1# 

2# Copyright (C) Citrix Systems Inc. 

3# 

4# This program is free software; you can redistribute it and/or modify 

5# it under the terms of the GNU Lesser General Public License as published 

6# by the Free Software Foundation; version 2.1 only. 

7# 

8# This program is distributed in the hope that it will be useful, 

9# but WITHOUT ANY WARRANTY; without even the implied warranty of 

10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

11# GNU Lesser General Public License for more details. 

12# 

13# You should have received a copy of the GNU Lesser General Public License 

14# along with this program; if not, write to the Free Software Foundation, Inc., 

15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

16# 

17# Persistent reference counter. This refcounter can maintain two separate 

18# refcounts: one binary (which can have a value of 0 or 1) and one normal. The 

19# parameter "binary" specifies which of the two counters to update, while the 

20# return value is zero IFF both counters are zero 

21# 

22# Synchronization must be done at a higher level, by the users of this module 

23# 

24 

25import os 

26from sm.core import util 

27from sm.core.lock import Lock 

28import errno 

29 

30 

31class RefCounterException(util.SMException): 

32 pass 

33 

34 

35class RefCounter: 

36 """Persistent local-FS file-based reference counter. The 

37 operations are get() and put(), and they are atomic.""" 

38 

39 BASE_DIR = "/run/sm/refcount" 

40 

41 def get(obj, binary, ns=None): 

42 """Get (inc ref count) 'obj' in namespace 'ns' (optional).  

43 Returns new ref count""" 

44 if binary: 

45 return RefCounter._adjust(ns, obj, 0, 1) 

46 else: 

47 return RefCounter._adjust(ns, obj, 1, 0) 

48 get = staticmethod(get) 

49 

50 def put(obj, binary, ns=None): 

51 """Put (dec ref count) 'obj' in namespace 'ns' (optional). If ref 

52 count was zero already, this operation is a no-op. 

53 Returns new ref count""" 

54 if binary: 

55 return RefCounter._adjust(ns, obj, 0, -1) 

56 else: 

57 return RefCounter._adjust(ns, obj, -1, 0) 

58 put = staticmethod(put) 

59 

60 def set(obj, count, binaryCount, ns=None): 

61 """Set normal & binary counts explicitly to the specified values. 

62 Returns new ref count""" 

63 (obj, ns) = RefCounter._getSafeNames(obj, ns) 

64 assert(count >= 0 and binaryCount >= 0) 

65 if binaryCount > 1: 

66 raise RefCounterException("Binary count = %d > 1" % binaryCount) 

67 RefCounter._set(ns, obj, count, binaryCount) 

68 set = staticmethod(set) 

69 

70 def check(obj, ns=None): 

71 """Get the ref count values for 'obj' in namespace 'ns' (optional)""" 

72 (obj, ns) = RefCounter._getSafeNames(obj, ns) 

73 return RefCounter._get(ns, obj) 

74 check = staticmethod(check) 

75 

76 def checkLocked(obj, ns): 

77 """Lock-protected access""" 

78 lock = Lock(obj, ns) 

79 lock.acquire() 

80 try: 

81 return RefCounter.check(obj, ns) 

82 finally: 

83 lock.release() 

84 checkLocked = staticmethod(checkLocked) 

85 

86 def reset(obj, ns=None): 

87 """Reset ref counts for 'obj' in namespace 'ns' (optional) to 0.""" 

88 RefCounter.resetAll(ns, obj) 

89 reset = staticmethod(reset) 

90 

91 def resetAll(ns=None, obj=None): 

92 """Reset ref counts of 'obj' in namespace 'ns' to 0. If obj is not 

93 provided, reset all existing objects in 'ns' to 0. If neither obj nor  

94 ns are supplied, do this for all namespaces""" 

95 if obj: 

96 (obj, ns) = RefCounter._getSafeNames(obj, ns) 

97 if ns: 

98 nsList = [ns] 

99 else: 

100 if not util.pathexists(RefCounter.BASE_DIR): 

101 return 

102 try: 

103 nsList = os.listdir(RefCounter.BASE_DIR) 

104 except OSError: 

105 raise RefCounterException("failed to get namespace list") 

106 for ns in nsList: 

107 RefCounter._reset(ns, obj) 

108 resetAll = staticmethod(resetAll) 

109 

110 def _adjust(ns, obj, delta, binaryDelta): 

111 """Add 'delta' to the normal refcount and 'binaryDelta' to the binary 

112 refcount of 'obj' in namespace 'ns'.  

113 Returns new ref count""" 

114 if binaryDelta > 1 or binaryDelta < -1: 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true

115 raise RefCounterException("Binary delta = %d outside [-1;1]" % \ 

116 binaryDelta) 

117 (obj, ns) = RefCounter._getSafeNames(obj, ns) 

118 (count, binaryCount) = RefCounter._get(ns, obj) 

119 

120 newCount = count + delta 

121 newBinaryCount = binaryCount + binaryDelta 

122 if newCount < 0: 

123 util.SMlog("WARNING: decrementing normal refcount of 0") 

124 newCount = 0 

125 if newBinaryCount < 0: 

126 util.SMlog("WARNING: decrementing binary refcount of 0") 

127 newBinaryCount = 0 

128 if newBinaryCount > 1: 

129 newBinaryCount = 1 

130 util.SMlog("Refcount for %s:%s (%d, %d) + (%d, %d) => (%d, %d)" % \ 

131 (ns, obj, count, binaryCount, delta, binaryDelta, 

132 newCount, newBinaryCount)) 

133 RefCounter._set(ns, obj, newCount, newBinaryCount) 

134 return newCount + newBinaryCount 

135 _adjust = staticmethod(_adjust) 

136 

137 def _get(ns, obj): 

138 """Get the ref count values for 'obj' in namespace 'ns'""" 

139 objFile = os.path.join(RefCounter.BASE_DIR, ns, obj) 

140 (count, binaryCount) = (0, 0) 

141 if util.pathexists(objFile): 

142 (count, binaryCount) = RefCounter._readCount(objFile) 

143 return (count, binaryCount) 

144 _get = staticmethod(_get) 

145 

146 def _set(ns, obj, count, binaryCount): 

147 """Set the ref count values for 'obj' in namespace 'ns'""" 

148 util.SMlog("Refcount for %s:%s set => (%d, %db)" % \ 

149 (ns, obj, count, binaryCount)) 

150 if count == 0 and binaryCount == 0: 

151 RefCounter._removeObject(ns, obj) 

152 else: 

153 objFile = os.path.join(RefCounter.BASE_DIR, ns, obj) 

154 

155 while not RefCounter._writeCount(objFile, count, binaryCount): 

156 RefCounter._createNamespace(ns) 

157 

158 _set = staticmethod(_set) 

159 

160 def _getSafeNames(obj, ns): 

161 """Get a name that can be used as a file name""" 

162 if not ns: 

163 ns = obj.split('/')[0] 

164 if not ns: 

165 ns = "default" 

166 for char in ['/', '*', '?', '\\']: 

167 obj = obj.replace(char, "_") 

168 return (obj, ns) 

169 _getSafeNames = staticmethod(_getSafeNames) 

170 

171 def _createNamespace(ns): 

172 nsDir = os.path.join(RefCounter.BASE_DIR, ns) 

173 try: 

174 os.makedirs(nsDir) 

175 except OSError as e: 

176 if e.errno != errno.EEXIST: 

177 raise RefCounterException("failed to makedirs '%s' (%s)" % \ 

178 (nsDir, e)) 

179 _createNamespace = staticmethod(_createNamespace) 

180 

181 def _removeObject(ns, obj): 

182 nsDir = os.path.join(RefCounter.BASE_DIR, ns) 

183 objFile = os.path.join(nsDir, obj) 

184 if not util.pathexists(objFile): 

185 return 

186 try: 

187 os.unlink(objFile) 

188 except OSError: 

189 raise RefCounterException("failed to remove '%s'" % objFile) 

190 

191 try: 

192 os.rmdir(nsDir) 

193 except OSError as e: 

194 namespaceAlreadyCleanedUp = e.errno == errno.ENOENT 

195 newObjectAddedToNamespace = e.errno == errno.ENOTEMPTY 

196 

197 if namespaceAlreadyCleanedUp or newObjectAddedToNamespace: 197 ↛ 200line 197 didn't jump to line 200, because the condition on line 197 was never false

198 pass 

199 else: 

200 raise RefCounterException("failed to remove '%s'" % nsDir) 

201 _removeObject = staticmethod(_removeObject) 

202 

203 def _reset(ns, obj=None): 

204 nsDir = os.path.join(RefCounter.BASE_DIR, ns) 

205 if not util.pathexists(nsDir): 

206 return 

207 if obj: 

208 if not util.pathexists(os.path.join(nsDir, obj)): 208 ↛ 209line 208 didn't jump to line 209, because the condition on line 208 was never true

209 return 

210 objList = [obj] 

211 else: 

212 try: 

213 objList = os.listdir(nsDir) 

214 except OSError: 

215 raise RefCounterException("failed to list '%s'" % ns) 

216 for obj in objList: 

217 RefCounter._removeObject(ns, obj) 

218 _reset = staticmethod(_reset) 

219 

220 def _readCount(fn): 

221 try: 

222 f = open(fn, 'r') 

223 line = f.readline() 

224 nums = line.split() 

225 count = int(nums[0]) 

226 binaryCount = int(nums[1]) 

227 f.close() 

228 except IOError: 

229 raise RefCounterException("failed to read file '%s'" % fn) 

230 return (count, binaryCount) 

231 _readCount = staticmethod(_readCount) 

232 

233 def _writeCount(fn, count, binaryCount): 

234 try: 

235 f = open(fn, 'w') 

236 f.write("%d %d\n" % (count, binaryCount)) 

237 f.close() 

238 return True 

239 except IOError as e: 

240 fileNotFound = e.errno == errno.ENOENT 

241 if fileNotFound: 241 ↛ 243line 241 didn't jump to line 243, because the condition on line 241 was never false

242 return False 

243 raise RefCounterException("failed to write '(%d %d)' to '%s': %s" \ 

244 % (count, binaryCount, fn, e)) 

245 _writeCount = staticmethod(_writeCount) 

246 

247 def _runTests(): 

248 "Unit tests" 

249 

250 RefCounter.resetAll() 

251 

252 # A 

253 (cnt, bcnt) = RefCounter.check("X", "A") 

254 if cnt != 0 or bcnt != 0: 254 ↛ 255line 254 didn't jump to line 255, because the condition on line 254 was never true

255 print("Error: check = %d != 0 in the beginning" % cnt) 

256 return -1 

257 

258 cnt = RefCounter.get("X", False, "A") 

259 if cnt != 1: 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true

260 print("Error: count = %d != 1 after first get()" % cnt) 

261 return -1 

262 (cnt, bcnt) = RefCounter.check("X", "A") 

263 if cnt != 1: 263 ↛ 264line 263 didn't jump to line 264, because the condition on line 263 was never true

264 print("Error: check = %d != 1 after first get()" % cnt) 

265 return -1 

266 

267 cnt = RefCounter.put("X", False, "A") 

268 if cnt != 0: 268 ↛ 269line 268 didn't jump to line 269, because the condition on line 268 was never true

269 print("Error: count = %d != 0 after get-put" % cnt) 

270 return -1 

271 (cnt, bcnt) = RefCounter.check("X", "A") 

272 if cnt != 0: 272 ↛ 273line 272 didn't jump to line 273, because the condition on line 272 was never true

273 print("Error: check = %d != 0 after get-put" % cnt) 

274 return -1 

275 

276 cnt = RefCounter.get("X", False, "A") 

277 if cnt != 1: 277 ↛ 278line 277 didn't jump to line 278, because the condition on line 277 was never true

278 print("Error: count = %d != 1 after get-put-get" % cnt) 

279 return -1 

280 

281 cnt = RefCounter.get("X", False, "A") 

282 if cnt != 2: 282 ↛ 283line 282 didn't jump to line 283, because the condition on line 282 was never true

283 print("Error: count = %d != 2 after second get()" % cnt) 

284 return -1 

285 

286 cnt = RefCounter.get("X", False, "A") 

287 if cnt != 3: 287 ↛ 288line 287 didn't jump to line 288, because the condition on line 287 was never true

288 print("Error: count = %d != 3 after third get()" % cnt) 

289 return -1 

290 (cnt, bcnt) = RefCounter.check("X", "A") 

291 if cnt != 3: 291 ↛ 292line 291 didn't jump to line 292, because the condition on line 291 was never true

292 print("Error: check = %d != 3 after third get()" % cnt) 

293 return -1 

294 

295 cnt = RefCounter.put("Y", False, "A") 

296 if cnt != 0: 296 ↛ 297line 296 didn't jump to line 297, because the condition on line 296 was never true

297 print("Error: count = %d != 0 after first put()" % cnt) 

298 return -1 

299 (cnt, bcnt) = RefCounter.check("Y", "A") 

300 if cnt != 0: 300 ↛ 301line 300 didn't jump to line 301, because the condition on line 300 was never true

301 print("Error: check = %d != 0 after first put()" % cnt) 

302 return -1 

303 

304 cnt = RefCounter.put("X", False, "A") 

305 if cnt != 2: 305 ↛ 306line 305 didn't jump to line 306, because the condition on line 305 was never true

306 print("Error: count = %d != 2 after 3get-1put" % cnt) 

307 return -1 

308 

309 cnt = RefCounter.put("X", False, "A") 

310 if cnt != 1: 310 ↛ 311line 310 didn't jump to line 311, because the condition on line 310 was never true

311 print("Error: count = %d != 1 after 3get-2put" % cnt) 

312 return -1 

313 

314 cnt = RefCounter.get("X", False, "A") 

315 if cnt != 2: 315 ↛ 316line 315 didn't jump to line 316, because the condition on line 315 was never true

316 print("Error: count = %d != 2 after 4get-2put" % cnt) 

317 return -1 

318 (cnt, bcnt) = RefCounter.check("X", "A") 

319 if cnt != 2: 319 ↛ 320line 319 didn't jump to line 320, because the condition on line 319 was never true

320 print("Error: check = %d != 2 after 4get-2put" % cnt) 

321 return -1 

322 

323 cnt = RefCounter.put("X", False, "A") 

324 if cnt != 1: 324 ↛ 325line 324 didn't jump to line 325, because the condition on line 324 was never true

325 print("Error: count = %d != 0 after 4get-3put" % cnt) 

326 return -1 

327 

328 cnt = RefCounter.put("X", False, "A") 

329 if cnt != 0: 329 ↛ 330line 329 didn't jump to line 330, because the condition on line 329 was never true

330 print("Error: count = %d != 0 after 4get-4put" % cnt) 

331 return -1 

332 (cnt, bcnt) = RefCounter.check("X", "A") 

333 if cnt != 0: 333 ↛ 334line 333 didn't jump to line 334, because the condition on line 333 was never true

334 print("Error: check = %d != 0 after 4get-4put" % cnt) 

335 return -1 

336 

337 # B 

338 cnt = RefCounter.put("Z", False, "B") 

339 if cnt != 0: 339 ↛ 340line 339 didn't jump to line 340, because the condition on line 339 was never true

340 print("Error: count = %d != 0 after new put()" % cnt) 

341 return -1 

342 

343 cnt = RefCounter.get("Z", False, "B") 

344 if cnt != 1: 344 ↛ 345line 344 didn't jump to line 345, because the condition on line 344 was never true

345 print("Error: count = %d != 1 after put-get" % cnt) 

346 return -1 

347 

348 cnt = RefCounter.put("Z", False, "B") 

349 if cnt != 0: 349 ↛ 350line 349 didn't jump to line 350, because the condition on line 349 was never true

350 print("Error: count = %d != 0 after put-get-put" % cnt) 

351 return -1 

352 (cnt, bcnt) = RefCounter.check("Z", "B") 

353 if cnt != 0: 353 ↛ 354line 353 didn't jump to line 354, because the condition on line 353 was never true

354 print("Error: check = %d != 0 after put-get-put" % cnt) 

355 return -1 

356 

357 cnt = RefCounter.get("Z", False, "B") 

358 if cnt != 1: 358 ↛ 359line 358 didn't jump to line 359, because the condition on line 358 was never true

359 print("Error: count = %d != 1 after put-get-put-get" % cnt) 

360 return -1 

361 (cnt, bcnt) = RefCounter.check("Z", "B") 

362 if cnt != 1: 362 ↛ 363line 362 didn't jump to line 363, because the condition on line 362 was never true

363 print("Error: check = %d != 1 after put-get-put-get" % cnt) 

364 return -1 

365 

366 # set 

367 (cnt, bcnt) = RefCounter.check("a/b") 

368 if cnt != 0: 368 ↛ 369line 368 didn't jump to line 369, because the condition on line 368 was never true

369 print("Error: count = %d != 0 initially" % cnt) 

370 return -1 

371 RefCounter.set("a/b", 2, 0) 

372 (cnt, bcnt) = RefCounter.check("a/b") 

373 if cnt != 2 or bcnt != 0: 373 ↛ 374line 373 didn't jump to line 374, because the condition on line 373 was never true

374 print("Error: count = (%d,%d) != (2,0) after set(2,0)" % (cnt, bcnt)) 

375 return -1 

376 cnt = RefCounter.put("a/b", False) 

377 if cnt != 1: 377 ↛ 378line 377 didn't jump to line 378, because the condition on line 377 was never true

378 print("Error: count = %d != 1 after set(2)-put" % cnt) 

379 return -1 

380 cnt = RefCounter.get("a/b", False) 

381 if cnt != 2: 381 ↛ 382line 381 didn't jump to line 382, because the condition on line 381 was never true

382 print("Error: count = %d != 2 after set(2)-put-get" % cnt) 

383 return -1 

384 RefCounter.set("a/b", 100, 0) 

385 (cnt, bcnt) = RefCounter.check("a/b") 

386 if cnt != 100 or bcnt != 0: 386 ↛ 387line 386 didn't jump to line 387, because the condition on line 386 was never true

387 print("Error: cnt,bcnt = (%d,%d) != (100,0) after set(100,0)" % \ 

388 (cnt, bcnt)) 

389 return -1 

390 cnt = RefCounter.get("a/b", False) 

391 if cnt != 101: 391 ↛ 392line 391 didn't jump to line 392, because the condition on line 391 was never true

392 print("Error: count = %d != 101 after get" % cnt) 

393 return -1 

394 RefCounter.set("a/b", 100, 1) 

395 (cnt, bcnt) = RefCounter.check("a/b") 

396 if cnt != 100 or bcnt != 1: 396 ↛ 397line 396 didn't jump to line 397, because the condition on line 396 was never true

397 print("Error: cnt,bcnt = (%d,%d) != (100,1) after set(100,1)" % \ 

398 (cnt, bcnt)) 

399 return -1 

400 RefCounter.reset("a/b") 

401 (cnt, bcnt) = RefCounter.check("a/b") 

402 if cnt != 0: 402 ↛ 403line 402 didn't jump to line 403, because the condition on line 402 was never true

403 print("Error: check = %d != 0 after reset" % cnt) 

404 return -1 

405 

406 # binary 

407 cnt = RefCounter.get("A", True) 

408 if cnt != 1: 408 ↛ 409line 408 didn't jump to line 409, because the condition on line 408 was never true

409 print("Error: count = %d != 1 after get(bin)" % cnt) 

410 return -1 

411 cnt = RefCounter.get("A", True) 

412 if cnt != 1: 412 ↛ 413line 412 didn't jump to line 413, because the condition on line 412 was never true

413 print("Error: count = %d != 1 after get(bin)*2" % cnt) 

414 return -1 

415 cnt = RefCounter.put("A", True) 

416 if cnt != 0: 416 ↛ 417line 416 didn't jump to line 417, because the condition on line 416 was never true

417 print("Error: count = %d != 0 after get(bin)*2-put(bin)" % cnt) 

418 return -1 

419 cnt = RefCounter.put("A", True) 

420 if cnt != 0: 420 ↛ 421line 420 didn't jump to line 421, because the condition on line 420 was never true

421 print("Error: count = %d != 0 after get(bin)*2-put(bin)*2" % cnt) 

422 return -1 

423 try: 

424 RefCounter.set("A", 0, 2) 

425 print("Error: set(0,2) was allowed") 

426 return -1 

427 except RefCounterException: 

428 pass 

429 cnt = RefCounter.get("A", True) 

430 if cnt != 1: 430 ↛ 431line 430 didn't jump to line 431, because the condition on line 430 was never true

431 print("Error: count = %d != 1 after get(bin)" % cnt) 

432 return -1 

433 cnt = RefCounter.get("A", False) 

434 if cnt != 2: 434 ↛ 435line 434 didn't jump to line 435, because the condition on line 434 was never true

435 print("Error: count = %d != 2 after get(bin)-get" % cnt) 

436 return -1 

437 cnt = RefCounter.get("A", False) 

438 if cnt != 3: 438 ↛ 439line 438 didn't jump to line 439, because the condition on line 438 was never true

439 print("Error: count = %d != 3 after get(bin)-get-get" % cnt) 

440 return -1 

441 cnt = RefCounter.get("A", True) 

442 if cnt != 3: 442 ↛ 443line 442 didn't jump to line 443, because the condition on line 442 was never true

443 print("Error: count = %d != 3 after get(bin)-get*2-get(bin)" % cnt) 

444 return -1 

445 cnt = RefCounter.put("A", False) 

446 if cnt != 2: 446 ↛ 447line 446 didn't jump to line 447, because the condition on line 446 was never true

447 print("Error: count = %d != 2 after get(bin)*2-get*2-put" % cnt) 

448 return -1 

449 cnt = RefCounter.put("A", True) 

450 if cnt != 1: 450 ↛ 451line 450 didn't jump to line 451, because the condition on line 450 was never true

451 print("Error: cnt = %d != 1 after get(b)*2-get*2-put-put(b)" % cnt) 

452 return -1 

453 cnt = RefCounter.put("A", False) 

454 if cnt != 0: 454 ↛ 455line 454 didn't jump to line 455, because the condition on line 454 was never true

455 print("Error: cnt = %d != 0 after get(b)*2-get*2-put*2-put(b)" % cnt) 

456 return -1 

457 

458 # names 

459 cnt = RefCounter.get("Z", False) 

460 if cnt != 1: 460 ↛ 461line 460 didn't jump to line 461, because the condition on line 460 was never true

461 print("Error: count = %d != 1 after get (no ns 1)" % cnt) 

462 return -1 

463 

464 cnt = RefCounter.get("Z/", False) 

465 if cnt != 1: 465 ↛ 466line 465 didn't jump to line 466, because the condition on line 465 was never true

466 print("Error: count = %d != 1 after get (no ns 2)" % cnt) 

467 return -1 

468 

469 cnt = RefCounter.get("/Z", False) 

470 if cnt != 1: 470 ↛ 471line 470 didn't jump to line 471, because the condition on line 470 was never true

471 print("Error: count = %d != 1 after get (no ns 3)" % cnt) 

472 return -1 

473 

474 cnt = RefCounter.get("/Z/*/?/\\", False) 

475 if cnt != 1: 475 ↛ 476line 475 didn't jump to line 476, because the condition on line 475 was never true

476 print("Error: count = %d != 1 after get (no ns 4)" % cnt) 

477 return -1 

478 

479 cnt = RefCounter.get("Z", False) 

480 if cnt != 2: 480 ↛ 481line 480 didn't jump to line 481, because the condition on line 480 was never true

481 print("Error: count = %d != 2 after get (no ns 1)" % cnt) 

482 return -1 

483 

484 cnt = RefCounter.get("Z/", False) 

485 if cnt != 2: 485 ↛ 486line 485 didn't jump to line 486, because the condition on line 485 was never true

486 print("Error: count = %d != 2 after get (no ns 2)" % cnt) 

487 return -1 

488 

489 cnt = RefCounter.get("/Z", False) 

490 if cnt != 2: 490 ↛ 491line 490 didn't jump to line 491, because the condition on line 490 was never true

491 print("Error: count = %d != 2 after get (no ns 3)" % cnt) 

492 return -1 

493 

494 cnt = RefCounter.get("/Z/*/?/\\", False) 

495 if cnt != 2: 495 ↛ 496line 495 didn't jump to line 496, because the condition on line 495 was never true

496 print("Error: count = %d != 2 after get (no ns 4)" % cnt) 

497 return -1 

498 

499 # resetAll 

500 RefCounter.resetAll("B") 

501 cnt = RefCounter.get("Z", False, "B") 

502 if cnt != 1: 502 ↛ 503line 502 didn't jump to line 503, because the condition on line 502 was never true

503 print("Error: count = %d != 1 after resetAll-get" % cnt) 

504 return -1 

505 

506 cnt = RefCounter.get("Z", False, "C") 

507 if cnt != 1: 507 ↛ 508line 507 didn't jump to line 508, because the condition on line 507 was never true

508 print("Error: count = %d != 1 after C.get" % cnt) 

509 return -1 

510 

511 RefCounter.resetAll("B") 

512 cnt = RefCounter.get("Z", False, "B") 

513 if cnt != 1: 513 ↛ 514line 513 didn't jump to line 514, because the condition on line 513 was never true

514 print("Error: count = %d != 1 after second resetAll-get" % cnt) 

515 return -1 

516 

517 cnt = RefCounter.get("Z", False, "C") 

518 if cnt != 2: 518 ↛ 519line 518 didn't jump to line 519, because the condition on line 518 was never true

519 print("Error: count = %d != 2 after second C.get" % cnt) 

520 return -1 

521 

522 RefCounter.resetAll("D") 

523 RefCounter.resetAll() 

524 cnt = RefCounter.put("Z", False, "B") 

525 if cnt != 0: 525 ↛ 526line 525 didn't jump to line 526, because the condition on line 525 was never true

526 print("Error: count = %d != 0 after resetAll-put" % cnt) 

527 return -1 

528 

529 cnt = RefCounter.put("Z", False, "C") 

530 if cnt != 0: 530 ↛ 531line 530 didn't jump to line 531, because the condition on line 530 was never true

531 print("Error: count = %d != 0 after C.resetAll-put" % cnt) 

532 return -1 

533 

534 RefCounter.resetAll() 

535 

536 return 0 

537 _runTests = staticmethod(_runTests) 

538 

539 

540if __name__ == '__main__': 540 ↛ 541line 540 didn't jump to line 541, because the condition on line 540 was never true

541 print("Running unit tests...") 

542 try: 

543 if RefCounter._runTests() == 0: 

544 print("All done, no errors") 

545 except RefCounterException as e: 

546 print("FAIL: Got exception: %s" % e) 

547 raise