# -*- encoding: UTF8 -*-
# tests missing
# - maximum
# - preflight check
from __future__ import print_function
import sys
import time
import P4
import logging
import io
import codecs
import logutils
import unittest, os, types, shutil, stat
from subprocess import Popen, PIPE
if sys.version_info[0] >= 3:
from configparser import ConfigParser
from io import StringIO
else:
from ConfigParser import ConfigParser
from StringIO import StringIO
import P4Transfer
P4D = "p4d"
P4USER = "testuser"
P4CLIENT = "test_ws"
TRANSFER_CLIENT = "transfer"
TRANSFER_CONFIG = "transfer.cfg"
TEST_COUNTER_NAME = "P4Transfer"
INTEG_ENGINE = 3
def onRmTreeError(function, path, exc_info):
os.chmod(path, stat.S_IWRITE)
os.remove(path)
def ensureDirectory(directory):
if not os.path.isdir(directory):
os.mkdir(directory)
def localDirectory(root, dir_name):
"Create and ensure it exists"
dir_path = os.path.join(root, dir_name)
ensureDirectory(dir_path)
return dir_path
def create_file(file_name, contents):
"Create file with specified contents"
with open(file_name, 'w') as f:
print(contents, file=f)
def append_to_file(file_name, contents):
"Append contents to file"
with open(file_name, 'a+') as f:
f.write(contents)
class P4Server:
def __init__(self, root):
self.root = root
self.server_root = os.path.join(root, "server")
self.client_root = os.path.join(root, "client")
ensureDirectory(self.root)
ensureDirectory(self.server_root)
ensureDirectory(self.client_root)
self.p4d = P4D
self.port = "rsh:%s -r \"%s\" -L log -i" % (self.p4d, self.server_root)
self.p4 = P4.P4()
self.p4.port = self.port
self.p4.user = P4USER
self.p4.client = P4CLIENT
self.p4.connect()
self.p4.run_depots() # triggers creation of the user
self.p4.run_configure('set', 'dm.integ.engine=%d' % INTEG_ENGINE)
self.p4.disconnect() # required to pick up the configure changes
self.p4.connect()
self.client_name = P4CLIENT
client = self.p4.fetch_client(self.client_name)
client._root = self.client_root
self.p4.save_client(client)
def shutDown(self):
if self.p4.connected():
self.p4.disconnect()
def createTransferClient(self, name, root):
pass
def enableUnicode(self):
cmd = [self.p4d, "-r", self.server_root, "-L", "log", "-vserver=3", "-xi"]
f = Popen(cmd, stdout=PIPE).stdout
for s in f.readlines():
pass
f.close()
def getCounter(self):
"Returns value of counter as integer"
result = self.p4.run('counter', TEST_COUNTER_NAME)
if result and 'counter' in result[0]:
return int(result[0]['value'])
return 0
class TestP4Transfer(unittest.TestCase):
def setUp(self):
self.setDirectories()
self.stdoutput = StringIO()
logger = logutils.getLogger(P4Transfer.LOGGER_NAME, self.stdoutput)
def tearDown(self):
self.source.shutDown()
self.target.shutDown()
time.sleep(1)
self.cleanupTestTree()
def setDirectories(self):
self.startdir = os.getcwd()
self.transfer_root = os.path.join(self.startdir, 'transfer')
self.cleanupTestTree()
ensureDirectory(self.transfer_root)
self.source = P4Server(os.path.join(self.transfer_root, 'source'))
self.target = P4Server(os.path.join(self.transfer_root, 'target'))
self.transfer_client_root = localDirectory(self.transfer_root, 'transfer_client')
def cleanupTestTree(self):
os.chdir(self.startdir)
if os.path.isdir(self.transfer_root):
shutil.rmtree(self.transfer_root, False, onRmTreeError)
def setupTransfer(self):
"""Creates client workspaces on source and target and a config file"""
source_client = self.source.p4.fetch_client(TRANSFER_CLIENT)
source_client._root = self.transfer_client_root
source_client._view = ['//depot/inside/... //%s/...' % TRANSFER_CLIENT]
self.source.p4.save_client(source_client)
target_client = self.target.p4.fetch_client('transfer')
target_client._root = self.transfer_client_root
target_client._view = ['//depot/import/... //%s/...' % TRANSFER_CLIENT]
self.target.p4.save_client(target_client)
# create the config file
self.parser = ConfigParser()
self.parser.add_section('source')
self.parser.set('source', 'p4port', self.source.port)
self.parser.set('source', 'p4user', P4USER)
self.parser.set('source', 'p4client', TRANSFER_CLIENT)
self.parser.add_section('target')
self.parser.set('target', 'p4port', self.target.port)
self.parser.set('target', 'p4user', P4USER)
self.parser.set('target', 'p4client', TRANSFER_CLIENT)
self.parser.add_section('general')
self.parser.set('general', 'logfile', os.path.join(self.transfer_root, 'temp', 'test.log'))
if not os.path.exists(os.path.join(self.transfer_root, 'temp')):
os.mkdir(os.path.join(self.transfer_root, 'temp'))
self.parser.set('general', 'counter_name', TEST_COUNTER_NAME)
# write the config file
self.transfer_cfg = os.path.join(self.transfer_root, TRANSFER_CONFIG)
with open(self.transfer_cfg, 'w') as f:
self.parser.write(f)
def run_P4Transfer(self, *args):
base_args = ['-c', self.transfer_cfg, '-s']
if args:
base_args.extend(args)
pt = P4Transfer.P4Transfer(*base_args)
result = pt.replicate()
return result
def assertCounters(self, sourceValue, targetValue):
sourceCounter = self.target.getCounter()
targetCounter = len(self.target.p4.run("changes"))
self.assertEqual(sourceCounter, sourceValue, "Source counter is not {} but {}".format(sourceValue, sourceCounter))
self.assertEqual(targetCounter, targetValue, "Target counter is not {} but {}".format(targetValue, targetCounter))
def testArgParsing(self):
self.setupTransfer()
args = ['-c', self.transfer_cfg, '-s']
pt = P4Transfer.P4Transfer(*args)
self.assertEqual(pt.options.config, self.transfer_cfg)
self.assertTrue(pt.options.stoponerror)
self.assertFalse(pt.options.preflight)
args = ['-c', self.transfer_cfg]
pt = P4Transfer.P4Transfer(*args)
def testAdd(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, 'Test content')
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
self.run_P4Transfer()
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 1, "Target does not have exactly one change")
self.assertEqual(changes[0]['change'], "1", "Target change is not 1")
files = self.target.p4.run_files('//depot/...')
self.assertEqual(len(files), 1, "Target does not have exactly one file")
self.assertEqual(files[0]['depotFile'], '//depot/import/inside_file1', "File not transferred to correct place")
self.assertCounters(1, 1)
def testUnicode(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = u"inside_file1uåäö".encode('mbcs')
localinside_file1 = os.path.join(inside, inside_file1)
create_file(localinside_file1, 'Test content')
self.source.p4.run_add('-f', localinside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
self.run_P4Transfer()
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 1, "Target does not have exactly one change")
self.assertEqual(changes[0]['change'], "1", "Target change is not 1")
files = self.target.p4.run_files('//depot/...')
self.assertEqual(len(files), 1, "Target does not have exactly one file")
self.assertEqual(files[0]['depotFile'], '//depot/import/%s' % inside_file1)
self.assertCounters(1, 1)
def testAddWildcardChars(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "@inside_file1")
create_file(inside_file1, 'Test content')
self.source.p4.run_add('-f', inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
self.run_P4Transfer()
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 1, "Target does not have exactly one change")
self.assertEqual(changes[0]['change'], "1", "Target change is not 1")
files = self.target.p4.run_files('//depot/...')
self.assertEqual(len(files), 1, "Target does not have exactly one file")
self.assertEqual(files[0]['depotFile'], '//depot/import/%40inside_file1', "File not transferred to correct place")
self.assertCounters(1, 1)
inside_file1Fixed = inside_file1.replace("@", "%40")
self.source.p4.run_edit(inside_file1Fixed)
create_file(inside_file1, 'Different stuff')
self.source.p4.run_submit('-d', 'inside_file1 modified')
self.run_P4Transfer()
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 2)
self.assertEqual(changes[0]['change'], "2")
def testEditAndDelete(self):
self.setupTransfer()
# add
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, 'Test content')
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', "inside_file1 added")
# edit
self.source.p4.run_edit(inside_file1)
append_to_file(inside_file1, 'More content')
self.source.p4.run_submit('-d', "inside_file1 edited")
self.run_P4Transfer()
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 2)
self.assertEqual(changes[0]['change'], "2")
self.assertCounters(2, 2)
# delete
self.source.p4.run_delete(inside_file1)
self.source.p4.run_submit('-d', "inside_file1 deleted")
self.run_P4Transfer()
self.assertCounters(3, 3)
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 3, "Target does not have exactly three changes")
filelog = self.target.p4.run_filelog('//depot/import/inside_file1')
self.assertEqual(filelog[0].revisions[0].action, 'delete', "Target has not been deleted")
# re-add
create_file(inside_file1, 'New content')
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', "Re-added")
self.run_P4Transfer()
self.assertCounters(4, 4)
filelog = self.target.p4.run_filelog('//depot/import/inside_file1')
self.assertEqual(filelog[0].revisions[0].action, 'add', "Target has not been re-added")
def testFileTypes(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, "Test content")
self.source.p4.run_add('-tbinary', inside_file1)
self.source.p4.run_submit('-d', "inside_file1 added")
self.run_P4Transfer()
self.assertCounters(1, 1)
filelog = self.target.p4.run_filelog('//depot/import/inside_file1')
self.assertEqual(filelog[0].revisions[0].type, 'binary', "File type is not binary, but %s" % filelog[0].revisions[0].type)
# change type to binary+x
self.source.p4.run_edit('-t+x', inside_file1)
append_to_file(inside_file1, "More content")
self.source.p4.run_submit('-d', "Type changed")
self.run_P4Transfer()
self.assertCounters(2, 2)
filelog = self.target.p4.run_filelog('//depot/import/inside_file1')
self.assertEqual(filelog[0].revisions[0].type, 'xbinary', "File type is not xbinary, but %s" % filelog[0].revisions[0].type)
# add ktext file
inside_file2 = os.path.join(inside, "inside_file2")
create_file(inside_file2, "$Id$\n$DateTime$")
self.source.p4.run_add('-t+k', inside_file2)
self.source.p4.run_submit('-d', "Ktext added")
self.run_P4Transfer()
self.assertCounters(3, 3)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].type, 'ktext', "File type is not ktext, but %s" % filelog[0].revisions[0].type)
verifyResult = self.target.p4.run_verify('-q', '//depot/import/inside_file2')
self.assertEqual(len(verifyResult), 0) # just to see that ktext gets transferred properly
content = self.target.p4.run_print('//depot/import/inside_file2')[1]
lines = content.split("\n")
self.assertEqual(lines[0], '$Id: //depot/import/inside_file2#1 $', "Content does not match : %s" % lines[0])
def testSimpleIntegrate(self):
self.setupTransfer()
# seed the integration
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, "Test content")
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
inside_file2 = os.path.join(inside, "inside_file2")
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_submit('-d', 'inside_file1 -> inside_file2')
result = self.run_P4Transfer()
# test integration
self.assertCounters(2, 2)
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 2, "Target does not have exactly two changes")
# seed edit/copy
self.source.p4.run_edit(inside_file1)
append_to_file(inside_file1, "More content")
self.source.p4.run_submit('-d', 'inside_file1 edited')
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_resolve('-at')
self.source.p4.run_submit('-d', 'inside_file1 -> inside_file2 (copy)')
result = self.run_P4Transfer()
self.assertCounters(4, 4)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(len(filelog[0].revisions), 2, "Not exactly 2 target revisions")
self.assertEqual(len(filelog[0].revisions[1].integrations), 1, "Not exactly 1 integration into target")
self.assertEqual(filelog[0].revisions[0].integrations[0].how, "copy from", "'How' is not copy from")
def testComplexIntegrate(self):
self.setupTransfer()
# seed the integration
content = """
Line 1
Line 2
Line 3
"""
content1 = """
Line 1
Line 2 - changed
Line 3
"""
content2 = """
Line 1
Line 2
Line 3 - changed
"""
content4 = """
Line 1
Line 2 - changed
Line 3 - differs
"""
content5 = """
Line 1
Line 2 - changed
Line 3 - edited
"""
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, content)
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
inside_file2 = os.path.join(inside, "inside_file2")
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_submit('-d', 'inside_file1 -> inside_file2')
# Prepare merge
self.source.p4.run_edit(inside_file1, inside_file2)
create_file(inside_file1, content1)
create_file(inside_file2, content2)
self.source.p4.run_submit('-d', "Changed both contents")
# Integrate with merge
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_resolve('-am')
self.source.p4.run_submit('-d', "Merged contents")
contentMerged = self.source.p4.run_print(inside_file2)[1]
sourceCounter = 4
targetCounter = 4
self.run_P4Transfer()
self.assertCounters(sourceCounter, targetCounter)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].integrations[0].how, 'merge from', "How is not merge from")
self.assertEqual(self.target.p4.run_print('//depot/import/inside_file2')[1], contentMerged, "Content not the same")
# Prepare integrate with edit
self.source.p4.run_edit(inside_file1, inside_file2)
create_file(inside_file1, content1)
self.source.p4.run_submit('-d', "Created a conflict")
# Integrate with edit
self.source.p4.run_integrate(inside_file1, inside_file2)
class EditResolve(P4.Resolver):
def resolve(self, mergeData):
create_file(mergeData.result_path, content5)
return 'ae'
self.source.p4.run_resolve(resolver=EditResolve())
self.source.p4.run_submit('-d', "Merge with edit")
sourceCounter += 2
targetCounter += 2
self.run_P4Transfer()
self.assertCounters(sourceCounter, targetCounter)
# Prepare ignore
self.source.p4.run_edit(inside_file1)
append_to_file(inside_file1, "For your eyes only")
self.source.p4.run_submit('-d', "Edit source again")
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_resolve('-ay') # ignore
self.source.p4.run_submit('-d', "Ignored change in inside_file1")
sourceCounter += 2
targetCounter += 2
self.run_P4Transfer()
self.assertCounters(sourceCounter, targetCounter)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].integrations[0].how, 'ignored', "How is not ignored")
content = self.target.p4.run_print('-a', '//depot/import/inside_file2')
self.assertEqual(content[1], content[3], "Content of #1 not equal to #2")
# Prepare delete
self.source.p4.run_delete(inside_file1)
self.source.p4.run_submit('-d', "Delete file 1")
self.source.p4.run_merge(inside_file1, inside_file2) # to trigger resolve
self.source.p4.run_resolve('-at')
self.source.p4.run_submit('-d', "Propagated delete")
sourceCounter += 2
targetCounter += 2
self.run_P4Transfer()
self.assertCounters(sourceCounter, targetCounter)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(len(filelog[0].revisions[0].integrations), 1, "No integration for delete")
self.assertEqual(filelog[0].revisions[0].integrations[0].how, 'delete from', "How is not delete from")
# Prepare re-add
create_file(inside_file1, content1)
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 re-added')
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_submit('-d', "inside_file2 re-added")
sourceCounter += 2
targetCounter += 2
self.run_P4Transfer()
self.assertCounters(sourceCounter, targetCounter)
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].integrations[0].how, 'branch from', "How is not branch from")
def testMultipleIntegrate(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
inside_file1 = os.path.join(inside, "inside_file1")
create_file(inside_file1, "Some content")
self.source.p4.run_add(inside_file1)
self.source.p4.run_submit('-d', 'inside_file1 added')
inside_file2 = os.path.join(inside, "inside_file2")
self.source.p4.run_integrate(inside_file1, inside_file2)
self.source.p4.run_submit('-d', 'inside_file1 -> inside_file2')
file3 = os.path.join(inside, "file3")
self.source.p4.run_integrate(inside_file2, file3)
self.source.p4.run_submit('-d', 'inside_file2 -> File3')
self.run_P4Transfer()
self.assertCounters(3, 3)
filelog1 = self.target.p4.run_filelog('//depot/import/inside_file1')
filelog2 = self.target.p4.run_filelog('//depot/import/inside_file2')
filelog3 = self.target.p4.run_filelog('//depot/import/file3')
self.assertEqual(len(filelog1[0].revisions), 1, "Not exactly one inside_file1 revision")
self.assertEqual(len(filelog2[0].revisions), 1, "Not exactly one inside_file2 revision")
self.assertEqual(len(filelog3[0].revisions), 1, "Not exactly one file3 revision")
self.assertEqual(len(filelog1[0].revisions[0].integrations), 1, "Not exactly one inside_file1 integ record")
self.assertEqual(len(filelog2[0].revisions[0].integrations), 2, "Not exactly two inside_file2 integ records")
self.assertEqual(len(filelog3[0].revisions[0].integrations), 1, "Not exactly one file3 integ record")
def testInsideOutside(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
outside = localDirectory(self.source.client_root, "outside")
# add from outside, integrate in
outside_file = os.path.join(outside, 'outside_file')
create_file(outside_file, "Some content")
self.source.p4.run_add(outside_file)
self.source.p4.run_submit('-d', "Outside outside_file")
inside_file2 = os.path.join(inside, 'inside_file2')
self.source.p4.run_integrate(outside_file, inside_file2)
self.source.p4.run_submit('-d', "Integrated from outside to inside")
self.run_P4Transfer()
self.assertCounters(2, 1)
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 1, "Not exactly one change on target")
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].action, "add", "inside_file2 action is not add")
# edit from outside, integrated in
self.source.p4.run_edit(outside_file)
append_to_file(outside_file, "More content")
self.source.p4.run_submit('-d', "Outside outside_file edited")
self.run_P4Transfer()
self.assertCounters(2, 1) # counters will not move, no change within the client workspace's scope
self.source.p4.run_integrate(outside_file, inside_file2)
self.source.p4.run_resolve('-at')
self.source.p4.run_submit('-d', "Copied outside_file -> inside_file2")
self.run_P4Transfer()
self.assertCounters(4, 2)
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 2, "Not exactly two changes on target")
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].action, "edit", "inside_file2 action is not edit")
# delete from outside, integrate in
self.source.p4.run_delete(outside_file)
self.source.p4.run_submit('-d', "outside_file deleted")
self.source.p4.run_integrate(outside_file, inside_file2)
self.source.p4.run_submit('-d', "inside_file2 deleted from outside_file")
self.run_P4Transfer()
self.assertCounters(6, 3)
changes = self.target.p4.run_changes()
self.assertEqual(len(changes), 3, "Not exactly three changes on target")
filelog = self.target.p4.run_filelog('//depot/import/inside_file2')
self.assertEqual(filelog[0].revisions[0].action, "delete", "inside_file2 action is not delete")
# Add to both inside and outside in the same changelist - check only inside transferred
create_file(inside_file2, "Different content")
create_file(outside_file, "Different content")
self.source.p4.run_add(inside_file2, outside_file)
self.source.p4.run_submit('-d', "adding inside and outside")
self.run_P4Transfer()
self.assertCounters(7, 4)
change = self.target.p4.run_describe('4')[0]
self.assertEqual(len(change['depotFile']), 1)
self.assertEqual(change['depotFile'][0], '//depot/import/inside_file2')
def testObliteratedSource(self):
"File has been integrated and then source obliterated"
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
outside = localDirectory(self.source.client_root, "outside")
# add from outside, integrate in
outside_file1 = os.path.join(outside, 'outside_file1')
create_file(outside_file1, "Some content")
self.source.p4.run_add(outside_file1)
self.source.p4.run_submit('-d', "Outside outside_file1")
inside_file2 = os.path.join(inside, 'inside_file2')
self.source.p4.run_integrate(outside_file1, inside_file2)
self.source.p4.run_submit('-d', "Integrated from outside to inside")
self.source.p4.run_obliterate('-y', outside_file1)
self.run_P4Transfer('--stoponerror')
self.assertCounters(2, 1)
def testKeywords(self):
self.setupTransfer()
inside = localDirectory(self.source.client_root, "inside")
files = []
for f in range(1, 5):
fname = "file{}".format(f)
files.append(os.path.join(inside, fname))
for fname in files:
create_file(fname, 'Test content')
self.source.p4.run_add(fname)
self.source.p4.run_reopen("-t", "ktext", files[0])
self.source.p4.run_reopen("-t", "kxtext", files[1])
self.source.p4.run_reopen("-t", "text+kmx", files[2])
self.source.p4.run_reopen("-t", "ktext+xkm", files[3])
self.source.p4.run_submit('-d', 'File(s) added')
self.run_P4Transfer("--nokeywords")
newFiles = self.target.p4.run_files("//...")
self.assertEqual(len(newFiles), 4, "Target does not have exactly 4 files")
self.assertEqual(newFiles[0]['type'], "text", "File1 does not have type text, but {}".format(newFiles[0]['type']))
self.assertEqual(newFiles[1]['type'], "xtext", "File2 does not have type xtext, but {}".format(newFiles[1]['type']))
self.assertEqual(newFiles[2]['type'], "text+mx", "File3 does not have type text+mx, but {}".format(newFiles[2]['type']))
self.assertEqual(newFiles[3]['type'], "text+mx", "File4 does not have type text+xm, but {}".format(newFiles[3]['type']))
fname = files[0]
self.source.p4.run_edit("-t", "+k", fname)
append_to_file(fname, "More stuff")
self.source.p4.run_submit('-d', 'File edited')
self.run_P4Transfer("--nokeywords")
files = self.target.p4.run_files("//depot/import/file1")
self.assertEqual(files[0]['type'], "text", "File1 does not have type text, but {}".format(files[0]['type']))
if __name__ == '__main__':
unittest.main()
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #98 | 27669 | Robert Cowham |
Retired (deleted) this version of the script - and include a reference to its replacement: https://github.com/perforce/p4transfer |
||
| #97 | 26350 | Robert Cowham | Handle move/delete where previous rev is obliterated | ||
| #96 | 26349 | Robert Cowham | Handle move/add where source obliterated | ||
| #95 | 26344 | Robert Cowham | New parameter --end-datetime added to allow unattended runs within a window of opportunity. | ||
| #94 | 26277 | Robert Cowham | Extra undo test | ||
| #93 | 26276 | Robert Cowham | Fix problem with replicating basic undo | ||
| #92 | 23271 | Robert Cowham |
Allow transfers to be done by non-superusers (only requiring write and review privilege). In non-super mode, changelist date/time and author fields are not updated. |
||
| #91 | 22794 | Robert Cowham | Allow change_map to be in a subdir | ||
| #90 | 22515 | Robert Cowham | Removed preview option which was confusing as it wasn't well tested. | ||
| #89 | 22466 | Robert Cowham | Improved preview option handling - with test. | ||
| #88 | 22465 | Robert Cowham |
Added tests for r99.2 created depot (no filesize/digest stored) Fixed problem with preview failing comparison Add --p4d option to tests to use different p4d version |
||
| #87 | 22461 | Robert Cowham | Tweak extraction of db.* data to support 12.2 server | ||
| #86 | 22278 | Robert Cowham | Fix problem transferring into 2012.2 server - need to convert types to canonical type | ||
| #85 | 19691 | Robert Cowham |
Fix problems with selective integrations (cherry picking). Has changed behaviour of a few tests. |
||
| #84 | 18701 | Robert Cowham | Handle the case of an edit after an archived version. | ||
| #83 | 18674 | Robert Cowham |
Fix tests failing due to new canonical form of filetypes with 2015.2 (xtext -> text+x). Unicode works for Mac/Linux with 2015.2+ P4Python installed so don't skip test and update doc. |
||
| #82 | 16568 | Robert Cowham |
Require clobber option in both source/target to avoid sync errors. As a result need to improve change_map.csv handling. |
||
| #81 | 16425 | Robert Cowham | Remove preflight option and add in test for maximum option. | ||
| #80 | 15827 | Robert Cowham |
Avoid unnecessary resyncs of edited files due to incorrect digest calculation and comparison. Update failing test. |
||
| #79 | 15529 | Robert Cowham | Fix problem when integrating cherry pick with different filetype. | ||
| #78 | 14139 | Robert Cowham | Fix strange problem with copy from & branch from to same revision. | ||
| #77 | 13959 | Robert Cowham |
Changed utf16 sync problem handling to raise an exception recommending a retype and linking to KB: http://answers.perforce.com/articles/KB/3117 |
||
| #76 | 13921 | Robert Cowham |
Handle unsyncable utf16 files: - note the sync errors - add them to ignore list - if and edit fails and previous version was utf16 then convert to add |
||
| #75 | 13576 | Robert Cowham | Fix for when filetypes are not integrated on purpose. | ||
| #74 | 12942 | Robert Cowham | Make test env more cross platform re P4CONFIG settings. | ||
| #73 | 12941 | Robert Cowham |
Make sure filetype changes are propagated via integrate even when integ.engine=2. Fix a couple of tests failing on OS X. |
||
| #72 | 12570 | Robert Cowham | Fix link handling problem. | ||
| #71 | 12549 | Robert Cowham | Handle symlinks (on Unix at least) | ||
| #70 | 12548 | Robert Cowham | Optimize slightly by checking whole file first before doing line by line search for RCS keywords. | ||
| #69 | 12547 | Robert Cowham | Handle RCS keyword files which have been merged in a dodgy manner (what should be a dirty merge pretending to be a clean merge). | ||
| #68 | 12532 | Robert Cowham | Handle edit failing when doing old style rename. | ||
| #67 | 12524 | Robert Cowham | Handled integrating a delete ontop of a deleted. | ||
| #66 | 12495 | Robert Cowham | Fix problem of missing move/add records where source was outside transfer workspace. | ||
| #65 | 12489 | Robert Cowham | Handle the multiple overlapping integrates with strange resolve -ay | ||
| #64 | 12486 | Robert Cowham | Test for overlapping integrates which passes in this form. | ||
| #63 | 12472 | Robert Cowham | Steps along the way to testing a move from outside. | ||
| #62 | 12409 | Robert Cowham | Handle utf16 with faulty BOM | ||
| #61 | 11882 | Robert Cowham |
Test for new revision with 2 integrates into it, including basic copy. Reduce number of forced integrates if there are multiple. |
||
| #60 | 11881 | Robert Cowham | Handled a multiple integrate including a copy after which the file was modified. | ||
| #59 | 11863 | Robert Cowham |
Use 'unix' instead of 'share' in transfer client line-endings. To avoid issues with MD5 checksums and the like. |
||
| #58 | 11839 | Robert Cowham |
Check for edited files changed. Remove unused parameter from read/write content calls (filetype). Fix problem with purged files being read-only. |
||
| #57 | 11835 | Robert Cowham |
Reordered tests into more logical groups. No functional change. |
||
| #56 | 11728 | Robert Cowham |
Handle very old logic (pre-tamper protection) where an integrate with resolve -ay could occur and the content of the file be edited in the same change. The result is demoted from an integrate to and edit, but still has the integraiton record. |
||
| #55 | 11548 | Robert Cowham | Remove unused logging calls. | ||
| #54 | 11518 | Robert Cowham |
Handle text filetypes properly with MD5 calcuations and line endings. Require source and client workspaces to have LineEnd='share' (with test). |
||
| #53 | 11478 | Robert Cowham |
Add the ability to batch changes up (default 20,000) Includes tests and adjustments to loggin. |
||
| #52 | 11445 | Robert Cowham | Handle delete integration requiring the force flag. | ||
| #51 | 11430 | Robert Cowham | Expanded testIntegDeleteProblem and fixed the resulting test failure. | ||
| #50 | 11426 | Robert Cowham | Rename a test for clarity. | ||
| #49 | 11425 | Robert Cowham | Handle strange case of 3 seperate integrations into a single (new) revision. | ||
| #48 | 11407 | Robert Cowham |
Fix problem where an integrate defaults to resolving an add as a delete and requires a second step to be resolved as an add (with integ.engine=3). |
||
| #47 | 11342 | Robert Cowham |
Handle renames which were badly propagated via individual integs of the move/add and move/delete using the integ.engine=2 |
||
| #46 | 11304 | Robert Cowham | Fix files with perforce wildcars in their names when they are branched from outside. | ||
| #45 | 11300 | Robert Cowham |
Create specific exception classes. Handle a integrate -Rb with ignore which comes from "outside" the source workspace. |
||
| #44 | 11296 | Robert Cowham | Add another file to the Wildcards test with a C# directory in its name | ||
| #43 | 11295 | Robert Cowham |
Handle an integrate where the source has been obliterated (fixes an index error). The target action will be an edit. |
||
| #42 | 11293 | Robert Cowham |
Handle integ -Rb which is ignored where the source is not in the source client Just ignore the revision in this case. |
||
| #41 | 11282 | Robert Cowham |
Reduce sleep time between tests (required to avoid files being locked). Run all tests down from 50s -> 15s! |
||
| #40 | 11276 | Robert Cowham | Remove unnecessary comments for assertions - if they fail then the default error message shown is sufficient. | ||
| #39 | 11275 | Robert Cowham |
Handle files with action 'archive' - they have been archived in an archive depot and are ignored by P4Transfer (with a warning in the log file). |
||
| #38 | 11271 | Robert Cowham |
Handle files with action 'import' which means they came from remote depots. This action is changed to an 'add' in the target. |
||
| #37 | 11268 | Robert Cowham | Add tests for other wildcards. | ||
| #36 | 11266 | Robert Cowham | Ensure that workspaces have a matching right hand side. | ||
| #35 | 11265 | Robert Cowham |
Handle an integrate with ignore to a deleted first revision Tweak tests to work with Python2.7/3.3 as regards unittest.assertRegex |
||
| #34 | 11257 | Robert Cowham | Fix handling of purged files (+Sn). | ||
| #33 | 11237 | Robert Cowham |
Added an (optional) change_map file which is appended to after submits. It shows: sourceP4Port,sourceChangeNo,targetChangeNo Makes for easy searching (offline or via p4 grep) as opposed to looking through change list descriptions. |
||
| #32 | 11234 | Robert Cowham | Implement configurable change descriptions | ||
| #31 | 11230 | Robert Cowham | Handle a delete on top of a delete. | ||
| #30 | 11049 | Robert Cowham | Handled strange 'add from' single integration (which is a backout of an earlier change followed by a move) | ||
| #29 | 10739 | Robert Cowham | Fix for integrating 2 versions into single target where the target is revision 1. | ||
| #28 | 10509 | Robert Cowham | Deal with multiple integrates into the same target. | ||
| #27 | 10504 | Robert Cowham | Test for integrate with -Dt being required (previous change) | ||
| #26 | 10502 | Robert Cowham | Handle need to do forced integrates | ||
| #25 | 10496 | Robert Cowham |
Test for failed submit. When running sizes command, restrict to workspace view for performance (avoid spec depot!) |
||
| #24 | 10485 | Robert Cowham | Refactored tests to log basically all p4 commands. | ||
| #23 | 10484 | Robert Cowham |
Test for file content changed when adding a file. Minor refactor for the above. Added testOutsideInsideDirtyCopy |
||
| #22 | 10481 | Robert Cowham |
Refactored applying journal patches. Include test for branch where file is edited even though it shouldn't be. |
||
| #21 | 10479 | Robert Cowham | Resync after move/add if necessary due to file content changes | ||
| #20 | 10475 | Robert Cowham | Handle the rename of a deleted file - rather esoteric but possible! | ||
| #19 | 10469 | Robert Cowham |
Check file size and digest after a clean merge Handled dodgy merges - cherry picked where they were edited afterwards |
||
| #18 | 10468 | Robert Cowham | Proper test for dirty merge which is changed to be a supposed clean merge. | ||
| #17 | 10467 | Robert Cowham |
Fix problem requiring integ -i (with integ.engine=2) Improved validation after submission. |
||
| #16 | 10438 | Robert Cowham |
Fixed bug where effectively selective integrations were being done on the source. Started better comparison preparation for changelist validation. |
||
| #15 | 10147 | Robert Cowham | Fix problem with move/add where there are other integration records on the file that is being added. | ||
| #14 | 10141 | Robert Cowham |
Enhanced logging - added specifid ids for different actions. Log a few extra items, including script version and client mappings. Fixed bug regarding the branching of a move file. |
||
| #13 | 10136 | Robert Cowham |
Added comments to test cases. Remove unneeded whitespace. |
||
| #12 | 10135 | Robert Cowham | Added test for moving from outside to inside | ||
| #11 | 10134 | Robert Cowham | Fixed problem where the target of a move is outside the client view - converts move/delete to a delete | ||
| #10 | 10110 | Robert Cowham |
Remove unused vars as per pyflakes warnings Add some extra debug logging of paths executed Remove unused code when processing 'add from' |
||
| #9 | 10099 | Sven Erik Knop | Added additional test case after reported problem | ||
| #8 | 10098 | Robert Cowham |
Fixed bug when a move (rename) by P4V was backed out via 'revert to revision' Removed python3 warning |
||
| #7 | 10005 | Sven Erik Knop |
Added move test. Also fixed Unicode test to use platform specific codec: 'mbcs' on Windows, 'UTF-8' on Mac. |
||
| #6 | 9731 | Robert Cowham | Added tests for protections - hide something that is in the client view and check that changes are correctly handled. | ||
| #5 | 9730 | Robert Cowham | Fixed problem where a changelist to both files inside and outside the workspace view in the same changelist are handled | ||
| #4 | 9729 | Robert Cowham | More small refactoring | ||
| #3 | 9728 | Robert Cowham |
Minor refactoring. Fixed a couple of pylint warnings. |
||
| #2 | 9727 | Robert Cowham | Refactored names of files for clarity | ||
| #1 | 9726 | Robert Cowham | Rename test harness | ||
| //guest/perforce_software/p4transfer/P4TransferTest.py | |||||
| #5 | 9644 | Robert Cowham | Renamed PerforceTransfer to P4Transfer to make tests work. | ||
| #4 | 9641 | Robert Cowham |
Latest changes by Robert. Added new options: --repeat for continuous operation --sample-config to produce sample config -Improved logging and notification options (via emails if configured) -Retries in case of error. |
||
| #3 | 9474 | Sven Erik Knop | Added test to verify that edited files will also have their +k removed. | ||
| #2 | 9473 | Sven Erik Knop |
Added the ability to remove +k from the target Currently tested for add, need to test for edit and integrate as well invoked by using option -k or --nokeywords |
||
| #1 | 9170 | Sven Erik Knop |
Branched PerforceTransfer from private area to perforce_software This tool will now get back its original name P4Transfer. |
||
| //guest/sven_erik_knop/P4Pythonlib/scripts/transferTest.py | |||||
| #10 | 8463 | Sven Erik Knop |
Fixed further problem with files that have an illegal file name containing @,#,* or %. Now it is possible to re-edit the file again as well. Added test case to prove the point. |
||
| #9 | 8461 | Sven Erik Knop |
Fixed adding files with illegal chars like '@'. Also added test case. |
||
| #8 | 8432 | Sven Erik Knop | Added pre-flight checks (-p) to avoid overwriting existing files. | ||
| #7 | 8425 | Sven Erik Knop |
Make PerforceTransfer unidirectional from source to target. Adjusted test cases accordingly. Still missing: Update change user and timestamp to the source user and timestamp Reverify ktext files affected by the change update. Add proper logging |
||
| #6 | 8216 | Sven Erik Knop |
Added test cases for integration from outside transfer scope. Fixed bug for integrated deletes from the outside. |
||
| #5 | 8215 | Sven Erik Knop |
Upgraded test to include merge w/ edit Fixed a bug in PerforceTransfer.py avoiding a tamper check error. |
||
| #4 | 8213 | Sven Erik Knop | Test case for re-add added | ||
| #3 | 8212 | Sven Erik Knop |
Added integrate-delete test case Solved integrate-delete problem in PerforceTransfer |
||
| #2 | 8211 | Sven Erik Knop |
Additional test cases for integrate Fixed a bug with "ignore", can now be replicated. |
||
| #1 | 8210 | Sven Erik Knop |
Fixed a bug in PerforceTransfer where an add followed by an integ to another branch would break the add. Also added the beginning of a test framework to catch those kind of problems in the future. Currently the test framework only checks add, edit, delete and simple integrates. |
||