1# Copyright (c) Twisted Matrix Laboratories. 2# See LICENSE for details. 3 4""" 5Test cases for L{twisted.names.rfc1982}. 6""" 7 8 9import calendar 10from datetime import datetime 11from functools import partial 12 13from twisted.names._rfc1982 import SerialNumber 14from twisted.trial import unittest 15 16 17class SerialNumberTests(unittest.TestCase): 18 """ 19 Tests for L{SerialNumber}. 20 """ 21 22 def test_serialBitsDefault(self): 23 """ 24 L{SerialNumber.serialBits} has default value 32. 25 """ 26 self.assertEqual(SerialNumber(1)._serialBits, 32) 27 28 def test_serialBitsOverride(self): 29 """ 30 L{SerialNumber.__init__} accepts a C{serialBits} argument whose value is 31 assigned to L{SerialNumber.serialBits}. 32 """ 33 self.assertEqual(SerialNumber(1, serialBits=8)._serialBits, 8) 34 35 def test_repr(self): 36 """ 37 L{SerialNumber.__repr__} returns a string containing number and 38 serialBits. 39 """ 40 self.assertEqual( 41 "<SerialNumber number=123 serialBits=32>", 42 repr(SerialNumber(123, serialBits=32)), 43 ) 44 45 def test_str(self): 46 """ 47 L{SerialNumber.__str__} returns a string representation of the current 48 value. 49 """ 50 self.assertEqual(str(SerialNumber(123)), "123") 51 52 def test_int(self): 53 """ 54 L{SerialNumber.__int__} returns an integer representation of the current 55 value. 56 """ 57 self.assertEqual(int(SerialNumber(123)), 123) 58 59 def test_hash(self): 60 """ 61 L{SerialNumber.__hash__} allows L{SerialNumber} instances to be hashed 62 for use as dictionary keys. 63 """ 64 self.assertEqual(hash(SerialNumber(1)), hash(SerialNumber(1))) 65 self.assertNotEqual(hash(SerialNumber(1)), hash(SerialNumber(2))) 66 67 def test_convertOtherSerialBitsMismatch(self): 68 """ 69 L{SerialNumber._convertOther} raises L{TypeError} if the other 70 SerialNumber instance has a different C{serialBits} value. 71 """ 72 s1 = SerialNumber(0, serialBits=8) 73 s2 = SerialNumber(0, serialBits=16) 74 75 self.assertRaises(TypeError, s1._convertOther, s2) 76 77 def test_eq(self): 78 """ 79 L{SerialNumber.__eq__} provides rich equality comparison. 80 """ 81 self.assertEqual(SerialNumber(1), SerialNumber(1)) 82 83 def test_eqForeignType(self): 84 """ 85 == comparison of L{SerialNumber} with a non-L{SerialNumber} instance 86 returns L{NotImplemented}. 87 """ 88 self.assertFalse(SerialNumber(1) == object()) 89 self.assertIs(SerialNumber(1).__eq__(object()), NotImplemented) 90 91 def test_ne(self): 92 """ 93 L{SerialNumber.__ne__} provides rich equality comparison. 94 """ 95 self.assertFalse(SerialNumber(1) != SerialNumber(1)) 96 self.assertNotEqual(SerialNumber(1), SerialNumber(2)) 97 98 def test_neForeignType(self): 99 """ 100 != comparison of L{SerialNumber} with a non-L{SerialNumber} instance 101 returns L{NotImplemented}. 102 """ 103 self.assertTrue(SerialNumber(1) != object()) 104 self.assertIs(SerialNumber(1).__ne__(object()), NotImplemented) 105 106 def test_le(self): 107 """ 108 L{SerialNumber.__le__} provides rich <= comparison. 109 """ 110 self.assertTrue(SerialNumber(1) <= SerialNumber(1)) 111 self.assertTrue(SerialNumber(1) <= SerialNumber(2)) 112 113 def test_leForeignType(self): 114 """ 115 <= comparison of L{SerialNumber} with a non-L{SerialNumber} instance 116 raises L{TypeError}. 117 """ 118 self.assertRaises(TypeError, lambda: SerialNumber(1) <= object()) 119 120 def test_ge(self): 121 """ 122 L{SerialNumber.__ge__} provides rich >= comparison. 123 """ 124 self.assertTrue(SerialNumber(1) >= SerialNumber(1)) 125 self.assertTrue(SerialNumber(2) >= SerialNumber(1)) 126 127 def test_geForeignType(self): 128 """ 129 >= comparison of L{SerialNumber} with a non-L{SerialNumber} instance 130 raises L{TypeError}. 131 """ 132 self.assertRaises(TypeError, lambda: SerialNumber(1) >= object()) 133 134 def test_lt(self): 135 """ 136 L{SerialNumber.__lt__} provides rich < comparison. 137 """ 138 self.assertTrue(SerialNumber(1) < SerialNumber(2)) 139 140 def test_ltForeignType(self): 141 """ 142 < comparison of L{SerialNumber} with a non-L{SerialNumber} instance 143 raises L{TypeError}. 144 """ 145 self.assertRaises(TypeError, lambda: SerialNumber(1) < object()) 146 147 def test_gt(self): 148 """ 149 L{SerialNumber.__gt__} provides rich > comparison. 150 """ 151 self.assertTrue(SerialNumber(2) > SerialNumber(1)) 152 153 def test_gtForeignType(self): 154 """ 155 > comparison of L{SerialNumber} with a non-L{SerialNumber} instance 156 raises L{TypeError}. 157 """ 158 self.assertRaises(TypeError, lambda: SerialNumber(2) > object()) 159 160 def test_add(self): 161 """ 162 L{SerialNumber.__add__} allows L{SerialNumber} instances to be summed. 163 """ 164 self.assertEqual(SerialNumber(1) + SerialNumber(1), SerialNumber(2)) 165 166 def test_addForeignType(self): 167 """ 168 Addition of L{SerialNumber} with a non-L{SerialNumber} instance raises 169 L{TypeError}. 170 """ 171 self.assertRaises(TypeError, lambda: SerialNumber(1) + object()) 172 173 def test_addOutOfRangeHigh(self): 174 """ 175 L{SerialNumber} cannot be added with other SerialNumber values larger 176 than C{_maxAdd}. 177 """ 178 maxAdd = SerialNumber(1)._maxAdd 179 self.assertRaises( 180 ArithmeticError, lambda: SerialNumber(1) + SerialNumber(maxAdd + 1) 181 ) 182 183 def test_maxVal(self): 184 """ 185 L{SerialNumber.__add__} returns a wrapped value when s1 plus the s2 186 would result in a value greater than the C{maxVal}. 187 """ 188 s = SerialNumber(1) 189 maxVal = s._halfRing + s._halfRing - 1 190 maxValPlus1 = maxVal + 1 191 self.assertTrue(SerialNumber(maxValPlus1) > SerialNumber(maxVal)) 192 self.assertEqual(SerialNumber(maxValPlus1), SerialNumber(0)) 193 194 def test_fromRFC4034DateString(self): 195 """ 196 L{SerialNumber.fromRFC4034DateString} accepts a datetime string argument 197 of the form 'YYYYMMDDhhmmss' and returns an L{SerialNumber} instance 198 whose value is the unix timestamp corresponding to that UTC date. 199 """ 200 self.assertEqual( 201 SerialNumber(1325376000), 202 SerialNumber.fromRFC4034DateString("20120101000000"), 203 ) 204 205 def test_toRFC4034DateString(self): 206 """ 207 L{DateSerialNumber.toRFC4034DateString} interprets the current value as 208 a unix timestamp and returns a date string representation of that date. 209 """ 210 self.assertEqual( 211 "20120101000000", SerialNumber(1325376000).toRFC4034DateString() 212 ) 213 214 def test_unixEpoch(self): 215 """ 216 L{SerialNumber.toRFC4034DateString} stores 32bit timestamps relative to 217 the UNIX epoch. 218 """ 219 self.assertEqual(SerialNumber(0).toRFC4034DateString(), "19700101000000") 220 221 def test_Y2106Problem(self): 222 """ 223 L{SerialNumber} wraps unix timestamps in the year 2106. 224 """ 225 self.assertEqual(SerialNumber(-1).toRFC4034DateString(), "21060207062815") 226 227 def test_Y2038Problem(self): 228 """ 229 L{SerialNumber} raises ArithmeticError when used to add dates more than 230 68 years in the future. 231 """ 232 maxAddTime = calendar.timegm(datetime(2038, 1, 19, 3, 14, 7).utctimetuple()) 233 234 self.assertEqual( 235 maxAddTime, 236 SerialNumber(0)._maxAdd, 237 ) 238 239 self.assertRaises( 240 ArithmeticError, lambda: SerialNumber(0) + SerialNumber(maxAddTime + 1) 241 ) 242 243 244def assertUndefinedComparison(testCase, s1, s2): 245 """ 246 A custom assertion for L{SerialNumber} values that cannot be meaningfully 247 compared. 248 249 "Note that there are some pairs of values s1 and s2 for which s1 is not 250 equal to s2, but for which s1 is neither greater than, nor less than, s2. 251 An attempt to use these ordering operators on such pairs of values produces 252 an undefined result." 253 254 @see: U{https://tools.ietf.org/html/rfc1982#section-3.2} 255 256 @param testCase: The L{unittest.TestCase} on which to call assertion 257 methods. 258 @type testCase: L{unittest.TestCase} 259 260 @param s1: The first value to compare. 261 @type s1: L{SerialNumber} 262 263 @param s2: The second value to compare. 264 @type s2: L{SerialNumber} 265 """ 266 testCase.assertFalse(s1 == s2) 267 testCase.assertFalse(s1 <= s2) 268 testCase.assertFalse(s1 < s2) 269 testCase.assertFalse(s1 > s2) 270 testCase.assertFalse(s1 >= s2) 271 272 273serialNumber2 = partial(SerialNumber, serialBits=2) 274 275 276class SerialNumber2BitTests(unittest.TestCase): 277 """ 278 Tests for correct answers to example calculations in RFC1982 5.1. 279 280 The simplest meaningful serial number space has SERIAL_BITS == 2. In this 281 space, the integers that make up the serial number space are 0, 1, 2, and 3. 282 That is, 3 == 2^SERIAL_BITS - 1. 283 284 https://tools.ietf.org/html/rfc1982#section-5.1 285 """ 286 287 def test_maxadd(self): 288 """ 289 In this space, the largest integer that it is meaningful to add to a 290 sequence number is 2^(SERIAL_BITS - 1) - 1, or 1. 291 """ 292 self.assertEqual(SerialNumber(0, serialBits=2)._maxAdd, 1) 293 294 def test_add(self): 295 """ 296 Then, as defined 0+1 == 1, 1+1 == 2, 2+1 == 3, and 3+1 == 0. 297 """ 298 self.assertEqual(serialNumber2(0) + serialNumber2(1), serialNumber2(1)) 299 self.assertEqual(serialNumber2(1) + serialNumber2(1), serialNumber2(2)) 300 self.assertEqual(serialNumber2(2) + serialNumber2(1), serialNumber2(3)) 301 self.assertEqual(serialNumber2(3) + serialNumber2(1), serialNumber2(0)) 302 303 def test_gt(self): 304 """ 305 Further, 1 > 0, 2 > 1, 3 > 2, and 0 > 3. 306 """ 307 self.assertTrue(serialNumber2(1) > serialNumber2(0)) 308 self.assertTrue(serialNumber2(2) > serialNumber2(1)) 309 self.assertTrue(serialNumber2(3) > serialNumber2(2)) 310 self.assertTrue(serialNumber2(0) > serialNumber2(3)) 311 312 def test_undefined(self): 313 """ 314 It is undefined whether 2 > 0 or 0 > 2, and whether 1 > 3 or 3 > 1. 315 """ 316 assertUndefinedComparison(self, serialNumber2(2), serialNumber2(0)) 317 assertUndefinedComparison(self, serialNumber2(0), serialNumber2(2)) 318 assertUndefinedComparison(self, serialNumber2(1), serialNumber2(3)) 319 assertUndefinedComparison(self, serialNumber2(3), serialNumber2(1)) 320 321 322serialNumber8 = partial(SerialNumber, serialBits=8) 323 324 325class SerialNumber8BitTests(unittest.TestCase): 326 """ 327 Tests for correct answers to example calculations in RFC1982 5.2. 328 329 Consider the case where SERIAL_BITS == 8. In this space the integers that 330 make up the serial number space are 0, 1, 2, ... 254, 255. 255 == 331 2^SERIAL_BITS - 1. 332 333 https://tools.ietf.org/html/rfc1982#section-5.2 334 """ 335 336 def test_maxadd(self): 337 """ 338 In this space, the largest integer that it is meaningful to add to a 339 sequence number is 2^(SERIAL_BITS - 1) - 1, or 127. 340 """ 341 self.assertEqual(SerialNumber(0, serialBits=8)._maxAdd, 127) 342 343 def test_add(self): 344 """ 345 Addition is as expected in this space, for example: 255+1 == 0, 346 100+100 == 200, and 200+100 == 44. 347 """ 348 self.assertEqual(serialNumber8(255) + serialNumber8(1), serialNumber8(0)) 349 self.assertEqual(serialNumber8(100) + serialNumber8(100), serialNumber8(200)) 350 self.assertEqual(serialNumber8(200) + serialNumber8(100), serialNumber8(44)) 351 352 def test_gt(self): 353 """ 354 Comparison is more interesting, 1 > 0, 44 > 0, 100 > 0, 100 > 44, 355 200 > 100, 255 > 200, 0 > 255, 100 > 255, 0 > 200, and 44 > 200. 356 """ 357 self.assertTrue(serialNumber8(1) > serialNumber8(0)) 358 self.assertTrue(serialNumber8(44) > serialNumber8(0)) 359 self.assertTrue(serialNumber8(100) > serialNumber8(0)) 360 self.assertTrue(serialNumber8(100) > serialNumber8(44)) 361 self.assertTrue(serialNumber8(200) > serialNumber8(100)) 362 self.assertTrue(serialNumber8(255) > serialNumber8(200)) 363 self.assertTrue(serialNumber8(100) > serialNumber8(255)) 364 self.assertTrue(serialNumber8(0) > serialNumber8(200)) 365 self.assertTrue(serialNumber8(44) > serialNumber8(200)) 366 367 def test_surprisingAddition(self): 368 """ 369 Note that 100+100 > 100, but that (100+100)+100 < 100. Incrementing a 370 serial number can cause it to become "smaller". Of course, incrementing 371 by a smaller number will allow many more increments to be made before 372 this occurs. However this is always something to be aware of, it can 373 cause surprising errors, or be useful as it is the only defined way to 374 actually cause a serial number to decrease. 375 """ 376 self.assertTrue(serialNumber8(100) + serialNumber8(100) > serialNumber8(100)) 377 self.assertTrue( 378 serialNumber8(100) + serialNumber8(100) + serialNumber8(100) 379 < serialNumber8(100) 380 ) 381 382 def test_undefined(self): 383 """ 384 The pairs of values 0 and 128, 1 and 129, 2 and 130, etc, to 127 and 255 385 are not equal, but in each pair, neither number is defined as being 386 greater than, or less than, the other. 387 """ 388 assertUndefinedComparison(self, serialNumber8(0), serialNumber8(128)) 389 assertUndefinedComparison(self, serialNumber8(1), serialNumber8(129)) 390 assertUndefinedComparison(self, serialNumber8(2), serialNumber8(130)) 391 assertUndefinedComparison(self, serialNumber8(127), serialNumber8(255)) 392