blob: 0a067b3872596c22ee75007c433a8054c928dbbd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Security.Principal;
#pragma warning disable 618
namespace Apache.Geode.DUnitFramework
{
using NUnit.Framework;
public static class Util
{
/// <summary>
/// Enumeration to specify logging level.
/// For now this is same as the GEODE_NATIVE_HOME product LogLevel enumeration.
/// </summary>
public enum LogLevel
{
None,
Error,
Warning,
Info,
Default,
Config,
Fine,
Finer,
Finest,
Debug,
All
}
#region Public accessors and members
/// <summary>
/// Channel to communicate with the server -- this must be setup by
/// each client process.
/// </summary>
public static IDriverComm DriverComm
{
get
{
return m_driverComm;
}
set
{
m_driverComm = value;
}
}
/// <summary>
/// The port to use for running the driver.
/// </summary>
public static int DriverPort
{
get
{
return m_driverPort;
}
set
{
m_driverPort = value;
}
}
/// <summary>
/// Channel to communicate with the BBserver -- this must be setup by
/// each client process.
/// </summary>
public static IBBComm BBComm
{
get
{
return m_bbComm;
}
set
{
m_bbComm = value;
}
}
/// <summary>
/// The URL of the BlackBoard server when using an external one.
/// </summary>
public static string ExternalBBServer
{
get
{
return m_externalBBServer;
}
set
{
m_externalBBServer = value;
}
}
/// <summary>
/// Get or set the ID for a client -- this must be set by each client
/// process for client and server side logging purpose.
/// </summary>
public static string ClientId
{
get
{
return m_clientId;
}
set
{
m_clientId = value;
}
}
public static string AppDomainPrefix
{
get
{
return "APPDOMAIN:";
}
}
public static string IPAddressString
{
get
{
if (m_ipAddress == null)
{
NetworkInterface[] adapters =
NetworkInterface.GetAllNetworkInterfaces();
if (adapters != null)
{
foreach (NetworkInterface adapter in adapters)
{
if (adapter.OperationalStatus == OperationalStatus.Up &&
adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback)
{
UnicastIPAddressInformationCollection unicastAddresses =
adapter.GetIPProperties().UnicastAddresses;
if (unicastAddresses != null)
{
foreach (UnicastIPAddressInformation unicastAddress in
unicastAddresses)
{
IPHostEntry hostEntry;
try
{
hostEntry = Dns.GetHostByAddress(unicastAddress.Address);
}
catch
{
hostEntry = null;
}
if (hostEntry != null && hostEntry.HostName != null &&
hostEntry.HostName.Length > 0)
{
m_ipAddress = unicastAddress.Address;
break;
}
}
if (m_ipAddress != null) break;
}
}
}
}
}
return (m_ipAddress == null ? null : m_ipAddress.ToString());
}
}
/// <summary>
/// Get or set a number for the client.
/// </summary>
public static int ClientNum
{
get
{
return m_clientNum;
}
set
{
m_clientNum = value;
}
}
public static bool LogAppend
{
get
{
return (m_log == null ? true : m_log.Append);
}
set
{
if (m_log == null)
{
throw new IllegalArgException("LogFile is not yet set.");
}
m_log.Append = value;
}
}
public static string LogFile
{
get
{
return (m_log == null ? null : m_log.File);
}
set
{
if (value == null)
{
if (m_log != null)
{
m_log.Dispose();
}
m_log = null;
}
else if (m_log == null)
{
m_log = new UnitLog(value, true);
}
else
{
m_log.File = value;
m_log.Append = true;
}
}
}
public static string LogPrefix
{
get
{
return m_logPrefix;
}
set
{
if (value == null)
{
m_logPrefix = DefaultLogPrefix;
}
else
{
m_logPrefix = value + ':';
}
}
}
public static string DUnitLogDir
{
get
{
if (m_logDir == null)
{
try
{
m_logDir = Util.GetEnvironmentVariable("DUNIT_LOGDIR");
if (m_logDir.Length == 0)
{
m_logDir = null;
}
}
catch (Exception)
{
m_logDir = null;
}
}
return m_logDir;
}
}
public static bool LogOnConsole
{
get
{
return m_logOnConsole;
}
set
{
m_logOnConsole = value;
}
}
public static string AssemblyDir
{
get
{
if (m_assemblyDir == null)
{
m_assemblyDir = Util.NormalizePath(Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location)).ToLower();
}
return m_assemblyDir;
}
}
public static XmlNodeReaderWriter DefaultSettings
{
get
{
if (m_defaultSettings == null)
{
m_defaultSettings = XmlNodeReaderWriter.GetInstance("Settings.xml");
}
return m_defaultSettings;
}
}
public static string HostName
{
get
{
if (m_hostName == null)
{
m_hostName = Dns.GetHostName();
}
return m_hostName;
}
}
public static Process Process
{
get
{
if (m_proc == null)
{
m_proc = Process.GetCurrentProcess();
}
return m_proc;
}
}
public static int PID
{
get
{
return Util.Process.Id;
}
}
public static string ProcessName
{
get
{
return Util.Process.ProcessName;
}
}
public static int ThreadID
{
get
{
return Thread.CurrentThread.ManagedThreadId;
}
}
public static string ThreadName
{
get
{
return Thread.CurrentThread.Name;
}
}
public static LogLevel CurrentLogLevel
{
get
{
return m_logLevel;
}
set
{
m_logLevel = value;
}
}
public static LogLevel DefaultLogLevel
{
get
{
return LogLevel.Info;
}
}
public static MethodBase CurrentTest
{
get
{
StackFrame sf = new StackFrame(1, false);
return sf.GetMethod();
}
}
public static string CurrentTestName
{
get
{
StackFrame sf = new StackFrame(1, false);
return sf.GetMethod().Name;
}
}
public static ICollection<UnitFnMethod> RegisteredTestCompleteDelegates
{
get
{
return m_testCompleteMap.Keys;
}
}
public static string SystemType
{
get
{
if (m_systemType == null)
{
m_systemType = Environment.OSVersion.Platform.ToString();
m_systemType = m_systemType.Substring(0, 3).ToUpper();
}
return m_systemType;
}
}
public static string MarkerString = Environment.NewLine + "------------" +
"-------------------------------------------------------" +
Environment.NewLine;
#endregion
#region Private members
private static Random m_rnd = new Random((int)DateTime.Now.Ticks);
private static IDriverComm m_driverComm = null;
private static int m_driverPort = RandPort(20000, 40000);
private static IPAddress m_ipAddress = null;
private static IBBComm m_bbComm = null;
private static string m_externalBBServer = null;
private static string m_clientId = "0";
private static int m_clientNum = 0;
private static string m_hostName = null;
private static Process m_proc = null;
private static volatile UnitLog m_log = null;
private static LogLevel m_logLevel = DefaultLogLevel;
private static string m_logDir = null;
private static string m_logPrefix = DefaultLogPrefix;
private static bool m_logOnConsole = false;
private static string m_assemblyDir = null;
private static XmlNodeReaderWriter m_defaultSettings = null;
private static string m_systemType = null;
private const string DefaultLogPrefix = "TEST:";
private const string NoServerConnMsg = "Server connection not established.";
private static Dictionary<UnitFnMethod, bool> m_testCompleteMap =
new Dictionary<UnitFnMethod, bool>();
#endregion
/// <summary>
/// Convenience function to convert a string to a byte.
/// </summary>
/// <param name="str">The string to convert.</param>
/// <returns>The converted byte.</returns>
public static byte String2Byte(string str)
{
if (str.StartsWith("0x"))
{
return byte.Parse(str.Substring(2), System.Globalization.NumberStyles.HexNumber);
}
return byte.Parse(str);
}
/// <summary>
/// Convenience function to convert a string to a 16-bit short integer.
/// </summary>
/// <param name="str">The string to convert.</param>
/// <returns>The converted 16-bit integer.</returns>
public static Int16 String2Int16(string str)
{
if (str.StartsWith("0x"))
{
return Int16.Parse(str.Substring(2), System.Globalization.NumberStyles.HexNumber);
}
return Int16.Parse(str);
}
/// <summary>
/// Convenience function to convert a string to a 32-bit integer.
/// </summary>
/// <param name="str">The string to convert.</param>
/// <returns>The converted 32-bit integer.</returns>
public static Int32 String2Int32(string str)
{
if (str.StartsWith("0x"))
{
return Int32.Parse(str.Substring(2), System.Globalization.NumberStyles.HexNumber);
}
return Int32.Parse(str);
}
/// <summary>
/// Convenience function to convert a string to a 64-bit integer.
/// </summary>
/// <param name="str">The string to convert.</param>
/// <returns>The converted 64-bit integer.</returns>
public static Int64 String2Int64(string str)
{
if (str.StartsWith("0x"))
{
return Int64.Parse(str.Substring(2), System.Globalization.NumberStyles.HexNumber);
}
return Int64.Parse(str);
}
/// <summary>
/// Convenience function to convert a string with ':' separated bytes to bytes.
/// </summary>
/// <param name="str">The string with different bytes separated by ':'</param>
/// <returns>Array of converted bytes.</returns>
public static byte[] String2Bytes(string str)
{
string[] strSplit = str.Split(':');
byte[] buffer = new byte[strSplit.Length];
for (int i = 0; i < strSplit.Length; i++)
{
buffer[i] = String2Byte(strSplit[i]);
}
return buffer;
}
/// <summary>
/// Convenience function to convert a string with ':' separated bytes to string.
/// Normally only useful for UTF encoded strings.
/// </summary>
/// <param name="str">The string with different bytes separated by ':'</param>
/// <returns>Array of converted string.</returns>
public static string String2String(string str)
{
string[] strSplit = str.Split(':');
char[] chars = new char[strSplit.Length];
for (int i = 0; i < strSplit.Length; i++)
{
chars[i] = (char)String2Int16(strSplit[i]);
}
return new string(chars);
}
/// <summary>
/// Convenience generic function to compare two arbitrary arrays.
/// </summary>
/// <typeparam name="T">The type of the arrays.</typeparam>
/// <param name="testArray">
/// The array of type '<typeparamref name="T"/>' to be tested.
/// </param>
/// <param name="expectedArray">
/// The expected array of type '<typeparamref name="T"/>'.
/// </param>
public static bool CompareArrays<T>(T[] testArray, T[] expectedArray)
{
if (testArray == null)
{
return testArray == expectedArray;
}
if (expectedArray == null)
{
return false;
}
if (testArray.Length != expectedArray.Length)
{
return false;
}
for (int i = 0; i < expectedArray.Length; i++)
{
if (!testArray[i].Equals(expectedArray[i]))
{
return false;
}
}
return true;
}
/// <summary>
/// Convenience generic function to compare two arbitrary arrays.
/// Checks if 'expectedArray' is a prefix of 'testArray'
/// </summary>
/// <typeparam name="T">The type of the arrays.</typeparam>
/// <param name="testArray">
/// The array of type '<typeparamref name="T"/>' to be tested.
/// </param>
/// <param name="expectedArray">
/// The expected array of type '<typeparamref name="T"/>'.
/// </param>
public static bool CompareArraysPrefix<T>(T[] testArray, T[] expectedArray)
{
if (testArray == null)
{
return testArray == expectedArray;
}
if (expectedArray == null)
{
return false;
}
if (testArray.Length < expectedArray.Length)
{
return false;
}
for (int i = 0; i < expectedArray.Length; i++)
{
if (!testArray[i].Equals(expectedArray[i]))
{
return false;
}
}
return true;
}
/// <summary>
/// Convenience generic function to compare two arbitrary arrays
/// using NUnit.
/// </summary>
/// <typeparam name="T">The type of the arrays.</typeparam>
/// <param name="expectedArray">
/// The expected array of type '<typeparamref name="T"/>'.
/// </param>
/// <param name="actualArray">
/// The actual result array of type '<typeparamref name="T"/>'.
/// </param>
public static void CompareTestArrays<T>(T[] expectedArray, T[] actualArray)
{
Assert.IsNotNull(expectedArray, "The expectedBytes cannot be null");
Assert.IsNotNull(actualArray, "The bytes read cannot be null");
Assert.IsTrue(expectedArray.Length <= actualArray.Length,
"The length of the resulting bytes is too small.{0}Expected: {1}{2}" +
"Got: {3}", Environment.NewLine, expectedArray.Length,
Environment.NewLine, actualArray.Length);
for (int i = 0; i < expectedArray.Length; i++)
{
Assert.AreEqual(expectedArray[i], actualArray[i],
"Incorrect result in array index {0}.", i);
}
}
/// <summary>
/// Convenience generic function to compare two arbitrary arrays with
/// custom comparer using NUnit.
/// </summary>
/// <typeparam name="T">The type of the arrays.</typeparam>
/// <param name="expectedArray">
/// The expected array of type '<typeparamref name="T"/>'.
/// </param>
/// <param name="actualArray">
/// The actual result array of type '<typeparamref name="T"/>'.
/// </param>
public static void CompareTestArrays<T>(T[] expectedArray, T[] actualArray,
UnitFnMethodR<bool, T, T> compareDelegate)
{
Assert.IsNotNull(expectedArray, "The expectedBytes cannot be null");
Assert.IsNotNull(actualArray, "The bytes read cannot be null");
Assert.IsTrue(expectedArray.Length <= actualArray.Length,
"The length of the resulting bytes is too small.{0}Expected: {1}{2}" +
"Got: {3}", Environment.NewLine, expectedArray.Length,
Environment.NewLine, actualArray.Length);
for (int i = 0; i < expectedArray.Length; i++)
{
Assert.IsTrue(compareDelegate(expectedArray[i], actualArray[i]),
"Incorrect result in array index {0}; Expected: {1}, Got: {2}.",
i, expectedArray[i], actualArray[i]);
}
}
/// <summary>
/// Get a random double between 0.0 and 1.0.
/// </summary>
/// <returns>the random double value</returns>
public static double Rand()
{
return m_rnd.NextDouble();
}
/// <summary>
/// Get a random integer.
/// </summary>
/// <param name="max">
/// The exclusive upper bound of the random integer.
/// </param>
/// <returns>the random integer</returns>
public static int Rand(int max)
{
return m_rnd.Next(max);
}
/// <summary>
/// Get a random integer.
/// </summary>
/// <param name="min">
/// The inclusive lower bound of the random integer.
/// </param>
/// <param name="max">
/// The exclusive upper bound of the random integer.
/// </param>
/// <returns>the random integer</returns>
public static int Rand(int min, int max)
{
return m_rnd.Next(min, max);
}
/// <summary>
/// Get random bytes in a buffer.
/// </summary>
/// <param name="buffer">The buffer to fill with the random bytes.</param>
public static void RandBytes(byte[] buffer)
{
m_rnd.NextBytes(buffer);
}
/// <summary>
/// Get an arrray of random bytes.
/// </summary>
/// <param name="size">The size of the random byte array.</param>
/// <returns>An array containing random bytes.</returns>
public static byte[] RandBytes(int size)
{
if (size > 0)
{
byte[] buffer = new byte[size];
m_rnd.NextBytes(buffer);
return buffer;
}
return null;
}
/// <summary>
/// Get a free random port in the given range.
/// </summary>
/// <param name="min">
/// The inclusive lower bound of the random integer.
/// </param>
/// <param name="max">
/// The exclusive upper bound of the random integer.
/// </param>
/// <returns>the free port number</returns>
public static int RandPort(int min, int max)
{
int portNo;
while (true)
{
portNo = Rand(min, max);
Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
s.Bind(new IPEndPoint(IPAddress.Any, portNo));
s.Close();
return portNo;
}
catch (SocketException ex)
{
// EADDRINUSE?
if (ex.ErrorCode == 10048)
{
continue;
}
else
{
throw;
}
}
}
}
/// <summary>
/// Swap two objects.
/// </summary>
/// <typeparam name="Tp">The type of the objects.</typeparam>
/// <param name="first">The first object.</param>
/// <param name="second">The second object.</param>
public static void Swap<Tp>(ref Tp first, ref Tp second)
{
Tp tmp = first;
first = second;
second = tmp;
}
/// <summary>
/// Start a process with the given path and arguments.
/// </summary>
/// <remarks>
/// This function starts the process using shell (cmd.exe on windows)
/// when the <see cref="Util.LogDir"/> is not set.
/// </remarks>
/// <param name="procPath">The path of the process to start.</param>
/// <param name="procArgs">The arguments to be passed.</param>
/// <param name="useShell">Whether to start in the DOS cmd shell.</param>
/// <param name="startDir">
/// The starting directory for the process;
/// pass null to use the current working directory.
/// </param>
/// <param name="proc">The created Process as an out parameter.</param>
/// <returns>Whether the process sucessfully started.</returns>
public static bool StartProcess(string procPath, string procArgs,
bool useShell, string startDir, bool redirectStdOut,
bool redirectStdIn, bool redirectStdErr, out Process proc)
{
return StartProcess(procPath, procArgs, useShell, startDir, redirectStdOut,
redirectStdIn, redirectStdErr, false, out proc);
}
public static bool StartProcess(string procPath, string procArgs,
bool useShell, string startDir, bool redirectStdOut,
bool redirectStdIn, bool redirectStdErr, bool createNoWindow, out Process proc)
{
ProcessStartInfo pInfo = new ProcessStartInfo(procPath, procArgs);
// Force launch without a shell. This allows launching FwkClient.exe without a window so tests can run in CI.
useShell = false;
if (!useShell)
{
if (m_externalBBServer != null || createNoWindow)
{
pInfo.CreateNoWindow = true;
}
pInfo.UseShellExecute = false;
}
if (startDir != null && startDir.Length > 0)
{
pInfo.WorkingDirectory = startDir;
}
pInfo.RedirectStandardOutput = redirectStdOut;
pInfo.RedirectStandardInput = redirectStdIn;
pInfo.RedirectStandardError = redirectStdErr;
proc = new Process();
proc.StartInfo = pInfo;
return proc.Start();
}
#region Methods for client side logging
/// <summary>
/// Format the message with time and other stuff appropriate to be
/// written into the log.
/// </summary>
/// <param name="prefix">A string to be prefixed in the log line.</param>
/// <param name="message">The message to be logged.</param>
/// <returns></returns>
public static string GetLogLine(string prefix, string message)
{
return GetLogLine(prefix, message, HostName + ':' + PID + ' ' + ThreadID);
}
/// <summary>
/// Format the message with time and other stuff appropriate to be
/// written into the log.
/// </summary>
/// <param name="logLevel">A string representing the logging level.</param>
/// <param name="message">The message to be logged.</param>
/// <param name="idString">The ID of process,thread to be logged.</param>
/// <returns></returns>
public static string GetLogLine(string prefix, string message, string idString)
{
DateTime now = DateTime.Now;
return '[' + prefix + ' ' + now.ToShortDateString() + ' ' +
now.Hour.ToString("D02") + ':' + now.Minute.ToString("D02") + ':' +
now.Second.ToString("D02") + '.' + now.Millisecond.ToString("D03") +
' ' + idString + "] " + message + Environment.NewLine;
}
/// <summary>
/// Log a message with the <see cref="CurrentLogLevel" />
/// logging level on the client side.
/// </summary>
/// <param name="message">The message to log.</param>
public static void Log(string message)
{
Log(m_logLevel, message);
}
/// <summary>
/// Log a formatted message with the <see cref="CurrentLogLevel" />
/// logging level on the client side.
/// </summary>
/// <param name="format">The formatted message to log.</param>
/// <param name="args">
/// The objects arguments to write into the formatted string.
/// </param>
public static void Log(string format, params object[] args)
{
Log(m_logLevel, string.Format(format, args));
}
/// <summary>
/// Log a formatted message with the given
/// logging level on the client side.
/// </summary>
/// <param name="logLevel">The required logging level.</param>
/// <param name="format">The formatted message to log.</param>
/// <param name="args">
/// The objects arguments to write into the formatted string.
/// </param>
public static void Log(LogLevel logLevel, string format,
params object[] args)
{
Log(logLevel, string.Format(format, args));
}
/// <summary>
/// Log a message with the given
/// logging level on the client side.
/// </summary>
/// <param name="logLevel">The required logging level.</param>
/// <param name="message">The message to log.</param>
public static void Log(LogLevel logLevel, string message)
{
string logLine = GetLogLine(m_logPrefix + logLevel.ToString(), message);
RawLog(logLine);
if (LogOnConsole && LogFile != null)
{
Console.WriteLine(logLine);
}
}
/// <summary>
/// Write the given message to the log file.
/// </summary>
/// <param name="message">The message to log.</param>
public static void RawLog(string message)
{
if (m_log == null)
{
Console.WriteLine(message);
}
else
{
lock (m_log)
{
m_log.Write(message);
}
}
}
#endregion
#region Convenience methods to perform server operations
/// <summary>
/// Log a message with the <see cref="CurrentLogLevel"/>
/// logging level on the main server thread.
/// </summary>
/// <param name="message">The message to log.</param>
public static void ServerLog(string message)
{
ServerLog(m_logLevel, message);
}
/// <summary>
/// Log a message with the given
/// logging level on the main server thread.
/// </summary>
/// <param name="logLevel">The required logging level.</param>
/// <param name="message">The message to log.</param>
public static void ServerLog(LogLevel logLevel, string message)
{
if (DriverComm != null)
{
DriverComm.Log(m_clientId, logLevel.ToString(), message);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Log a formatted message with the <see cref="CurrentLogLevel"/>
/// logging level on the main server thread.
/// </summary>
/// <param name="format">The formatted message to log.</param>
/// <param name="args">
/// The objects arguments to write into the formatted string.
/// </param>
public static void ServerLog(string format, params object[] args)
{
ServerLog(m_logLevel, format, args);
}
/// <summary>
/// Log a formatted message with the given
/// logging level on the main server thread.
/// </summary>
/// <param name="logLevel">The required logging level.</param>
/// <param name="format">The formatted message to log.</param>
/// <param name="args">
/// The objects arguments to write into the formatted string.
/// </param>
public static void ServerLog(LogLevel logLevel, string format,
params object[] args)
{
if (DriverComm != null)
{
string message = string.Format(format, args);
DriverComm.Log(m_clientId, logLevel.ToString(), message);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Write an object with a given key and black-board on the server.
/// Useful for communication between different client processes. There is
/// intentionally no data protection i.e. any client can (over-)write any
/// object. The value should be Serializable i.e. the class (and all the
/// types that it contains) should be marked with [Serializable] attribute.
/// </summary>
/// <param name="bbName">The name of the black-board to use.</param>
/// <param name="key">The key of the object to write.</param>
/// <param name="value">The value of the object.</param>
public static void BBSet(string bbName, string key, object value)
{
if (BBComm != null)
{
BBComm.WriteObject(bbName + '.' + key, value);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Read the value of an object for the given key and black-board from the
/// server. Useful for communication between different client processes.
/// </summary>
/// <param name="bbName">The name of the black-board to use.</param>
/// <param name="key">The key of the object to read.</param>
/// <returns>The value of the object with the given key.</returns>
public static object BBGet(string bbName, string key)
{
if (BBComm != null)
{
return BBComm.ReadObject(bbName + '.' + key);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Remove the value of an object for the given key and black-board from the
/// server. Useful for communication between different client processes.
/// </summary>
/// <param name="bbName">The name of the black-board to use.</param>
/// <param name="key">The key of the object to remove.</param>
public static void BBRemove(string bbName, string key)
{
if (BBComm != null)
{
BBComm.RemoveObject(bbName + '.' + key);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
public static int BBAdd(string bbName, string key, int incValue)
{
if (BBComm != null)
{
return BBComm.AddInt(bbName + '.' + key, incValue);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
public static int BBIncrement(string bbName, string key)
{
if (BBComm != null)
{
return BBComm.AddOrSetInt(bbName + '.' + key, 1);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
public static int BBDecrement(string bbName, string key)
{
if (BBComm != null)
{
return BBComm.AddInt(bbName + '.' + key, -1);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Signal that the client is up and running, so that the server
/// can initiate connection to the client.
/// </summary>
public static void ClientListening()
{
if (DriverComm != null)
{
DriverComm.ClientListening(m_clientId);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Client callback to request server to start a task on another Windows machine.
/// </summary>
/// <param name="clientId">The ID of the client.</param>
/// <param name="hostName">
/// The host name where the task is to be started.
/// </param>
/// <param name="taskSpec">
/// A specification of the task to be executed on the new client.
/// </param>
/// <returns>Whether the task successfully executed.</returns>
public static bool RunClientWinTask(string clientId, string hostName, string taskSpec)
{
if (DriverComm != null)
{
return DriverComm.RunWinTask(clientId, hostName, taskSpec);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
/// <summary>
/// Client callback to request server to run a shell command on another machine.
/// </summary>
/// <param name="clientId">The ID of the client.</param>
/// <param name="hostName">
/// The host name where the task is to be started.
/// </param>
/// <param name="shellCmd">
/// Command to be executed using the shell.
/// </param>
/// <param name="envVars">
/// An optional list of environment variables to be set in the shell.
/// </param>
/// <returns>The standard output of the task.</returns>
public static string RunClientShellTask(string clientId, string hostName,
string shellCmd, Dictionary<string, string> envVars)
{
if (DriverComm != null)
{
return DriverComm.RunShellTask(clientId, hostName, shellCmd, envVars);
}
else
{
throw new NoServerConnectionException(NoServerConnMsg);
}
}
#endregion
/// <summary>
/// Normalize a given path by finding its full path and
/// converting to POSIX convention.
/// </summary>
/// <param name="path">The path to normalize.</param>
/// <returns>The normalized path.</returns>
public static string NormalizePath(string path)
{
return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar).Replace('\\', '/');
}
/// <summary>
/// Delete a share (if any) of the given directory.
/// </summary>
/// <param name="directoryPath">
/// The path of the directory that is shared.
/// </param>
public static void DeleteSharePath(string directoryPath)
{
directoryPath = directoryPath.Replace(@"\", @"\\");
using (ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT Name FROM Win32_Share WHERE Path='" +
directoryPath + "'"))
{
ManagementObjectCollection objColl = searcher.Get();
foreach (ManagementObject obj in objColl)
{
obj.Delete();
obj.Dispose();
}
}
}
/// <summary>
/// Delete a share (if any) of the given name.
/// </summary>
/// <param name="shareName">The name of the share.</param>
public static void DeleteShareName(string shareName)
{
using (ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT Name FROM Win32_Share WHERE Name='" +
shareName + "'"))
{
ManagementObjectCollection objColl = searcher.Get();
foreach (ManagementObject obj in objColl)
{
obj.Delete();
obj.Dispose();
}
}
}
/// <summary>
/// Check whether the given share name already exists.
/// </summary>
/// <param name="shareName">The name of the share to check.</param>
/// <returns>
/// True if the share name already exists; false otherwise.
/// </returns>
public static bool ShareExists(string shareName)
{
using (ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT Name FROM Win32_Share WHERE Name='" +
shareName + "'"))
{
ManagementObjectCollection objColl = searcher.Get();
return (objColl != null && objColl.Count > 0);
}
}
public static ManagementObject permissionForShareFolder()
{
// Create a WMI instance of the principal (Win32_Trustee).
//Getting the Sid value is not required for Vista.
NTAccount account = new NTAccount(System.Environment.UserDomainName, System.Environment.UserName);
SecurityIdentifier sid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
byte[] sidArray = new byte[sid.BinaryLength];
sid.GetBinaryForm(sidArray, 0);
ManagementObject Trustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null);
Trustee["Domain"] = System.Environment.UserDomainName;
Trustee["Name"] = System.Environment.UserName;
Trustee["SID"] = sidArray;
//Create a WMI instance of Win32_Ace, assign the Trustee to this Win32_Ace instance.
ManagementObject AdminACE = new ManagementClass(new ManagementPath("Win32_Ace"), null);
AdminACE["AccessMask"] = 2032127;
AdminACE["AceFlags"] = 3;
AdminACE["AceType"] = 0;
AdminACE["Trustee"] = Trustee;
//To know what values you need to put there, check msdn (link). I actually encourage you to write an enum flag to encapsulate those values.
//In nut shell, 2032127 is for full access, Access Flags 3 is for non-container and container child objects to inherit the ACE, and Ace Type 0 is to allow the trustee to access it.
//Create a WMI instance of the security descriptor (Win32_SecurityDescriptor)
ManagementObject secDescriptor = new ManagementClass(new ManagementPath("Win32_SecurityDescriptor"), null);
secDescriptor["ControlFlags"] = 4; //SE_DACL_PRESENT
secDescriptor["DACL"] = new object[] { AdminACE };
return secDescriptor;
//Now, create a WMI instance of Win32_Share, and setup the security.
// ManagementObject share = new ManagementObject(@"\\ContosoServer\root\cimv2:Win32_Share.Name='JohnShare'");
//share.InvokeMethod("SetShareInfo", new object[] {Int32.MaxValue, "This is John's share", secDescriptor});
//Check the return value of the Invoke, the method returns an Object, convert it to Int32.
}
/// <summary>
/// Make a given directory shared, overwriting previous share (if any).
/// </summary>
/// <param name="directoryPath">The directory to be made shared.</param>
/// <param name="sharePrefix">
/// The prefix for the share name.
/// The actual name will be of the form '[prefix][num]' for the first free
/// share name available for all integer [num] starting from 1.
/// </param>
/// <param name="shareDesc">A description for the share.</param>
/// <returns>The share name of the directory.</returns>
public static string ShareDirectory(string directoryPath,
string sharePrefix, string shareDesc)
{
string resShareName = null;
DeleteSharePath(directoryPath);
using (ManagementClass manage = new ManagementClass("Win32_Share"))
{
int num = 1;
string shareName;
while (ShareExists((shareName = sharePrefix + num)))
{
num++;
}
ManagementBaseObject inputArgs = manage.GetMethodParameters("Create");
inputArgs["Name"] = shareName;
inputArgs["Path"] = directoryPath;
inputArgs["Description"] = shareDesc;
inputArgs["Type"] = 0; // Disk share type
inputArgs["Access"] = permissionForShareFolder();
ManagementBaseObject outParams = manage.InvokeMethod("Create", inputArgs, null);
uint ret = (uint)outParams.Properties["ReturnValue"].Value;
if (ret == 0)
{
resShareName = "//" + IPAddressString + '/' + shareName;
}
// Set the correct permissions on the share:
// full control for the current user while readable for "Everyone"
//ManagementObject v_theTrusteeObject =
// new ManagementClass("Win32_Trustee").CreateInstance();
//Object[] v_theSID = { 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 };
//v_theTrusteeObject["Domain"] = null;
//v_theTrusteeObject["Name"] = "Everyone";
//v_theTrusteeObject["SID"] = v_theSID;
//ManagementObject v_theAceObject =
// new ManagementClass("Win32_ACE").CreateInstance();
//v_theAceObject["AccessMask"] = (uint)0xffffffff;
//v_theAceObject["AceFlags"] = 0x3;
//v_theAceObject["AceType"] = 0;
//v_theAceObject["Trustee"] = v_theTrusteeObject;
//ManagementObject v_theSecDescObject =
// new ManagementClass("Win32_SecurityDescriptor").CreateInstance();
//object[] v_theAceArray = { v_theAceObject };
//v_theSecDescObject["DACL"] = v_theAceArray;
//ManagementBaseObject secArgs = manage.GetMethodParameters("SetShareInfo");
//secArgs["Access"] = v_theSecDescObject;
//ManagementBaseObject secOut = manage.InvokeMethod("SetShareInfo", secArgs, null);
}
return resShareName;
}
/// <summary>
/// Check whether the given hostname belongs to this host
/// </summary>
/// <param name="hostName">The hostname to check</param>
/// <returns>True if the given hostname belongs to this host</returns>
public static bool IsHostMyself(string hostName)
{
if (hostName.ToLower() == "localhost")
{
return true;
}
// Get the IP addresses associated with the hostName
IPAddress[] ipAddresses = Dns.GetHostAddresses(hostName);
if (ipAddresses != null)
{
// Check if any of those IP address is of a network interface
foreach (IPAddress ipAddress in ipAddresses)
{
NetworkInterface[] adapters =
NetworkInterface.GetAllNetworkInterfaces();
if (adapters != null)
{
foreach (NetworkInterface adapter in adapters)
{
foreach (UnicastIPAddressInformation ipInfo in
adapter.GetIPProperties().UnicastAddresses)
{
if (ipInfo.Address.Equals(ipAddress))
{
return true;
}
}
}
}
}
}
return false;
}
/// <summary>
/// Return a string for host type converting to standard string
/// representation of 3 characters.
/// </summary>
/// <param name="hostTypeStr">
/// A host type string typically obtained by invoking uname
/// </param>
public static string GetHostType(string hostTypeStr)
{
if (!string.IsNullOrEmpty(hostTypeStr))
{
try
{
hostTypeStr = hostTypeStr.Trim().Substring(0, 3).ToUpper();
if (hostTypeStr == "CYG")
{
hostTypeStr = "WIN";
}
}
catch (ArgumentOutOfRangeException)
{
}
}
else
hostTypeStr = "WIN";
return hostTypeStr;
}
/// <summary>
/// Get value of given environment variable accomodating for the
/// convention of variables like [varname].[system type]
/// </summary>
/// <param name="envVar">
/// The name of the environment variable. If [envVar].[system type]
/// is set then value of that variable is returned as result else
/// value of [envVar] is returned.
/// </param>
/// <param name="systemType">
/// A string representing the type of the system.
/// This is typically obtained using uname command.
/// </param>
public static string GetEnvironmentVariable(string envVar,
string systemType)
{
string envVal;
if (systemType == null)
{
systemType = SystemType;
}
if (envVar.EndsWith('.' + systemType))
{
envVal = Environment.GetEnvironmentVariable(envVar);
if (envVal == null || envVal.Length == 0)
{
envVal = Environment.GetEnvironmentVariable(envVar.Substring(0,
envVar.Length - systemType.Length - 1));
}
}
else
{
envVal = Environment.GetEnvironmentVariable(envVar + '.' + systemType);
if (envVal == null || envVal.Length == 0)
{
envVal = Environment.GetEnvironmentVariable(envVar);
}
}
return envVal;
}
/// <summary>
/// Get value of given environment variable accomodating for the
/// convention of variables like [varname].[system type]
/// </summary>
/// <param name="envVar">
/// The name of the environment variable. If [envVar].[system type]
/// is set then value of that variable is returned as result else
/// value of [envVar] is returned.
/// </param>
public static string GetEnvironmentVariable(string envVar)
{
return GetEnvironmentVariable(envVar, SystemType);
}
/// <summary>
/// Set given environment variable to given value accomodating for the
/// convention of variables like [varname].[system type]
/// </summary>
/// <param name="envVar">
/// The name of the environment variable. This can be of the form
/// [varname].[system type] and if the [system type] matches the type of
/// this system then just [varname] is also set to the given value else
/// value of [varname].[my system type] is also set to the given value.
/// </param>
/// <param name="envVal">The value of the environment variable</param>
public static void SetEnvironmentVariable(string envVar, string envVal)
{
Environment.SetEnvironmentVariable(envVar, envVal);
if (envVar.EndsWith('.' + SystemType))
{
Environment.SetEnvironmentVariable(envVar.Substring(0,
envVar.Length - SystemType.Length - 1), envVal);
}
else
{
string currentVal = Environment.GetEnvironmentVariable(
envVar + '.' + SystemType);
if (currentVal == null || currentVal.Length == 0)
{
Environment.SetEnvironmentVariable(envVar + '.' + SystemType,
envVal);
}
}
}
/// <summary>
/// Hide the Plink password arg (if any) from the given args.
/// </summary>
/// <remarks>
/// Will not work properly if the password contains space/tab characters.
/// </remarks>
/// <param name="sshArgs">The arguments to the plink command.</param>
/// <returns>The arguments with the password hidden.</returns>
public static string HideSshPassword(string sshArgs)
{
return Regex.Replace(sshArgs, @"-pw[ \t]*[^ \t]*", "-pw [hidden]");
}
/// <summary>
/// Hide the Psexec password arg (if any) from the given args.
/// </summary>
/// <remarks>
/// Will not work properly if the password contains space/tab characters.
/// </remarks>
/// <param name="psexecArgs">The arguments to the psexec command.</param>
/// <returns>The arguments with the password hidden.</returns>
public static string HidePsexecPassword(string psexecArgs)
{
return Regex.Replace(psexecArgs, @" -p [ \t]*[^ \t]*", "-p [hidden]");
}
/// <summary>
/// Register a delegate to be invoked when the test on client completes.
/// </summary>
/// <param name="deleg">The delegate to be registered.</param>
public static void RegisterTestCompleteDelegate(UnitFnMethod deleg)
{
if (deleg != null)
{
m_testCompleteMap[deleg] = true;
}
}
/// <summary>
/// Get the base log directory for all tests for the given host type.
/// </summary>
/// <param name="hostType">The host type of the node.</param>
/// <returns>
/// Path of the log directory for the host or "." if environment is not
/// properly set up.
/// </returns>
public static string GetFwkLogDir(string hostType)
{
string baseDir = Util.GetEnvironmentVariable("FWK_LOGDIR", hostType);
if (baseDir != null && baseDir.Length > 0)
{
string logDir = Util.NormalizePath(Util.LogFile);
int logSuffixPos = logDir.IndexOf("/FwkCS_");
string logSuffix = logDir.Substring(logSuffixPos + 1);
int logSuffixEndPos = logSuffix.IndexOf('/');
baseDir = baseDir + '/' + logSuffix.Substring(0, logSuffixEndPos);
}
return baseDir;
}
/// <summary>
/// Get the base directory where logs go for the current test for
/// the given host type.
/// </summary>
/// <param name="hostType">The host type of the node.</param>
/// <returns>
/// Path of the log directory for the host or "." if environment is not
/// properly set up.
/// </returns>
public static string GetLogBaseDir(string hostType)
{
string baseDir = null;
if (Util.LogFile != null && Util.LogFile.Length > 0)
{
string logDir = Util.GetEnvironmentVariable("FWK_LOGDIR", hostType);
baseDir = Util.NormalizePath(Path.GetDirectoryName(Util.LogFile));
if (logDir != null && logDir.Length > 0)
{
int baseSuffixPos = baseDir.IndexOf("/FwkCS_");
baseDir = logDir + baseDir.Substring(baseSuffixPos);
}
}
if (baseDir == null || baseDir.Length == 0)
{
baseDir = ".";
}
return baseDir;
}
/// <summary>
/// Try to copy the given set of files (matching the given pattern) for
/// source directory to destination directory.
/// </summary>
/// <param name="sourceDir">source directory containing the files</param>
/// <param name="pattern">pattern to match the files</param>
/// <param name="destDir">destination directory for the files</param>
/// <param name="recursive">true to do recursive copy</param>
public static void CopyFiles(string sourceDir, string pattern,
string destDir, bool recursive)
{
try
{
SearchOption opt = (recursive ? SearchOption.AllDirectories :
SearchOption.TopDirectoryOnly);
sourceDir = NormalizePath(sourceDir);
string[] files = Directory.GetFiles(sourceDir, pattern, opt);
for (int index = 0; index < files.Length; ++index)
{
try
{
string file = NormalizePath(files[index]);
string fileName = Path.GetFileName(file);
string dirPath = Util.NormalizePath(destDir + '/' +
Path.GetDirectoryName(file.Replace(sourceDir, "")));
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
File.Copy(file, dirPath + '/' + fileName);
}
catch (Exception)
{
}
}
}
catch (Exception)
{
}
}
}
/// <summary>
/// A simple generic reference class that wraps a value type.
/// </summary>
/// <typeparam name="T">The type to be wrapped.</typeparam>
/// <remarks>
/// This class is useful to make a value type act as a reference type
/// so that different parts of code can modify same value.
/// </remarks>
public class RefValue<T>
where T : struct
{
public T Value
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
public RefValue(T value)
{
m_value = value;
}
public override bool Equals(object obj)
{
return m_value.Equals(obj);
}
public override int GetHashCode()
{
return m_value.GetHashCode();
}
public override string ToString()
{
return m_value.ToString();
}
public static implicit operator T(RefValue<T> value)
{
return value.m_value;
}
public static explicit operator RefValue<T>(T value)
{
return new RefValue<T>(value);
}
private T m_value;
}
public class PaceMeter
{
private DateTime m_endTime;
private int m_current;
private TimeSpan m_waitTime;
private int m_opsLimit;
public PaceMeter(int ops)
: this(ops, 1)
{
}
public PaceMeter(int ops, int seconds)
{
m_current = 0;
m_opsLimit = 0;
if (ops > 0 && seconds > 0)
{
if (ops < seconds) // This is the "seconds per op" case
{
m_opsLimit = ops;
m_waitTime = TimeSpan.FromSeconds(seconds);
}
else
{
int opsSec = ops / seconds;
if (opsSec > 4) // Use sample interval of 0.25 secs
{
m_opsLimit = opsSec / 4;
m_waitTime = TimeSpan.FromSeconds(0.25);
}
else // Use sample interval of 1.0 secs
{
m_opsLimit = opsSec;
m_waitTime = TimeSpan.FromSeconds(1.0);
}
}
ResetCurrent(DateTime.Now);
}
}
public void CheckPace()
{
if (m_opsLimit > 0)
{
if (++m_current >= m_opsLimit)
{
DateTime now = DateTime.Now;
if (m_endTime > now)
{
TimeSpan sleepSpan = m_endTime - now;
//Util.Log("PaceMeter:CheckPace:: Expected to end at: {0}:{1}:{2}.{3}, sleep span: {4}",
// m_endTime.Hour.ToString("D02"), m_endTime.Minute.ToString("D02"),
// m_endTime.Second.ToString("D02"), m_endTime.Millisecond.ToString("D03"), sleepSpan);
Thread.Sleep(sleepSpan);
}
ResetCurrent(now);
}
}
}
private void ResetCurrent(DateTime now)
{
m_current = 0;
m_endTime = now + m_waitTime;
}
}
}