# -*- encoding: UTF8 -*-
# Test harness for CheckFolderStructure.py
from __future__ import print_function
import sys
import unittest
import os
import re
import P4
from p4testutils import TestCase, P4Server, localDirectory, create_file, append_to_file
python3 = sys.version_info[0] >= 3
if python3:
from unittest.mock import Mock
else:
from mock import Mock
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
from CheckFolderStructure import CheckFolderStructure, NewDirFinder
from WorkflowTriggers import GroupMemberChecker
os.environ["LOGS"] = "."
LOGGER_NAME = "TestCheckFolderStructure"
LOG_FILE = "log-TestCheckFolderStructure.log"
class TestCheckFolderStructure(TestCase):
def __init__(self, methodName='runTest'):
super(TestCheckFolderStructure, self).__init__(LOGGER_NAME, LOG_FILE, methodName=methodName)
def setUp(self):
self.server = P4Server()
trigpath = os.path.join(parent_dir, "CheckFolderStructure.py")
self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "~test_config.yaml")
p4 = self.server.p4
self.p4 = p4
p4.logger = self.logger
depot = p4.fetch_depot("streams")
depot["Type"] = "stream"
p4.save_depot(depot)
# This works if no spaces in server root pathname!
port = p4.port.replace('"', '')
self.logger.debug("port: |%s|" % port)
triggers = p4.fetch_triggers()
triggers['Triggers'] = ['check-folder-structure change-submit //streams/... " python {} -p %quote%{}%quote% '
'-u {} -c {} %change% "'.format(trigpath, port, p4.user, self.config_path),
]
self.logger.debug(triggers)
p4.save_triggers(triggers)
# Reconnect to pick up changes
p4.disconnect()
p4.connect()
stream = p4.fetch_stream("-tmainline", "//streams/main")
p4.save_stream(stream)
client = p4.fetch_client()
client['Stream'] = "//streams/main"
p4.save_client(client)
def tearDown(self):
pass
def testCheckNewDirs(self):
"""unit testing with mock framework"""
mock_p4 = Mock()
mock_p4.run_dirs.return_value = []
df = NewDirFinder(mock_p4)
level = 0
self.assertFalse(df.findNewDir("//depot/stream/fred.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir/fred.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir/subdir/fred.txt", level))
mock_p4.run_dirs.return_value = []
df = NewDirFinder(mock_p4)
level = 1
self.assertFalse(df.findNewDir("//depot/stream/fred.txt", level))
self.assertEqual("//depot/stream/dir", df.findNewDir("//depot/stream/dir/fred.txt", level))
mock_p4.run_dirs.return_value = [{'dir': "//depot/stream/dir"}]
call_count = mock_p4.run_dirs.call_count
df = NewDirFinder(mock_p4)
newDir = df.findNewDir("//depot/stream/dir/fred.txt", level)
self.assertFalse(newDir)
call_count += 1
self.assertEqual(call_count, mock_p4.run_dirs.call_count)
# Test caching - so don't expect call count to be incremented even if we call it
# multiple times
mock_p4.run_dirs.return_value = [{'dir': "//depot/stream/dir"}]
call_count = mock_p4.run_dirs.call_count
df = NewDirFinder(mock_p4)
self.assertFalse(df.findNewDir("//depot/stream/dir/fred.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir/fred2.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir/fred3.txt", level))
call_count += 1
self.assertEqual(call_count, mock_p4.run_dirs.call_count)
# Test multiple dirs under that level
mock_p4.run_dirs.return_value = [{'dir': "//depot/stream/dir1"},
{'dir': "//depot/stream/dir2"}]
call_count = mock_p4.run_dirs.call_count
df = NewDirFinder(mock_p4)
self.assertFalse(df.findNewDir("//depot/stream/dir1/fred.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir2/fred2.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir2/fred3.txt", level))
self.assertEqual("//depot/stream/dir3", df.findNewDir("//depot/stream/dir3/fred4.txt", level))
call_count += 1
self.assertEqual(call_count, mock_p4.run_dirs.call_count)
# Test multiple depots - so multiple calls to dirs
mock_p4.run_dirs.side_effect = [[{'dir': "//depotA/streamX/dir1"}],
[{'dir': "//depotB/streamY/dir2"}]]
call_count = mock_p4.run_dirs.call_count
df = NewDirFinder(mock_p4)
self.assertFalse(df.findNewDir("//depotA/streamX/dir1/fred.txt", level))
self.assertFalse(df.findNewDir("//depotB/streamY/dir2/fred2.txt", level))
self.assertEqual("//depotB/streamY/dir3", df.findNewDir("//depotB/streamY/dir3/fred4.txt", level))
call_count += 2
self.assertEqual(call_count, mock_p4.run_dirs.call_count)
mock_p4.run_dirs.side_effect = None
# Now test level = 2
# Test multiple dirs under that level
level = 2
call_count = mock_p4.run_dirs.call_count
mock_p4.run_dirs.side_effect = [[{'dir': "//depot/stream/dir1"}],
[{'dir': "//depot/stream/dir1/dir2"}]]
df = NewDirFinder(mock_p4)
self.assertFalse(df.findNewDir("//depot/stream/dir1/fred.txt", level))
self.assertFalse(df.findNewDir("//depot/stream/dir1/dir2/fred2.txt", level))
self.assertEqual("//depot/stream/dir3", df.findNewDir("//depot/stream/dir3/fred4.txt", level))
self.assertEqual("//depot/stream/dir1/dir3", df.findNewDir("//depot/stream/dir1/dir3/fred4.txt", level))
call_count += 2
self.assertEqual(call_count, mock_p4.run_dirs.call_count)
def testGroupMemberChecker(self):
"""test group membership"""
mock_p4 = Mock()
mock_p4.run_groups.return_value = [
{'user': 'user1', 'group': 'G1', 'isSubGroup': '0', 'isOwner': '0', 'isUser': '1'},
{'user': 'user2', 'group': 'G1', 'isSubGroup': '0', 'isOwner': '0', 'isUser': '1'},
{'user': 'user3', 'group': 'G3', 'isSubGroup': '0', 'isOwner': '1', 'isUser': '0'},
# Next record means G1 is a subgroup of G2
{'user': 'G1', 'group': 'G2', 'isSubGroup': '1', 'isOwner': '0', 'isUser': '0'},
{'user': 'G2', 'group': 'G3', 'isSubGroup': '1', 'isOwner': '0', 'isUser': '0'}]
gchk = GroupMemberChecker(mock_p4)
exceptions_list = ['user1']
self.assertTrue(gchk.IsMember('user1', exceptions_list))
self.assertFalse(gchk.IsMember('user2', exceptions_list))
exceptions_list = ['user1', 'user2']
self.assertTrue(gchk.IsMember('user2', exceptions_list))
exceptions_list = ['G1']
self.assertTrue(gchk.IsMember('user1', exceptions_list))
self.assertTrue(gchk.IsMember('user2', exceptions_list))
exceptions_list = ['G2']
self.assertTrue(gchk.IsMember('user1', exceptions_list))
self.assertTrue(gchk.IsMember('user2', exceptions_list))
self.assertFalse(gchk.IsMember('user3', exceptions_list))
exceptions_list = ['G3']
# G3 has subgroup G2 which has subgroup G1
self.assertTrue(gchk.IsMember('user1', exceptions_list))
self.assertTrue(gchk.IsMember('user2', exceptions_list))
self.assertTrue(gchk.IsMember('user3', exceptions_list))
self.assertFalse(gchk.IsMember('user4', exceptions_list))
def testCheckFolderStructure(self):
"""check that it works when called as a trigger"""
p4 = self.p4
# Create a directory
dir1 = localDirectory(self.server.client_root, "dir1")
file1 = os.path.join(dir1, "file1")
create_file(file1, "Some content")
# Allow new dirs at all levels
with open(self.config_path, "w") as f:
f.write("""
msg_new_folder_not_allowed:
- ""
- "You are not allowed to create new folders at this level in the stream."
- "Please add your files/folders to an existing folder at this level."
projects:
- name: ProjectA
new_folder_allowed_level: 0
depot_paths:
- //streams/...
""")
p4.run('add', file1)
result = p4.run('submit', '-d', 'file1 added')
self.assertTrue('submittedChange' in result[-1])
# Now block level 1
with open(self.config_path, "w") as f:
f.write("""
msg_new_folder_not_allowed:
- ""
- "You are not allowed to create new folders at this level in the stream."
- "Please add your files/folders to an existing folder at this level."
projects:
- name: ProjectA
new_folder_allowed_level: 1
depot_paths:
- //streams/...
""")
dir2 = localDirectory(self.server.client_root, "dir2")
file2 = os.path.join(dir2, "file2")
create_file(file2, "Some content")
p4.run('add', file2)
try:
p4.run('submit', '-d', 'file2 added')
self.assertTrue(False, "Expected exception not found")
except P4.P4Exception as e:
self.assertRegex(str(e), r"You are not allowed to create new folders")
p4.run('revert', file2)
# But creating file in existing dir is OK
file3 = os.path.join(dir1, "file3")
create_file(file3, "Some content")
p4.run('add', file3)
result = p4.run('submit', '-d', 'file3 added')
self.assertTrue('submittedChange' in result[-1])
# Moving a file also OK in existing dir but not a new dir
file4 = os.path.join(dir1, "file4")
p4.run('edit', file1)
p4.run('move', file1, file4)
result = p4.run('submit', '-d', 'file1 moved')
self.assertTrue('submittedChange' in result[-1])
file5 = os.path.join(dir2, "file5")
p4.run('edit', file3)
p4.run('move', file3, file5)
try:
p4.run('submit', '-d', 'file3 renamed')
self.assertTrue(False, "Expected exception not found")
except P4.P4Exception as e:
self.assertRegex(str(e), r"You are not allowed to create new folders")
if __name__ == '__main__':
unittest.main()
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #3 | 27331 | C. Thomas Tyler |
Released SDP 2020.1.27325 (2021/01/29). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| #2 | 26470 | C. Thomas Tyler |
Released SDP 2019.3.26468 (2020/04/10). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| #1 | 25933 | C. Thomas Tyler |
Released SDP 2019.2.25923 (2019/08/05). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| //guest/perforce_software/sdp/dev/Server/Unix/p4/common/bin/triggers/tests/TestCheckFolderStructure.py | |||||
| #2 | 25626 | Robert Cowham | Add group membership checking for exceptions list (to bypass trigger). | ||
| #1 | 25625 | Robert Cowham |
First version of trigger to enforce stream folder naming structure - control at which level in a stream users are allowed to create new folders. |
||