Files
UnrealEngine/Engine/Source/ThirdParty/Perforce/P4Api.Net/p4api.net-unit-test/Utilities.cs
2025-05-18 13:04:45 +08:00

675 lines
23 KiB
C#

using System;
using System.IO;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NLog;
using Perforce.P4;
using System.Net.Sockets;
using System.Net;
namespace p4api.net.unit.test
{
class ResetServerType
{
private bool unicodeReset = false;
private bool nonUnicodeReset = false;
public bool IsResetRequired(bool unicode)
{
if (unicode)
return unicodeReset;
return nonUnicodeReset;
}
public void SetResetRequired(bool unicode, bool value)
{
if (unicode)
unicodeReset = value;
else
nonUnicodeReset = value;
}
}
class Utilities
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private static ResetServerType ResetServer = new ResetServerType();
private static Stopwatch stopWatchAllTests = null;
private static Stopwatch stopWatch = null;
private static int testCount = 0;
private static string[] settings = new string[] { "P4CLIENT", "P4CONFIG", "P4IGNORE", "P4PASSWD", "P4PORT", "P4TRUST", "P4TICKETS", "P4USER" };
private static string[] settingValues;
private static string[] envValues;
private static string cwd;
private static int[] preTestObjectCount;
private static P4CallBacks.LogMessageDelegate logDelegate = null;
private static LogFile.LogMessageDelgate logFileDelegate = null;
private static long allocs = 0;
private static long frees = 0;
private static void LogFunction(int log_level, string file, int line, string message)
{
logger.Debug(String.Format("{0}({1}): {2}", file, line, message));
}
private static void LogFileFunction(int log_level, string src, string message)
{
logger.Debug(String.Format("{0}: {1}", src, message));
}
private static void SaveP4Prefs()
{
settingValues = new string[settings.Length];
envValues = new string[settings.Length];
for (int i = 0; i < settings.Length; i++)
{
settingValues[i] = P4Server.Get(settings[i]);
// also clear the value
P4Server.Set(settings[i], "");
}
}
private static void RestoreP4Prefs()
{
P4Bridge.ReloadEnviro();
for (int i = 0; i < settings.Length; i++)
{
P4Server.Set(settings[i], settingValues[i]);
}
}
public static void LogTestStart(TestContext testContext)
{
// save and restore the p4config env variable and the cwd
SaveP4Prefs();
allocs = P4Debugging.GetStringAllocs();
frees = P4Debugging.GetStringReleases();
cwd = System.IO.Directory.GetCurrentDirectory();
logDelegate = new P4CallBacks.LogMessageDelegate(LogFunction);
logFileDelegate = new LogFile.LogMessageDelgate(LogFileFunction);
LogFile.SetLoggingFunction(logFileDelegate);
P4Debugging.SetBridgeLogFunction(logDelegate);
preTestObjectCount = new int[P4Debugging.GetAllocObjectCount()];
for (int i = 0; i < preTestObjectCount.Length; i++)
{
preTestObjectCount[i] = P4Debugging.GetAllocObject(i);
}
// reset the p4d_cmd variable to the default
p4d_cmd = _p4d_cmd;
testCount++;
logger.Info("====== TestName: {0}", testContext.TestName);
stopWatch = Stopwatch.StartNew();
if (stopWatchAllTests == null)
stopWatchAllTests = Stopwatch.StartNew();
}
public static void LogTestFinish(TestContext testContext)
{
System.IO.Directory.SetCurrentDirectory(cwd);
RestoreP4Prefs();
logger.Info("------ {0}: {1}, {2} ms", testContext.TestName, testContext.CurrentTestOutcome, stopWatch.ElapsedMilliseconds);
int iExtraObjects = 0;
for (int i = 0; i < P4Debugging.GetAllocObjectCount(); i++)
{
int postTest = P4Debugging.GetAllocObject(i);
if (preTestObjectCount[i] != postTest)
{
iExtraObjects += postTest - preTestObjectCount[i];
logger.Info(String.Format("<<<<*** Item count for {0} mismatch: {1}/{2}",
P4Debugging.GetAllocObjectName(i), preTestObjectCount[i], postTest));
}
}
long postAllocs = P4Debugging.GetStringAllocs();
long postFrees = P4Debugging.GetStringReleases();
if (postAllocs - allocs != postFrees - frees)
{
logger.Info(String.Format("<<<<*** String alloc mismatch: {0}/{1}", postAllocs - allocs, postFrees - frees));
Assert.AreEqual(postAllocs - allocs, postFrees - frees);
}
Assert.AreEqual(0, iExtraObjects);
}
public static void LogAllTestsFinish()
{
logger.Info("@@@@@@ Time for tests: {0} - {1} s", testCount, stopWatchAllTests.Elapsed);
}
public static void ClobberDirectory(String path)
{
DirectoryInfo di = new DirectoryInfo(path);
ClobberDirectory(di);
}
public static void ClobberDirectory(DirectoryInfo di)
{
string comSpec = Environment.GetEnvironmentVariable("ComSpec");
Process Zapper = new Process();
ProcessStartInfo si = new ProcessStartInfo(comSpec, "/c rd /S /Q " + di.FullName);
si.WorkingDirectory = Path.GetDirectoryName(di.FullName);
si.UseShellExecute = true;
Zapper.StartInfo = si;
try
{
Zapper.Start();
}
catch (Exception ex)
{
logger.Info("In ClobberDirectory, Zapper.Start() failed: {0}", ex.Message);
}
if (Zapper.HasExited == false)
{
Zapper.WaitForExit();
}
if (Directory.Exists(di.FullName))
{
bool worked = false;
int retries = 0;
do
{
if (!di.Exists)
return;
try
{
FileInfo[] files = di.GetFiles();
foreach (FileInfo fi in files)
{
if (fi.IsReadOnly)
fi.IsReadOnly = false;
fi.Delete();
}
DirectoryInfo[] subDirs = di.GetDirectories();
foreach (DirectoryInfo sdi in subDirs)
{
ClobberDirectory(sdi);
}
di.Delete();
worked = true;
}
catch (Exception)
{
System.Threading.Thread.Sleep(100);
}
retries++;
}
while (!worked && retries < 2);
}
}
static string p4d_exe = System.Environment.GetEnvironmentVariable("P4DPATH");
//const String _p4d_cmd = "-vrpc=3 -vnet=9 -p localhost:6666 -Id {1} -r {0} -L p4d.log -vserver=3";
const String _p4d_cmd = "-p localhost:6666 -Id {1} -r {0} -L p4d.log";
static String p4d_cmd = null;
static String rsh_p4d_cmd = "rsh:" + p4d_exe + " -r {0} -L p4d.log -vserver=3 -i";
static String restore_cmd = "-r {0} -jr checkpoint.{1}";
static String upgrade_cmd = "-r {0} -xu";
static String generate_key_cmd = string.Empty;
static string rubbishBin = "c:\\MyTestDir-rubbish-bin";
public static Process DeploySSLP4TestServer(string path, bool Unicode)
{
System.Environment.SetEnvironmentVariable("P4SSLDIR", path);
string test = System.Environment.GetEnvironmentVariable("P4SSLDIR");
p4d_cmd = "-p ssl:6666 -Id UnitTestServer -r {0}";
generate_key_cmd = "-Gc";
return DeployP4TestServer(path, 1, Unicode);
}
public static Process DeployIPv6P4TestServer(string path, string tcp, bool Unicode)
{
p4d_cmd = "-p " + tcp + ":::1:6666 -Id UnitTestServer -r {0}";
return DeployP4TestServer(path, 1, Unicode);
}
public static Process DeployP4TestServer(string path, bool unicode, string testName="")
{
return DeployP4TestServer(path, 1, unicode, testName);
}
public static string TestServerRoot(string baseRoot, bool Unicode)
{
String extra = "a";
if (Unicode)
{
extra = "u";
}
var root = Path.Combine(baseRoot, extra, "server");
logger.Debug("Server root: {0}", root);
return root;
}
public static string TestRshServerPort(string baseRoot, bool Unicode)
{
String extra = "a";
if (Unicode)
{
extra = "u";
}
var root = Path.Combine(baseRoot, extra, "server");
root = String.Format(rsh_p4d_cmd, root);
logger.Debug("Server root: {0}", root);
return root;
}
public static string TestClientRoot(string baseRoot, bool Unicode)
{
String extra = "a";
if (Unicode)
{
extra = "u";
}
var root = Path.Combine(baseRoot, extra, "clients");
logger.Debug("Client root: {0}", root);
return root;
}
public static void SetClientRoot(Repository rep, string TestDir, bool unicode, string ws_client)
{
Client c = rep.GetClient(ws_client, null);
c.Root = Path.Combine(TestClientRoot(TestDir, unicode), ws_client);
rep.UpdateClient(c);
var syncOpts = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force, 0);
rep.Connection.Client.SyncFiles(syncOpts, new FileSpec(new DepotPath("//...")));
}
public static Process DeployP4TestServer(string path, int checkpointRev, bool unicode, string testName = "")
{
String zippedFile = "a.exe";
string currentPath = Directory.GetCurrentDirectory();
if (unicode)
{
zippedFile = "u.exe";
}
return DeployP4TestServer(path, checkpointRev, zippedFile, unicode, testName);
}
public static Process DeployP4TestServer(string path, int checkpointRev, string zippedFile, bool unicode, string testName = "")
{
return DeployP4TestServer(path, checkpointRev, zippedFile, null, unicode, testName);
}
public static Process DeployP4TestServer(string testRoot, int checkpointRev, string zippedFile, string P4DCmd,
bool unicode, string testName = "")
{
logger.Info("DeployP4TestServer");
if (!Directory.Exists(rubbishBin))
{
Directory.CreateDirectory(rubbishBin);
}
string assemblyFile = typeof(Utilities).Assembly.CodeBase;
String unitTestDir = Path.GetDirectoryName(assemblyFile);
String EnvPath = Environment.GetEnvironmentVariable("path");
String CurWDir = Environment.CurrentDirectory;
var zipBase = Path.GetFileNameWithoutExtension(zippedFile); // a or u
var unzippedContentsDir = Path.Combine(rubbishBin, zipBase);
var testServerRoot = Path.Combine(testRoot, zipBase, "server");
var testClientsRoot = Path.Combine(testRoot, zipBase, "clients");
CreateDir(testServerRoot, 10);
try
{
Environment.CurrentDirectory = testServerRoot;
}
catch (Exception ex)
{
bool dirExists = Directory.Exists(testServerRoot);
logger.Error("Can't cd to {0}, {1}", testServerRoot, ex.Message);
return null;
}
using (StreamWriter sw = new StreamWriter("CmdLog.txt", false))
{
int idx;
if (unitTestDir.ToLower().StartsWith("file:\\"))
{
// cut off the file:\\
idx = unitTestDir.IndexOf("\\") + 1;
unitTestDir = unitTestDir.Substring(idx);
}
// if ((idx = unitTestDir.IndexOf("TestResults")) > 0)
// {
// unitTestDir = unitTestDir.Substring(0, idx);
//if (unitTestDir.ToLower().Contains("bin\\debug") == false) // is this needed?
//{
// unitTestDir = Path.Combine(unitTestDir, "bin\\debug");
//}
//}
// Decide whether to reset server
bool resetServerDir = ResetServer.IsResetRequired(unicode);
ResetServer.SetResetRequired(unicode, false);
ProcessStartInfo si;
String msg;
string unitTestZip = Path.Combine(unitTestDir, zippedFile);
string targetTestZip = Path.Combine(unzippedContentsDir, zippedFile);
if (!Directory.Exists(unzippedContentsDir))
{
// Copy file and unzip it
CreateDir(unzippedContentsDir, 5);
CopyFile(unitTestZip, targetTestZip);
FileInfo fi = new FileInfo(targetTestZip);
Process Unzipper = new Process();
// unpack the zip
si = new ProcessStartInfo(zippedFile);
si.WorkingDirectory = unzippedContentsDir;
si.Arguments = "-y";
logger.Info("Unzipping {0} {1}", si.FileName, si.Arguments);
Unzipper.StartInfo = si;
Unzipper.Start();
Unzipper.WaitForExit();
// Copy unzipped directory tree
CopyDirTree(unzippedContentsDir, testServerRoot);
}
else if (resetServerDir)
{
CopyDirTree(unzippedContentsDir, testServerRoot);
}
// Make sure no db.* files present
logger.Info("Removing db.* from {0}", testServerRoot);
try
{
foreach (string fname in Directory.GetFiles(testServerRoot, "db.*", SearchOption.TopDirectoryOnly))
{
System.IO.File.Delete(fname);
}
}
catch (Exception ex)
{
logger.Error(ex.Message);
logger.Error(ex.StackTrace);
}
// Reset client directories
DelDir(testClientsRoot);
CreateDir(testClientsRoot, 5);
foreach (
string fname in Directory.GetDirectories(unzippedContentsDir, "*space*", SearchOption.TopDirectoryOnly)
)
{
var dirOnly = Path.GetFileName(fname);
CopyDirTree(fname, Path.Combine(testClientsRoot, dirOnly));
}
if (p4d_cmd.Contains("ssl:"))
{
Process GenKeyandCert = new Process();
// generate private key and certificate
si = new ProcessStartInfo(p4d_exe);
si.Arguments = generate_key_cmd;
si.WorkingDirectory = testServerRoot;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.RedirectStandardOutput = true;
msg = si.Arguments;
logger.Info(msg);
GenKeyandCert.StartInfo = si;
GenKeyandCert.Start();
GenKeyandCert.WaitForExit();
}
// restore the checkpoint
Process RestoreCheckPoint = new Process();
si = new ProcessStartInfo(p4d_exe);
si.Arguments = String.Format(restore_cmd, testServerRoot, checkpointRev);
//si.FileName = "p4d.exe";
si.WorkingDirectory = testServerRoot;
si.UseShellExecute = false;
si.RedirectStandardOutput = true;
si.CreateNoWindow = true;
logger.Info("{0} {1}", si.FileName, si.Arguments);
RestoreCheckPoint.StartInfo = si;
RestoreCheckPoint.Start();
RestoreCheckPoint.WaitForExit();
if (RestoreCheckPoint.ExitCode != 0)
return null;
// upgrade the db tables
Process UpgradeTables = new Process();
si = new ProcessStartInfo(p4d_exe);
si.Arguments = String.Format(upgrade_cmd, testServerRoot);
si.WorkingDirectory = testServerRoot;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.RedirectStandardOutput = true;
logger.Info("{0} {1}", si.FileName, si.Arguments);
UpgradeTables.StartInfo = si;
UpgradeTables.Start();
UpgradeTables.WaitForExit();
if (UpgradeTables.ExitCode != 0)
return null;
Process p4d = new Process();
if (P4DCmd != null)
{
string P4DCmdSrc = Path.Combine(unitTestDir, P4DCmd);
string P4DCmdTarget = Path.Combine(testServerRoot, P4DCmd);
System.IO.File.Copy(P4DCmdSrc, P4DCmdTarget);
// run the command to start p4d
si = new ProcessStartInfo(P4DCmdTarget);
si.Arguments = String.Format(testServerRoot);
si.WorkingDirectory = testServerRoot;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.RedirectStandardOutput = true;
logger.Info("{0} {1}", si.FileName, si.Arguments);
p4d.StartInfo = si;
p4d.Start();
}
else
{
//start p4d
si = new ProcessStartInfo(p4d_exe);
if (string.IsNullOrEmpty(testName))
testName = "UnitTestServer";
si.Arguments = String.Format(p4d_cmd, testServerRoot, testName);
si.WorkingDirectory = testServerRoot;
si.UseShellExecute = false;
si.RedirectStandardOutput = true;
si.CreateNoWindow = true;
logger.Info("{0} {1}", si.FileName, si.Arguments);
p4d.StartInfo = si;
p4d.Start();
}
Environment.CurrentDirectory = CurWDir;
// Give p4d time to start up
DateTime started = DateTime.UtcNow;
TcpClient client = new TcpClient();
while (DateTime.UtcNow.Subtract(started).Milliseconds < 500)
{
client.Connect("localhost", 6666);
if (client.Connected)
return p4d;
}
// that didn't work
return p4d;
}
}
private static void CopyDirTree(string srcDir, string targDir)
{
logger.Info("Copying directory {0} to {1}", srcDir, targDir);
// First Create all of the directories
foreach (string dirPath in Directory.GetDirectories(srcDir, "*",
SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(srcDir, targDir));
// Then copy all the files & Replaces any files with the same name
foreach (string fname in Directory.GetFiles(srcDir, "*.*", SearchOption.AllDirectories))
{
var target = fname.Replace(srcDir, targDir);
try
{
System.IO.File.Copy(fname, target, true);
}
catch (Exception ex)
{
logger.Debug(ex.Message);
}
}
logger.Info("Finished copying {0} to {1}", srcDir, targDir);
}
private static void CopyFile(string srcFile, string targetFile)
{
int retries = 3;
int delay = 1000; // initial delay 1 second
while (retries > 0)
{
try
{
System.IO.File.Copy(srcFile, targetFile);
break; //success
} catch (Exception)
{
System.Threading.Thread.Sleep(delay);
delay *= 2; // wait twice as long next time
retries--;
}
}
}
private static void CreateDir(string path, int retries)
{
while ((Directory.Exists(path) == false) && (retries > 0))
{
try
{
Directory.CreateDirectory(path);
if (Directory.Exists(path))
{
break;
}
retries--;
System.Threading.Thread.Sleep(1000);
} catch (Exception ex)
{
retries--;
bool dirExists = Directory.Exists(path);
Trace.WriteLine(ex.Message);
if (dirExists)
{
break;
}
System.Threading.Thread.Sleep(200);
}
}
}
private static void DelDir(string dirPath)
{
if (!Directory.Exists(dirPath))
return;
try
{
Directory.Delete(dirPath, true);
} catch
{
try
{
// delete failed, try to rename it
Directory.Move(dirPath, string.Format("{0}-{1}", dirPath, DateTime.Now.Ticks));
} catch
{
// rename failed, try to clobber it (can be slow so last resort)
Utilities.ClobberDirectory(dirPath);
}
}
}
public static void RemoveTestServer(Process p, String testRoot, bool resetDepot=false, bool unicode=false)
{
logger.Info("RemoveTestServer");
if (p != null)
{
if (!p.HasExited)
p.Kill();
p.WaitForExit();
// sleep for a second to let the system clean up
// System.Threading.Thread.Sleep(100);
}
MainTest.RememberToCleanup(rubbishBin);
MainTest.RememberToCleanup(testRoot);
// Flag for next unit test to reset depot - see DeployP4TestServer
ResetServer.SetResetRequired(unicode, resetDepot);
}
private static void MoveDir(string srcDir, string targDir)
{
logger.Info("Moving {0} to {1}", srcDir, targDir);
try
{
int retries = 60;
while ((Directory.Exists(srcDir)) && (retries > 0))
{
try
{
// Try to rename it
Directory.Move(srcDir, targDir);
//must have worked
break;
} catch
{
retries--;
System.Threading.Thread.Sleep(1000);
if (retries <= 0)
{
throw;
}
}
}
} catch (Exception ex)
{
logger.Info("In DeployP4TestServer, Directory.Move failed: {0}", ex.Message);
// rename failed, try to clobber it (can be slow so last resort)
Utilities.ClobberDirectory(srcDir);
}
}
}
}