//#define _LOG_TO_FILE using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Threading; using Perforce.P4; namespace p4api.net.unit.test { /// <summary> ///This is a test class for P4CommandTest and is intended ///to contain all P4CommandTest Unit Tests ///</summary> [TestClass()] public class P4CommandTest { String TestDir = "c:\\MyTestDir"; private TestContext testContextInstance; /// <summary> ///Gets or sets the test context which provides ///information about and functionality for the current test run. ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // //You can use the following additional attributes as you write your tests: // //Use ClassInitialize to run code before running the first test in the class //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //Use ClassCleanup to run code after all tests in a class have run //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} // //Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion /// <summary> ///A test for Args ///</summary> [TestMethod()] public void ArgsTest() { bool unicode = false; string serverAddr = "localhost:6666"; string user = "admin"; string pass = string.Empty; string ws_client = "admin_space"; // turn off exceptions for this test ErrorSeverity oldExceptionLevel = P4Exception.MinThrowLevel; P4Exception.MinThrowLevel = ErrorSeverity.E_NOEXC; for( int i = 0; i < 2; i++ ) // run once for ascii, once for unicode { Process p4d = Utilities.DeployP4TestServer( TestDir, unicode ); try { using( P4Server server = new P4Server( serverAddr, user, pass, ws_client ) ) { if( unicode ) Assert.IsTrue( server.UseUnicode, "Unicode server detected as not supporting Unicode" ); else Assert.IsFalse( server.UseUnicode, "Non Unicode server detected as supporting Unicode" ); P4Command target = new P4Command( server ); StringList expected = new StringList(new string[]{ "a", "b", "c" }); target.Args = expected; StringList actual = target.Args; Assert.AreEqual( expected, actual ); } } finally { Utilities.RemoveTestServer( p4d, TestDir ); } unicode = !unicode; } // reset the exception level P4Exception.MinThrowLevel = oldExceptionLevel; } /// <summary> ///A test for Run ///</summary> [TestMethod()] public void RunTest() { bool unicode = false; string serverAddr = "localhost:6666"; string user = "admin"; string pass = string.Empty; string ws_client = "admin_space"; // turn off exceptions for this test ErrorSeverity oldExceptionLevel = P4Exception.MinThrowLevel; P4Exception.MinThrowLevel = ErrorSeverity.E_NOEXC; for( int i = 0; i < 2; i++ ) // run once for ascii, once for unicode { Process p4d = Utilities.DeployP4TestServer( TestDir, unicode ); try { using( P4Server server = new P4Server( serverAddr, user, pass, ws_client ) ) { if( unicode ) Assert.IsTrue( server.UseUnicode, "Unicode server detected as not supporting Unicode" ); else Assert.IsFalse( server.UseUnicode, "Non Unicode server detected as supporting Unicode" ); P4Command target = new P4Command( server, "help", false, null ); P4CommandResult results = target.Run(); Assert.IsTrue( results.Success ); } } finally { Utilities.RemoveTestServer( p4d, TestDir ); } unicode = !unicode; } // reset the exception level P4Exception.MinThrowLevel = oldExceptionLevel; } /// <summary> ///A test for Run ///</summary> [TestMethod()] public void RunTest1() { bool unicode = false; string serverAddr = "localhost:6666"; string user = "admin"; string pass = string.Empty; string ws_client = "admin_space"; // turn off exceptions for this test ErrorSeverity oldExceptionLevel = P4Exception.MinThrowLevel; P4Exception.MinThrowLevel = ErrorSeverity.E_NOEXC; for( int i = 0; i < 2; i++ ) // run once for ascii, once for unicode { Process p4d = Utilities.DeployP4TestServer( TestDir, unicode ); try { using( P4Server server = new P4Server( serverAddr, user, pass, ws_client ) ) { if( unicode ) Assert.IsTrue( server.UseUnicode, "Unicode server detected as not supporting Unicode" ); else Assert.IsFalse( server.UseUnicode, "Non Unicode server detected as supporting Unicode" ); P4Command target = new P4Command( server, "help", false, null ); P4CommandResult results = target.Run(new String[] { "print" }); Assert.IsTrue( results.Success ); P4ClientInfoMessageList helpTxt = target.InfoOutput; Assert.IsNotNull( helpTxt ); } } finally { Utilities.RemoveTestServer( p4d, TestDir ); } unicode = !unicode; } // reset the exception level P4Exception.MinThrowLevel = oldExceptionLevel; } P4Command cmd1 = null; P4Command cmd2 = null; P4Command cmd3 = null; P4Command cmd4 = null; P4Command cmd5 = null; P4Command cmd6 = null; bool run = true; TimeSpan delay = TimeSpan.FromMilliseconds(5); private void cmdThreadProc1() { try { while (run) { cmd1 = new P4Command(server, "fstat", false, "//depot/..."); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 1 starting command: {0:X8}, at {1}", cmd1.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd1.Run(); WriteLine(string.Format("Thread 1 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd1.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now - StartedAt).TotalMilliseconds)); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput != null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList != null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput != null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput != null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs != null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 1, fstat failed:{0}",(result.ErrorList!=null && result.ErrorList.Count>0)?result.ErrorList[0].ErrorMessage :"<unknown error>")); } else { WriteLine(string.Format("Thread 1, fstat Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 1 cleanly exited"); return; } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } private void cmdThreadProc2() { try { while (run) { cmd2 = new P4Command(server, "dirs", false, "//depot/*"); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 2 starting command: {0:X8}, at {1}", cmd2.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd2.Run(); WriteLine(string.Format("Thread 2 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd2.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now - StartedAt).TotalMilliseconds)); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput!=null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList!=null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput!=null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput!=null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs!=null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 2, dirs failed:{0}", (result.ErrorList != null && result.ErrorList.Count > 0) ? result.ErrorList[0].ErrorMessage : "<unknown error>")); } else { WriteLine(string.Format("Thread 2, dirs Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 2 cleanly exited"); } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } private void cmdThreadProc3() { try { while (run) { cmd3 = new P4Command(server, "edit", false, "-n", "C:\\MyTestDir\\admin_space\\..."); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 3 starting command: {0:X8}, at {1}", cmd3.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd3.Run(); WriteLine(string.Format("Thread 3 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd3.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now - StartedAt).TotalMilliseconds)); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput != null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList != null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput != null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput != null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs != null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 3, edit failed:{0}", (result.ErrorList != null && result.ErrorList.Count>0) ? result.ErrorList[0].ErrorMessage : "<unknown error>")); } else { WriteLine(string.Format("Thread 3, edit Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 3 cleanly exited"); } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } private void cmdThreadProc4() { try { while (run) { using (P4Server _P4Server = new P4Server("localhost:6666", null, null, null)) { string val = P4Server.Get("P4IGNORE"); bool _p4IgnoreSet = !string.IsNullOrEmpty(val); if (_p4IgnoreSet) { WriteLine(string.Format("P4Ignore is set, {0}", val)); } else { WriteLine("P4Ignore is not set"); } Assert.IsTrue(_P4Server.ApiLevel > 0); } cmd4 = new P4Command(server, "fstat", false, "//depot/..."); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 4 starting command: {0:X8}, at {1}", cmd4.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd4.Run(); WriteLine(string.Format("Thread 4 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd4.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now - StartedAt).TotalMilliseconds)); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput != null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList != null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput != null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput != null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs != null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 4, fstat failed:{0}",(result.ErrorList!=null && result.ErrorList.Count>0)?result.ErrorList[0].ErrorMessage :"<unknown error>")); } else { WriteLine(string.Format("Thread 4, fstat Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 4 cleanly exited"); } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } private void cmdThreadProc5() { try { while (run) { cmd5 = new P4Command(server, "dirs", false, "//depot/*"); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 5 starting command: {0:X8}, at {1}", cmd5.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd5.Run(); WriteLine(string.Format("Thread 5 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd5.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now - StartedAt).TotalMilliseconds)); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput != null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList != null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput != null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput != null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs != null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 5, dirs failed:{0}", (result.ErrorList != null && result.ErrorList.Count > 0) ? result.ErrorList[0].ErrorMessage : "<unknown error>")); } else { WriteLine(string.Format("Thread 5, dirs Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 5 cleanly exited"); } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } private void cmdThreadProc6() { try { while (run) { cmd6 = new P4Command(server, "edit", false, "-n", "C:\\MyTestDir\\admin_space\\..."); DateTime StartedAt = DateTime.Now; WriteLine(string.Format("Thread 6 starting command: {0:X8}, at {1}", cmd6.CommandId, StartedAt.ToLongTimeString())); P4CommandResult result = cmd6.Run(); WriteLine(string.Format("Thread 6 Finished command: {0:X8}, at {1}, run time {2} Milliseconds", cmd6.CommandId, StartedAt.ToLongTimeString(), (DateTime.Now-StartedAt).TotalMilliseconds )); P4CommandResult lastResult = server.LastResults; Assert.AreEqual(result.Success, lastResult.Success); if (result.InfoOutput != null) { Assert.AreEqual(result.InfoOutput.Count, lastResult.InfoOutput.Count); } else { Assert.IsNull(lastResult.InfoOutput); } if (result.ErrorList != null) { Assert.AreEqual(result.ErrorList.Count, lastResult.ErrorList.Count); } else { Assert.IsNull(result.ErrorList); } if (result.TextOutput != null) { Assert.AreEqual(result.TextOutput, lastResult.TextOutput); } else { Assert.IsNull(lastResult.TextOutput); } if (result.TaggedOutput != null) { Assert.AreEqual(result.TaggedOutput.Count, lastResult.TaggedOutput.Count); } else { Assert.IsNull(lastResult.TaggedOutput); } Assert.AreEqual(result.Cmd, lastResult.Cmd); if (result.CmdArgs != null) { Assert.AreEqual(result.CmdArgs.Length, lastResult.CmdArgs.Length); } else { Assert.IsNull(lastResult.CmdArgs); } if (!result.Success) { WriteLine(string.Format("Thread 6, edit failed:{0}", (result.ErrorList != null && result.ErrorList.Count>0) ? result.ErrorList[0].ErrorMessage : "<unknown error>")); } else { WriteLine(string.Format("Thread 6, edit Success:{0}", (result.InfoOutput != null && result.InfoOutput.Count > 0) ? result.InfoOutput[0].Message : "<no output>")); } if (delay != TimeSpan.Zero) { Thread.Sleep(delay); } } WriteLine("Thread 6 cleanly exited"); } catch (ThreadAbortException) { Thread.ResetAbort(); return; } catch (Exception ex) { Assert.Fail(ex.Message); } } P4Server server = null; #if _LOG_TO_FILE static System.IO.StreamWriter sw = null; public static void WriteLine(string msg) { lock (sw) { sw.WriteLine(msg); sw.Flush(); } } public static void LogBridgeMessage( int log_level,String source,String message ) { WriteLine(string.Format("[{0}] {1}:{2}", source, log_level, message)); } private static LogFile.LogMessageDelgate LogFn = new LogFile.LogMessageDelgate(LogBridgeMessage); #else public void WriteLine(string msg) { Trace.WriteLine(msg); } #endif /// <summary> ///A test for Running multiple command concurrently ///</summary> [TestMethod()] public void RunAsyncTest() { #if _LOG_TO_FILE using (sw = new System.IO.StreamWriter("C:\\Logs\\RunAsyncTestLog.Txt", true)) { LogFile.SetLoggingFunction(LogFn); #endif bool unicode = false; string serverAddr = "localhost:6666"; string user = "admin"; string pass = string.Empty; string ws_client = "admin_space"; // turn off exceptions for this test ErrorSeverity oldExceptionLevel = P4Exception.MinThrowLevel; P4Exception.MinThrowLevel = ErrorSeverity.E_NOEXC; for (int i = 0; i < 1; i++) // run once for ascii, once for unicode { Process p4d = Utilities.DeployP4TestServer(TestDir, unicode); try { using (server = new P4Server(serverAddr, user, pass, ws_client)) { if (unicode) Assert.IsTrue(server.UseUnicode, "Unicode server detected as not supporting Unicode"); else Assert.IsFalse(server.UseUnicode, "Non Unicode server detected as supporting Unicode"); run = true; Thread t1 = new Thread(new ThreadStart(cmdThreadProc1)); t1.Name = "RunAsyncTest Thread t1"; Thread t2 = new Thread(new ThreadStart(cmdThreadProc2)); t2.Name = "RunAsyncTest Thread t2"; Thread t3 = new Thread(new ThreadStart(cmdThreadProc3)); t3.Name = "RunAsyncTest Thread t3"; Thread t4 = new Thread(new ThreadStart(cmdThreadProc4)); t4.Name = "RunAsyncTest Thread t4"; Thread t5 = new Thread(new ThreadStart(cmdThreadProc5)); t5.Name = "RunAsyncTest Thread t5"; Thread t6 = new Thread(new ThreadStart(cmdThreadProc6)); t6.Name = "RunAsyncTest Thread t6"; t1.Start(); Thread.Sleep(TimeSpan.FromSeconds(5)); // wait to start a 4th thread t2.Start(); t3.Start(); Thread.Sleep(TimeSpan.FromSeconds(5)); // wait to start a 4th thread run = false; if (t1.Join(1000) == false) { WriteLine("Thread 1 did not cleanly exit"); t1.Abort(); } if (t2.Join(1000) == false) { WriteLine("Thread 2 did not cleanly exit"); t2.Abort(); } if (t3.Join(1000) == false) { WriteLine("Thread 3 did not cleanly exit"); t3.Abort(); } Thread.Sleep(TimeSpan.FromSeconds(15)); // wait 15 seconds so will disconnect run = true; ; t1 = new Thread(new ThreadStart(cmdThreadProc1)); t1.Name = "RunAsyncTest Thread t1b"; t2 = new Thread(new ThreadStart(cmdThreadProc2)); t2.Name = "RunAsyncTest Thread t2b"; t3 = new Thread(new ThreadStart(cmdThreadProc3)); t3.Name = "RunAsyncTest Thread t3b"; t1.Start(); t2.Start(); t3.Start(); Thread.Sleep(TimeSpan.FromSeconds(1)); // wait to start a 4th thread t4.Start(); Thread.Sleep(TimeSpan.FromSeconds(2)); // wait to start a 5th thread t5.Start(); Thread.Sleep(TimeSpan.FromSeconds(3)); // wait to start a 6th thread t6.Start(); Thread.Sleep(TimeSpan.FromSeconds(15)); // run all threads for 15 seconds run = false; if (t1.Join(1000) == false) { WriteLine("Thread 1 did not cleanly exit"); t1.Abort(); } if (t2.Join(1000) == false) { WriteLine("Thread 2 did not cleanly exit"); t2.Abort(); } if (t3.Join(1000) == false) { WriteLine("Thread 3 did not cleanly exit"); t3.Abort(); } if (t4.Join(1000) == false) { WriteLine("Thread 4 did not cleanly exit"); t4.Abort(); } if (t5.Join(1000) == false) { WriteLine("Thread 5 did not cleanly exit"); t5.Abort(); } if (t6.Join(1000) == false) { WriteLine("Thread 6 did not cleanly exit"); t6.Abort(); } } } catch (Exception ex) { Assert.Fail("Test threw an exception: {0}\r\n{1}", ex.Message, ex.StackTrace); } finally { Utilities.RemoveTestServer(p4d, TestDir); } unicode = !unicode; } // reset the exception level P4Exception.MinThrowLevel = oldExceptionLevel; #if _LOG_TO_FILE } #endif } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 12585 | dbarbee |
Populate -o //guest/perforce_software/p4connect/... //guest/dbarbee/dev/p4connect/.... |
||
//guest/perforce_software/p4connect/src/P4Bridge/p4api.net-unit-test/P4CommandTest.cs | |||||
#2 | 12135 | Norman Morse |
Integrate dev branch changes into main. This code is the basiis of the 2.7 BETA release which provides Unity 5 compatibility |
||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |