You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/scripts/detect_file_changes/snapshot_folder/tests/test_snapshots.py

190 lines
7.1 KiB
Python

#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import unittest
from unittest.mock import patch
from snapshot_folder.snapshot_folder import FolderSnapshot
class empty_object():
pass
def fakestat(file_path):
f = empty_object()
f.st_mtime = 12345
return f
@patch('os.stat', side_effect=fakestat)
@patch('os.walk')
def test_CreateSnapshot_sanity(mock_os_walk, mock_os_stat):
mock_os_walk.return_value = [
# this mocks os.walk, which always returns a tuple of (root name, folder names, file names)
( '', # root name
['subfolder1', 'subfolder2', 'subfolder3'], # folders in here
['file1.cpp'], # files in here
),
( 'subfolder1',
[],
['file1.cpp', 'file2.cpp'], # file1 is same name as above, but different folder name!
),
( 'subfolder2',
[''],
[], # empty folders still get tracked
),
( 'subfolder3',
['subfolder4'], # folders only containing folders
[],
),
( 'subfolder3/subfolder4',
[],
['file4.cpp'],
)
]
snap = FolderSnapshot.CreateSnapshot('.', ignore_patterns=[])
assert 'subfolder1' in snap.folder_paths
assert 'subfolder2' in snap.folder_paths
assert 'subfolder3' in snap.folder_paths
assert 'subfolder3/subfolder4' in snap.folder_paths
assert 'file1.cpp' in snap.file_modtimes
assert 'subfolder1/file1.cpp' in snap.file_modtimes
assert 'subfolder1/file2.cpp' in snap.file_modtimes
assert 'subfolder3/subfolder4/file4.cpp' in snap.file_modtimes
@patch('os.stat', side_effect=fakestat)
@patch('os.walk')
def test_CreateSnapshot_obeys_exclusions(mock_os_walk, mock_os_stat):
mock_os_walk.return_value = [
( '.',
['sub_buildfolder', 'build', 'build_mac', 'normal_subfolder'], # sneaky trap, sub_buildfolder should not be ignored
['file.tif', 'file_not_a_tif.bmp'],
),
( 'sub_buildfolder', # should not be ignored, its not a match to build_*
[],
['file2.cpp', 'file2.tif'],
),
( 'build_mac',
['normalfolder'], # does not have build in its name but should still be ignored because parent is
['file3.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
),
( 'build_mac/normalfolder',
[],
['file4.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
),
( 'build',
[],
['file4.cpp'], # even though it doesnt match a rule itself, it should be omitted since its in build
),
( 'normal_subfolder',
['build'], # a matching folder in a subfolder
[],
),
( 'normal_subfolder/build',
['file.txt'], # a matching folder in a subfolder
[],
)
]
snap = FolderSnapshot.CreateSnapshot('.', ignore_patterns=['*.tif', 'build', 'build_*'])
assert 'sub_buildfolder' in snap.folder_paths
assert 'file_not_a_tif.bmp' in snap.file_modtimes
assert 'sub_buildfolder/file2.cpp' in snap.file_modtimes
assert 'normal_subfolder' in snap.folder_paths
assert 'build' not in snap.folder_paths
assert 'build_mac' not in snap.folder_paths
assert 'normal_subfolder/build' not in snap.folder_paths
assert 'build_mac/normalfolder' not in snap.folder_paths
assert 'file1.tif' not in snap.file_modtimes
assert 'sub_buildfolder/file2.tif' not in snap.file_modtimes
assert 'build/file3.cpp' not in snap.file_modtimes
assert 'build_mac/file4.cpp' not in snap.file_modtimes
assert 'build_mac/normalfolder/file4.cpp' not in snap.file_modtimes
assert 'normal_subfolder/build/file.txt' not in snap.file_modtimes
def test_CompareSnapshots_identical_snapshots_nodiffs():
# emulate identical snapshots
snap1 = FolderSnapshot()
snap1.folder_paths = ['myfolder1', 'myfolder2']
snap1.file_modtimes = {
'rootfile.txt' : 12345,
'myfolder1/file.txt' : 12345
}
snap2 = FolderSnapshot()
snap2.folder_paths = ['myfolder1', 'myfolder2']
snap2.file_modtimes = {
'rootfile.txt' : 12345,
'myfolder1/file.txt' : 12345
}
changes = FolderSnapshot.CompareSnapshots(snap1, snap2)
assert not changes.any_changed()
changed_things = [c for c in changes.enumerate_changes()]
assert not changed_things
def test_CompareSnapshots_two_of_each_kind_of_change():
# emulate identical snapshots
snap1 = FolderSnapshot()
snap1.folder_paths = ['myfolder1', 'myfolder2', 'myfolder3', 'myfolder4']
snap1.file_modtimes = {
'rootfile.txt' : 12345,
'rootfile2.txt' : 12345,
'rootfile3.txt' : 12345,
'myfolder1/file.txt' : 12345,
'myfolder2/file2.txt' : 12345,
'myfolder2/file3.txt' : 12345,
}
snap2 = FolderSnapshot()
# myfolder1 deleted
# myfolder2 unchanged
# myfolder3 unchanged
# myfolder4 deleted
# myfolder5 as well as its subfolder, myfolder6 added
snap2.folder_paths = ['myfolder3', 'myfolder2', 'myfolder5', 'myfolder5/myfolder6']
snap2.file_modtimes = {
'rootfile2.txt' : 12345, # unchanged, in root
'rootfile3.txt' : 33333, # modified
'rootfile4.txt' : 12345, # a new file
# myfolder1 is deleted, so myfolder1/file.txt should show up as deleted
'myfolder2/file2.txt' : 12345, # unchanged, but in subfolder
'myfolder2/file3.txt' : 33333, # modified in subfolder
'myfolder5/file4.txt' : 12345, # a new file in a new folder
}
changes = FolderSnapshot.CompareSnapshots(snap1, snap2)
assert changes.any_changed()
changed_things = [c for c in changes.enumerate_changes()]
# folders
for expected_element in [
('FOLDER_ADDED', 'myfolder5'),
('FOLDER_ADDED', 'myfolder5/myfolder6'),
('FOLDER_DELETED', 'myfolder1'),
('FOLDER_DELETED', 'myfolder4'),
('DELETED', 'rootfile.txt'),
('DELETED', 'myfolder1/file.txt'),
('ADDED', 'rootfile4.txt'),
('ADDED', 'myfolder5/file4.txt'),
('CHANGED', 'rootfile3.txt'),
('CHANGED', 'myfolder2/file3.txt')
]:
assert expected_element in changed_things
changed_things.remove(expected_element)
# every time we did an assert above, we removed the matching element
# from the change list. This means that the change list should now be empty
# since everything that changed has been accounted for
assert not changed_things, f"Unexpected change {changed_things}"