test_snapshots.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. import unittest
  9. from unittest.mock import patch
  10. from snapshot_folder.snapshot_folder import FolderSnapshot
  11. class empty_object():
  12. pass
  13. def fakestat(file_path):
  14. f = empty_object()
  15. f.st_mtime = 12345
  16. return f
  17. @patch('os.stat', side_effect=fakestat)
  18. @patch('os.walk')
  19. def test_CreateSnapshot_sanity(mock_os_walk, mock_os_stat):
  20. mock_os_walk.return_value = [
  21. # this mocks os.walk, which always returns a tuple of (root name, folder names, file names)
  22. ( '', # root name
  23. ['subfolder1', 'subfolder2', 'subfolder3'], # folders in here
  24. ['file1.cpp'], # files in here
  25. ),
  26. ( 'subfolder1',
  27. [],
  28. ['file1.cpp', 'file2.cpp'], # file1 is same name as above, but different folder name!
  29. ),
  30. ( 'subfolder2',
  31. [''],
  32. [], # empty folders still get tracked
  33. ),
  34. ( 'subfolder3',
  35. ['subfolder4'], # folders only containing folders
  36. [],
  37. ),
  38. ( 'subfolder3/subfolder4',
  39. [],
  40. ['file4.cpp'],
  41. )
  42. ]
  43. snap = FolderSnapshot.CreateSnapshot('.', ignore_patterns=[])
  44. assert 'subfolder1' in snap.folder_paths
  45. assert 'subfolder2' in snap.folder_paths
  46. assert 'subfolder3' in snap.folder_paths
  47. assert 'subfolder3/subfolder4' in snap.folder_paths
  48. assert 'file1.cpp' in snap.file_modtimes
  49. assert 'subfolder1/file1.cpp' in snap.file_modtimes
  50. assert 'subfolder1/file2.cpp' in snap.file_modtimes
  51. assert 'subfolder3/subfolder4/file4.cpp' in snap.file_modtimes
  52. @patch('os.stat', side_effect=fakestat)
  53. @patch('os.walk')
  54. def test_CreateSnapshot_obeys_exclusions(mock_os_walk, mock_os_stat):
  55. mock_os_walk.return_value = [
  56. ( '.',
  57. ['sub_buildfolder', 'build', 'build_mac', 'normal_subfolder'], # sneaky trap, sub_buildfolder should not be ignored
  58. ['file.tif', 'file_not_a_tif.bmp'],
  59. ),
  60. ( 'sub_buildfolder', # should not be ignored, its not a match to build_*
  61. [],
  62. ['file2.cpp', 'file2.tif'],
  63. ),
  64. ( 'build_mac',
  65. ['normalfolder'], # does not have build in its name but should still be ignored because parent is
  66. ['file3.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
  67. ),
  68. ( 'build_mac/normalfolder',
  69. [],
  70. ['file4.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
  71. ),
  72. ( 'build',
  73. [],
  74. ['file4.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
  75. ),
  76. ( 'normal_subfolder',
  77. ['build'], # a matching folder in a subfolder
  78. [],
  79. ),
  80. ( 'normal_subfolder/build',
  81. ['file.txt'], # a matching folder in a subfolder
  82. [],
  83. )
  84. ]
  85. snap = FolderSnapshot.CreateSnapshot('.', ignore_patterns=['*.tif', 'build', 'build_*'])
  86. assert 'sub_buildfolder' in snap.folder_paths
  87. assert 'file_not_a_tif.bmp' in snap.file_modtimes
  88. assert 'sub_buildfolder/file2.cpp' in snap.file_modtimes
  89. assert 'normal_subfolder' in snap.folder_paths
  90. assert 'build' not in snap.folder_paths
  91. assert 'build_mac' not in snap.folder_paths
  92. assert 'normal_subfolder/build' not in snap.folder_paths
  93. assert 'build_mac/normalfolder' not in snap.folder_paths
  94. assert 'file1.tif' not in snap.file_modtimes
  95. assert 'sub_buildfolder/file2.tif' not in snap.file_modtimes
  96. assert 'build/file3.cpp' not in snap.file_modtimes
  97. assert 'build_mac/file4.cpp' not in snap.file_modtimes
  98. assert 'build_mac/normalfolder/file4.cpp' not in snap.file_modtimes
  99. assert 'normal_subfolder/build/file.txt' not in snap.file_modtimes
  100. def test_CompareSnapshots_identical_snapshots_nodiffs():
  101. # emulate identical snapshots
  102. snap1 = FolderSnapshot()
  103. snap1.folder_paths = ['myfolder1', 'myfolder2']
  104. snap1.file_modtimes = {
  105. 'rootfile.txt' : 12345,
  106. 'myfolder1/file.txt' : 12345
  107. }
  108. snap2 = FolderSnapshot()
  109. snap2.folder_paths = ['myfolder1', 'myfolder2']
  110. snap2.file_modtimes = {
  111. 'rootfile.txt' : 12345,
  112. 'myfolder1/file.txt' : 12345
  113. }
  114. changes = FolderSnapshot.CompareSnapshots(snap1, snap2)
  115. assert not changes.any_changed()
  116. changed_things = [c for c in changes.enumerate_changes()]
  117. assert not changed_things
  118. def test_CompareSnapshots_two_of_each_kind_of_change():
  119. # emulate identical snapshots
  120. snap1 = FolderSnapshot()
  121. snap1.folder_paths = ['myfolder1', 'myfolder2', 'myfolder3', 'myfolder4']
  122. snap1.file_modtimes = {
  123. 'rootfile.txt' : 12345,
  124. 'rootfile2.txt' : 12345,
  125. 'rootfile3.txt' : 12345,
  126. 'myfolder1/file.txt' : 12345,
  127. 'myfolder2/file2.txt' : 12345,
  128. 'myfolder2/file3.txt' : 12345,
  129. }
  130. snap2 = FolderSnapshot()
  131. # myfolder1 deleted
  132. # myfolder2 unchanged
  133. # myfolder3 unchanged
  134. # myfolder4 deleted
  135. # myfolder5 as well as its subfolder, myfolder6 added
  136. snap2.folder_paths = ['myfolder3', 'myfolder2', 'myfolder5', 'myfolder5/myfolder6']
  137. snap2.file_modtimes = {
  138. 'rootfile2.txt' : 12345, # unchanged, in root
  139. 'rootfile3.txt' : 33333, # modified
  140. 'rootfile4.txt' : 12345, # a new file
  141. # myfolder1 is deleted, so myfolder1/file.txt should show up as deleted
  142. 'myfolder2/file2.txt' : 12345, # unchanged, but in subfolder
  143. 'myfolder2/file3.txt' : 33333, # modified in subfolder
  144. 'myfolder5/file4.txt' : 12345, # a new file in a new folder
  145. }
  146. changes = FolderSnapshot.CompareSnapshots(snap1, snap2)
  147. assert changes.any_changed()
  148. changed_things = [c for c in changes.enumerate_changes()]
  149. # folders
  150. for expected_element in [
  151. ('FOLDER_ADDED', 'myfolder5'),
  152. ('FOLDER_ADDED', 'myfolder5/myfolder6'),
  153. ('FOLDER_DELETED', 'myfolder1'),
  154. ('FOLDER_DELETED', 'myfolder4'),
  155. ('DELETED', 'rootfile.txt'),
  156. ('DELETED', 'myfolder1/file.txt'),
  157. ('ADDED', 'rootfile4.txt'),
  158. ('ADDED', 'myfolder5/file4.txt'),
  159. ('CHANGED', 'rootfile3.txt'),
  160. ('CHANGED', 'myfolder2/file3.txt')
  161. ]:
  162. assert expected_element in changed_things
  163. changed_things.remove(expected_element)
  164. # every time we did an assert above, we removed the matching element
  165. # from the change list. This means that the change list should now be empty
  166. # since everything that changed has been accounted for
  167. assert not changed_things, f"Unexpected change {changed_things}"