1#! /usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# test_virtual_path.py --- Test module for terrasync.virtual_path 5# Copyright (C) 2018 Florent Rougon 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License as 9# published by the Free Software Foundation; either version 2 of the 10# License, or (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, but 13# WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 21# In order to exercise all tests, run the following command from the parent 22# directory (you may omit the 'discover' argument): 23# 24# python3 -m unittest discover 25 26import collections 27import unittest 28 29from terrasync.virtual_path import VirtualPath, MutableVirtualPath 30 31# Hook doctest-based tests into the unittest test discovery mechanism 32import doctest 33import terrasync.virtual_path 34 35def load_tests(loader, tests, ignore): 36 # Tell unittest to run doctests from terrasync.virtual_path 37 tests.addTests(doctest.DocTestSuite(terrasync.virtual_path)) 38 return tests 39 40 41class VirtualPathCommonTests: 42 """Common tests to run for both VirtualPath and MutableVirtualPath. 43 44 The tests inside this class must exercice the class (VirtualPath or 45 MutableVirtualPath) stored in the 'cls' class attribute. They must 46 work for both VirtualPath and MutableVirtualPath, otherwise they 47 don't belong here! 48 49 """ 50 51 def test_normalizeStringPath(self): 52 self.assertEqual(self.cls.normalizeStringPath("/"), "/") 53 self.assertEqual(self.cls.normalizeStringPath(""), "/") 54 self.assertEqual( 55 self.cls.normalizeStringPath("/abc/Def ijk//l Mn///op/q/rst/"), 56 "/abc/Def ijk/l Mn/op/q/rst") 57 self.assertEqual(self.cls.normalizeStringPath("abc/def"), "/abc/def") 58 self.assertEqual(self.cls.normalizeStringPath("/abc/def"), "/abc/def") 59 self.assertEqual(self.cls.normalizeStringPath("//abc/def"), 60 "/abc/def") 61 self.assertEqual(self.cls.normalizeStringPath("///abc/def"), 62 "/abc/def") 63 self.assertEqual(self.cls.normalizeStringPath("/abc//def"), 64 "/abc/def") 65 66 # Unless the implementation of VirtualPath.__init__() has changed 67 # meanwhile, the following function must be essentially the same as 68 # test_normalizeStringPath(). 69 def test_constructor_and_str(self): 70 p = self.cls("/") 71 self.assertEqual(str(p), "/") 72 73 p = self.cls("") 74 self.assertEqual(str(p), "/") 75 76 p = self.cls("/abc/Def ijk//l Mn///op/q/rst/") 77 self.assertEqual(str(p), "/abc/Def ijk/l Mn/op/q/rst") 78 79 p = self.cls("abc/def") 80 self.assertEqual(str(p), "/abc/def") 81 82 p = self.cls("/abc/def") 83 self.assertEqual(str(p), "/abc/def") 84 85 p = self.cls("//abc/def") 86 self.assertEqual(str(p), "/abc/def") 87 88 p = self.cls("///abc/def") 89 self.assertEqual(str(p), "/abc/def") 90 91 p = self.cls("/abc//def") 92 self.assertEqual(str(p), "/abc/def") 93 94 def test_asPosix (self): 95 self.assertEqual(self.cls("").asPosix(), "/") 96 self.assertEqual(self.cls("/").asPosix(), "/") 97 self.assertEqual(self.cls("/abc//def").asPosix(), "/abc/def") 98 self.assertEqual(self.cls("/abc//def/").asPosix(), "/abc/def") 99 self.assertEqual(self.cls("//abc//def//").asPosix(), "/abc/def") 100 self.assertEqual(self.cls("////abc//def//").asPosix(), "/abc/def") 101 102 def test_samePath(self): 103 self.assertTrue(self.cls("").samePath(self.cls(""))) 104 self.assertTrue(self.cls("").samePath(self.cls("/"))) 105 self.assertTrue(self.cls("/").samePath(self.cls(""))) 106 self.assertTrue(self.cls("/").samePath(self.cls("/"))) 107 108 self.assertTrue( 109 self.cls("/abc/def").samePath(self.cls("/abc/def"))) 110 self.assertTrue( 111 self.cls("/abc//def").samePath(self.cls("/abc/def"))) 112 self.assertTrue( 113 self.cls("/abc/def/").samePath(self.cls("/abc/def"))) 114 115 def test_comparisons(self): 116 self.assertEqual(self.cls("/abc/def"), self.cls("/abc/def")) 117 self.assertEqual(self.cls("/abc//def"), self.cls("/abc/def")) 118 self.assertEqual(self.cls("/abc/def/"), self.cls("/abc/def")) 119 120 self.assertNotEqual(self.cls("/abc/dEf"), self.cls("/abc/def")) 121 self.assertNotEqual(self.cls("/abc/def "), self.cls("/abc/def")) 122 123 self.assertLessEqual(self.cls("/foo/bar"), self.cls("/foo/bar")) 124 self.assertLessEqual(self.cls("/foo/bar"), self.cls("/foo/bbr")) 125 self.assertLess(self.cls("/foo/bar"), self.cls("/foo/bbr")) 126 127 self.assertGreaterEqual(self.cls("/foo/bar"), self.cls("/foo/bar")) 128 self.assertGreaterEqual(self.cls("/foo/bbr"), self.cls("/foo/bar")) 129 self.assertGreater(self.cls("/foo/bbr"), self.cls("/foo/bar")) 130 131 def test_truedivOperators(self): 132 """ 133 Test operators used to add paths components to a VirtualPath instance.""" 134 p = self.cls("/foo/bar/baz/quux/zoot") 135 self.assertEqual(p, self.cls("/") / "foo" / "bar" / "baz/quux/zoot") 136 self.assertEqual(p, self.cls("/foo") / "bar" / "baz/quux/zoot") 137 self.assertEqual(p, self.cls("/foo/bar") / "baz/quux/zoot") 138 139 def test_joinpath(self): 140 p = self.cls("/foo/bar/baz/quux/zoot") 141 self.assertEqual( 142 p, 143 self.cls("/foo").joinpath("bar", "baz", "quux/zoot")) 144 145 def test_nameAttribute(self): 146 self.assertEqual(self.cls("/").name, "") 147 148 p = self.cls("/foo/bar/baz/quux/zoot") 149 self.assertEqual(p.name, "zoot") 150 151 def test_partsAttribute(self): 152 self.assertEqual(self.cls("/").parts, ("/",)) 153 154 p = self.cls("/foo/bar/baz/quux/zoot") 155 self.assertEqual(p.parts, ("/", "foo", "bar", "baz", "quux", "zoot")) 156 157 def test_parentsAttribute(self): 158 def pathify(*args): 159 return tuple( (self.cls(s) for s in args) ) 160 161 p = self.cls("/") 162 self.assertEqual(tuple(p.parents), pathify()) # empty tuple 163 164 p = self.cls("/foo") 165 self.assertEqual(tuple(p.parents), pathify("/")) 166 167 p = self.cls("/foo/bar") 168 self.assertEqual(tuple(p.parents), pathify("/foo", "/")) 169 170 p = self.cls("/foo/bar/baz") 171 self.assertEqual(tuple(p.parents), pathify("/foo/bar", "/foo", "/")) 172 173 def test_parentAttribute(self): 174 def pathify(s): 175 return self.cls(s) 176 177 p = self.cls("/") 178 self.assertEqual(p.parent, pathify("/")) 179 180 p = self.cls("/foo") 181 self.assertEqual(p.parent, pathify("/")) 182 183 p = self.cls("/foo/bar") 184 self.assertEqual(p.parent, pathify("/foo")) 185 186 p = self.cls("/foo/bar/baz") 187 self.assertEqual(p.parent, pathify("/foo/bar")) 188 189 def test_suffixAttribute(self): 190 p = self.cls("/") 191 self.assertEqual(p.suffix, '') 192 193 p = self.cls("/foo/bar/baz.py") 194 self.assertEqual(p.suffix, '.py') 195 196 p = self.cls("/foo/bar/baz.py.bla") 197 self.assertEqual(p.suffix, '.bla') 198 199 p = self.cls("/foo/bar/baz") 200 self.assertEqual(p.suffix, '') 201 202 def test_suffixesAttribute(self): 203 p = self.cls("/") 204 self.assertEqual(p.suffixes, []) 205 206 p = self.cls("/foo/bar/baz.py") 207 self.assertEqual(p.suffixes, ['.py']) 208 209 p = self.cls("/foo/bar/baz.py.bla") 210 self.assertEqual(p.suffixes, ['.py', '.bla']) 211 212 p = self.cls("/foo/bar/baz") 213 self.assertEqual(p.suffixes, []) 214 215 def test_stemAttribute(self): 216 p = self.cls("/") 217 self.assertEqual(p.stem, '') 218 219 p = self.cls("/foo/bar/baz.py") 220 self.assertEqual(p.stem, 'baz') 221 222 p = self.cls("/foo/bar/baz.py.bla") 223 self.assertEqual(p.stem, 'baz.py') 224 225 def test_asRelative(self): 226 self.assertEqual(self.cls("/").asRelative(), "") 227 self.assertEqual(self.cls("/foo/bar/baz/quux/zoot").asRelative(), 228 "foo/bar/baz/quux/zoot") 229 230 def test_relativeTo(self): 231 self.assertEqual(self.cls("").relativeTo(""), "") 232 self.assertEqual(self.cls("").relativeTo("/"), "") 233 self.assertEqual(self.cls("/").relativeTo("/"), "") 234 self.assertEqual(self.cls("/").relativeTo(""), "") 235 236 p = self.cls("/foo/bar/baz/quux/zoot") 237 238 self.assertEqual(p.relativeTo(""), "foo/bar/baz/quux/zoot") 239 self.assertEqual(p.relativeTo("/"), "foo/bar/baz/quux/zoot") 240 241 self.assertEqual(p.relativeTo("foo"), "bar/baz/quux/zoot") 242 self.assertEqual(p.relativeTo("foo/"), "bar/baz/quux/zoot") 243 self.assertEqual(p.relativeTo("/foo"), "bar/baz/quux/zoot") 244 self.assertEqual(p.relativeTo("/foo/"), "bar/baz/quux/zoot") 245 246 self.assertEqual(p.relativeTo("foo/bar/baz"), "quux/zoot") 247 self.assertEqual(p.relativeTo("foo/bar/baz/"), "quux/zoot") 248 self.assertEqual(p.relativeTo("/foo/bar/baz"), "quux/zoot") 249 self.assertEqual(p.relativeTo("/foo/bar/baz/"), "quux/zoot") 250 251 with self.assertRaises(ValueError): 252 p.relativeTo("/foo/ba") 253 254 with self.assertRaises(ValueError): 255 p.relativeTo("/foo/balloon") 256 257 def test_withName(self): 258 p = self.cls("/foo/bar/baz/quux/zoot") 259 260 self.assertEqual(p.withName(""), 261 VirtualPath("/foo/bar/baz/quux")) 262 self.assertEqual(p.withName("pouet"), 263 VirtualPath("/foo/bar/baz/quux/pouet")) 264 self.assertEqual(p.withName("pouet/zdong"), 265 VirtualPath("/foo/bar/baz/quux/pouet/zdong")) 266 267 # The self.cls object has no 'name' (referring to the 'name' property) 268 with self.assertRaises(ValueError): 269 self.cls("").withName("foobar") 270 271 with self.assertRaises(ValueError): 272 self.cls("/").withName("foobar") 273 274 def test_withSuffix(self): 275 p = self.cls("/foo/bar/baz.tar.gz") 276 self.assertEqual(p.withSuffix(".bz2"), 277 VirtualPath("/foo/bar/baz.tar.bz2")) 278 p = self.cls("/foo/bar/baz") 279 self.assertEqual(p.withSuffix(".tar.xz"), 280 VirtualPath("/foo/bar/baz.tar.xz")) 281 282 # The self.cls object has no 'name' (referring to the 'name' property) 283 with self.assertRaises(ValueError): 284 self.cls("/foo/bar/baz.tar.gz").withSuffix("no-leading-dot") 285 286 with self.assertRaises(ValueError): 287 # The root virtual path ('/') can't be used for this 288 self.cls("/").withSuffix(".foobar") 289 290 291class TestVirtualPath(unittest.TestCase, VirtualPathCommonTests): 292 """Tests for the VirtualPath class. 293 294 These are the tests using the common infrastructure from 295 VirtualPathCommonTests. 296 297 """ 298 299 cls = VirtualPath 300 301class TestVirtualPathSpecific(unittest.TestCase): 302 """Tests specific to the VirtualPath class.""" 303 304 def test_isHashableType(self): 305 p = VirtualPath("/foo") 306 self.assertTrue(isinstance(p, collections.Hashable)) 307 308 def test_insideSet(self): 309 l1 = [ VirtualPath("/foo/bar"), 310 VirtualPath("/foo/baz") ] 311 l2 = l1 + [ VirtualPath("/foo/bar") ] # l2 has a duplicate element 312 313 # Sets allow one to ignore duplicate elements when comparing 314 self.assertEqual(set(l1), set(l2)) 315 self.assertEqual(frozenset(l1), frozenset(l2)) 316 317 318class TestMutableVirtualPath(unittest.TestCase, VirtualPathCommonTests): 319 """Tests for the MutableVirtualPath class. 320 321 These are the tests using the common infrastructure from 322 VirtualPathCommonTests. 323 324 """ 325 326 cls = MutableVirtualPath 327 328class TestMutableVirtualPathSpecific(unittest.TestCase): 329 """Tests specific to the MutableVirtualPath class.""" 330 331 def test_mixedComparisons(self): 332 self.assertTrue( 333 VirtualPath("/abc/def").samePath(MutableVirtualPath("/abc/def"))) 334 self.assertTrue( 335 VirtualPath("/abc//def").samePath(MutableVirtualPath("/abc/def"))) 336 self.assertTrue( 337 VirtualPath("/abc/def/").samePath(MutableVirtualPath("/abc/def"))) 338 339 self.assertTrue( 340 MutableVirtualPath("/abc/def").samePath(VirtualPath("/abc/def"))) 341 self.assertTrue( 342 MutableVirtualPath("/abc//def").samePath(VirtualPath("/abc/def"))) 343 self.assertTrue( 344 MutableVirtualPath("/abc/def/").samePath(VirtualPath("/abc/def"))) 345 346 def test_inPlacePathConcatenation(self): 347 p = VirtualPath("/foo/bar/baz/quux/zoot") 348 349 q = MutableVirtualPath("/foo") 350 q /= "bar" 351 q /= "baz/quux/zoot" 352 353 self.assertTrue(p.samePath(q)) 354 355 def test_isNotHashableType(self): 356 p = MutableVirtualPath("/foo") 357 self.assertFalse(isinstance(p, collections.Hashable)) 358 359 360if __name__ == "__main__": 361 unittest.main() 362