CoopTerminationTests.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Threading;
  30. using Nini.Config;
  31. using NUnit.Framework;
  32. using OpenMetaverse;
  33. using OpenSim.Framework;
  34. using OpenSim.Region.CoreModules.Scripting.WorldComm;
  35. using OpenSim.Region.Framework.Scenes;
  36. using OpenSim.Region.Framework.Interfaces;
  37. using OpenSim.Region.ScriptEngine.XEngine;
  38. using OpenSim.Tests.Common;
  39. namespace OpenSim.Region.ScriptEngine.Shared.Instance.Tests
  40. {
  41. /// <summary>
  42. /// Test that co-operative script thread termination is working correctly.
  43. /// </summary>
  44. [TestFixture]
  45. public class CoopTerminationTests : OpenSimTestCase
  46. {
  47. private TestScene m_scene;
  48. private OpenSim.Region.ScriptEngine.XEngine.XEngine m_xEngine;
  49. private AutoResetEvent m_chatEvent;
  50. private AutoResetEvent m_stoppedEvent;
  51. private OSChatMessage m_osChatMessageReceived;
  52. /// <summary>
  53. /// Number of chat messages received so far. Reset before each test.
  54. /// </summary>
  55. private int m_chatMessagesReceived;
  56. /// <summary>
  57. /// Number of chat messages expected. m_chatEvent is not fired until this number is reached or exceeded.
  58. /// </summary>
  59. private int m_chatMessagesThreshold;
  60. [SetUp]
  61. public void Init()
  62. {
  63. m_osChatMessageReceived = null;
  64. m_chatMessagesReceived = 0;
  65. m_chatMessagesThreshold = 0;
  66. m_chatEvent = new AutoResetEvent(false);
  67. m_stoppedEvent = new AutoResetEvent(false);
  68. //AppDomain.CurrentDomain.SetData("APPBASE", Environment.CurrentDirectory + "/bin");
  69. // Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);
  70. m_xEngine = new OpenSim.Region.ScriptEngine.XEngine.XEngine();
  71. m_xEngine.DebugLevel = 1;
  72. IniConfigSource configSource = new IniConfigSource();
  73. IConfig startupConfig = configSource.AddConfig("Startup");
  74. startupConfig.Set("DefaultScriptEngine", "XEngine");
  75. IConfig xEngineConfig = configSource.AddConfig("XEngine");
  76. xEngineConfig.Set("Enabled", "true");
  77. xEngineConfig.Set("StartDelay", "0");
  78. // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call
  79. // to AssemblyResolver.OnAssemblyResolve fails.
  80. xEngineConfig.Set("AppDomainLoading", "false");
  81. xEngineConfig.Set("ScriptStopStrategy", "co-op");
  82. // Make sure loops aren't actually being terminated by a script delay wait.
  83. xEngineConfig.Set("ScriptDelayFactor", 0);
  84. // This is really just set for debugging the test.
  85. xEngineConfig.Set("WriteScriptSourceToDebugFile", true);
  86. // Set to false if we need to debug test so the old scripts don't get wiped before each separate test
  87. // xEngineConfig.Set("DeleteScriptsOnStartup", false);
  88. // This is not currently used at all for co-op termination. Bumping up to demonstrate that co-op termination
  89. // has an effect - without it tests will fail due to a 120 second wait for the event to finish.
  90. xEngineConfig.Set("WaitForEventCompletionOnScriptStop", 120000);
  91. IConfig config = configSource.AddConfig("OSSL");
  92. config.Set("DebuggerSafe", false);
  93. config.Set("AllowOSFunctions", "true");
  94. config.Set("OSFunctionThreatLevel", "Severe");
  95. m_scene = new SceneHelpers().SetupScene("My Test", TestHelpers.ParseTail(0x9999), 1000, 1000, configSource);
  96. SceneHelpers.SetupSceneModules(m_scene, configSource, m_xEngine);
  97. m_scene.StartScripts();
  98. }
  99. /// <summary>
  100. /// Test co-operative termination on derez of an object containing a script with a long-running event.
  101. /// </summary>
  102. /// <remarks>
  103. /// TODO: Actually compiling the script is incidental to this test. Really want a way to compile test scripts
  104. /// within the build itself.
  105. /// </remarks>
  106. [Test]
  107. public void TestStopOnLongSleep()
  108. {
  109. TestHelpers.InMethod();
  110. // TestHelpers.EnableLogging();
  111. string script =
  112. @"default
  113. {
  114. state_entry()
  115. {
  116. llSay(0, ""Thin Lizzy"");
  117. llSleep(60);
  118. }
  119. }";
  120. TestStop(script);
  121. }
  122. [Test]
  123. public void TestNoStopOnSingleStatementForLoop()
  124. {
  125. TestHelpers.InMethod();
  126. // TestHelpers.EnableLogging();
  127. string script =
  128. @"default
  129. {
  130. state_entry()
  131. {
  132. integer i = 0;
  133. for (i = 0; i <= 1; i++) llSay(0, ""Iter "" + (string)i);
  134. }
  135. }";
  136. TestSingleStatementNoStop(script);
  137. }
  138. [Test]
  139. public void TestStopOnLongSingleStatementForLoop()
  140. {
  141. TestHelpers.InMethod();
  142. // TestHelpers.EnableLogging();
  143. string script =
  144. @"default
  145. {
  146. state_entry()
  147. {
  148. integer i = 0;
  149. llSay(0, ""Thin Lizzy"");
  150. for (i = 0; i < 2147483647; i++) llSay(0, ""Iter "" + (string)i);
  151. }
  152. }";
  153. TestStop(script);
  154. }
  155. [Test]
  156. public void TestStopOnLongCompoundStatementForLoop()
  157. {
  158. TestHelpers.InMethod();
  159. // TestHelpers.EnableLogging();
  160. string script =
  161. @"default
  162. {
  163. state_entry()
  164. {
  165. integer i = 0;
  166. llSay(0, ""Thin Lizzy"");
  167. for (i = 0; i < 2147483647; i++)
  168. {
  169. llSay(0, ""Iter "" + (string)i);
  170. }
  171. }
  172. }";
  173. TestStop(script);
  174. }
  175. [Test]
  176. public void TestNoStopOnSingleStatementWhileLoop()
  177. {
  178. TestHelpers.InMethod();
  179. // TestHelpers.EnableLogging();
  180. string script =
  181. @"default
  182. {
  183. state_entry()
  184. {
  185. integer i = 0;
  186. while (i < 2) llSay(0, ""Iter "" + (string)i++);
  187. }
  188. }";
  189. TestSingleStatementNoStop(script);
  190. }
  191. [Test]
  192. public void TestStopOnLongSingleStatementWhileLoop()
  193. {
  194. TestHelpers.InMethod();
  195. // TestHelpers.EnableLogging();
  196. string script =
  197. @"default
  198. {
  199. state_entry()
  200. {
  201. integer i = 0;
  202. llSay(0, ""Thin Lizzy"");
  203. while (1 == 1)
  204. llSay(0, ""Iter "" + (string)i++);
  205. }
  206. }";
  207. TestStop(script);
  208. }
  209. [Test]
  210. public void TestStopOnLongCompoundStatementWhileLoop()
  211. {
  212. TestHelpers.InMethod();
  213. // TestHelpers.EnableLogging();
  214. string script =
  215. @"default
  216. {
  217. state_entry()
  218. {
  219. integer i = 0;
  220. llSay(0, ""Thin Lizzy"");
  221. while (1 == 1)
  222. {
  223. llSay(0, ""Iter "" + (string)i++);
  224. }
  225. }
  226. }";
  227. TestStop(script);
  228. }
  229. [Test]
  230. public void TestNoStopOnSingleStatementDoWhileLoop()
  231. {
  232. TestHelpers.InMethod();
  233. // TestHelpers.EnableLogging();
  234. string script =
  235. @"default
  236. {
  237. state_entry()
  238. {
  239. integer i = 0;
  240. do llSay(0, ""Iter "" + (string)i++);
  241. while (i < 2);
  242. }
  243. }";
  244. TestSingleStatementNoStop(script);
  245. }
  246. [Test]
  247. public void TestStopOnLongSingleStatementDoWhileLoop()
  248. {
  249. TestHelpers.InMethod();
  250. // TestHelpers.EnableLogging();
  251. string script =
  252. @"default
  253. {
  254. state_entry()
  255. {
  256. integer i = 0;
  257. llSay(0, ""Thin Lizzy"");
  258. do llSay(0, ""Iter "" + (string)i++);
  259. while (1 == 1);
  260. }
  261. }";
  262. TestStop(script);
  263. }
  264. [Test]
  265. public void TestStopOnLongCompoundStatementDoWhileLoop()
  266. {
  267. TestHelpers.InMethod();
  268. // TestHelpers.EnableLogging();
  269. string script =
  270. @"default
  271. {
  272. state_entry()
  273. {
  274. integer i = 0;
  275. llSay(0, ""Thin Lizzy"");
  276. do
  277. {
  278. llSay(0, ""Iter "" + (string)i++);
  279. } while (1 == 1);
  280. }
  281. }";
  282. TestStop(script);
  283. }
  284. [Test]
  285. public void TestStopOnInfiniteJumpLoop()
  286. {
  287. TestHelpers.InMethod();
  288. TestHelpers.EnableLogging();
  289. string script =
  290. @"default
  291. {
  292. state_entry()
  293. {
  294. integer i = 0;
  295. llSay(0, ""Thin Lizzy"");
  296. @p1;
  297. llSay(0, ""Iter "" + (string)i++);
  298. jump p1;
  299. }
  300. }";
  301. TestStop(script);
  302. }
  303. // Disabling for now as these are not particularly useful tests (since they fail due to stack overflow before
  304. // termination can even be tried.
  305. // [Test]
  306. public void TestStopOnInfiniteUserFunctionCallLoop()
  307. {
  308. TestHelpers.InMethod();
  309. // TestHelpers.EnableLogging();
  310. string script =
  311. @"
  312. integer i = 0;
  313. ufn1()
  314. {
  315. llSay(0, ""Iter ufn1() "" + (string)i++);
  316. ufn1();
  317. }
  318. default
  319. {
  320. state_entry()
  321. {
  322. integer i = 0;
  323. llSay(0, ""Thin Lizzy"");
  324. ufn1();
  325. }
  326. }";
  327. TestStop(script);
  328. }
  329. // Disabling for now as these are not particularly useful tests (since they fail due to stack overflow before
  330. // termination can even be tried.
  331. // [Test]
  332. public void TestStopOnInfiniteManualEventCallLoop()
  333. {
  334. TestHelpers.InMethod();
  335. // TestHelpers.EnableLogging();
  336. string script =
  337. @"default
  338. {
  339. state_entry()
  340. {
  341. integer i = 0;
  342. llSay(0, ""Thin Lizzy"");
  343. llSay(0, ""Iter"" + (string)i++);
  344. default_event_state_entry();
  345. }
  346. }";
  347. TestStop(script);
  348. }
  349. private SceneObjectPart CreateScript(string script, string itemName, UUID userId)
  350. {
  351. // UUID objectId = TestHelpers.ParseTail(0x100);
  352. // UUID itemId = TestHelpers.ParseTail(0x3);
  353. SceneObjectGroup so
  354. = SceneHelpers.CreateSceneObject(1, userId, string.Format("Object for {0}", itemName), 0x100);
  355. m_scene.AddNewSceneObject(so, true);
  356. InventoryItemBase itemTemplate = new InventoryItemBase();
  357. // itemTemplate.ID = itemId;
  358. itemTemplate.Name = itemName;
  359. itemTemplate.Folder = so.UUID;
  360. itemTemplate.InvType = (int)InventoryType.LSL;
  361. m_scene.EventManager.OnChatFromWorld += OnChatFromWorld;
  362. return m_scene.RezNewScript(userId, itemTemplate, script);
  363. }
  364. private void TestSingleStatementNoStop(string script)
  365. {
  366. // In these tests we expect to see at least 2 chat messages to confirm that the loop is working properly.
  367. m_chatMessagesThreshold = 2;
  368. UUID userId = TestHelpers.ParseTail(0x1);
  369. // UUID objectId = TestHelpers.ParseTail(0x100);
  370. // UUID itemId = TestHelpers.ParseTail(0x3);
  371. string itemName = "TestNoStop";
  372. SceneObjectPart partWhereRezzed = CreateScript(script, itemName, userId);
  373. // Wait for the script to start the event before we try stopping it.
  374. m_chatEvent.WaitOne(60000);
  375. if (m_osChatMessageReceived == null)
  376. Assert.Fail("Script did not start");
  377. else
  378. Assert.That(m_chatMessagesReceived, Is.EqualTo(2));
  379. bool running;
  380. TaskInventoryItem scriptItem = partWhereRezzed.Inventory.GetInventoryItem(itemName);
  381. Assert.That(
  382. SceneObjectPartInventory.TryGetScriptInstanceRunning(m_scene, scriptItem, out running), Is.True);
  383. Assert.That(running, Is.True);
  384. }
  385. private void TestStop(string script)
  386. {
  387. // In these tests we're only interested in the first message to confirm that the script has started.
  388. m_chatMessagesThreshold = 1;
  389. UUID userId = TestHelpers.ParseTail(0x1);
  390. // UUID objectId = TestHelpers.ParseTail(0x100);
  391. // UUID itemId = TestHelpers.ParseTail(0x3);
  392. string itemName = "TestStop";
  393. SceneObjectPart partWhereRezzed = CreateScript(script, itemName, userId);
  394. TaskInventoryItem rezzedItem = partWhereRezzed.Inventory.GetInventoryItem(itemName);
  395. // Wait for the script to start the event before we try stopping it.
  396. m_chatEvent.WaitOne(60000);
  397. if (m_osChatMessageReceived != null)
  398. Console.WriteLine("Script started with message [{0}]", m_osChatMessageReceived.Message);
  399. else
  400. Assert.Fail("Script did not start");
  401. // FIXME: This is a very poor way of trying to avoid a low-probability race condition where the script
  402. // executes llSay() but has not started the next statement before we try to stop it.
  403. Thread.Sleep(1000);
  404. // We need a way of carrying on if StopScript() fail, since it won't return if the script isn't actually
  405. // stopped. This kind of multi-threading is far from ideal in a regression test.
  406. new Thread(() => { m_xEngine.StopScript(rezzedItem.ItemID); m_stoppedEvent.Set(); }).Start();
  407. if (!m_stoppedEvent.WaitOne(30000))
  408. Assert.Fail("Script did not co-operatively stop.");
  409. bool running;
  410. TaskInventoryItem scriptItem = partWhereRezzed.Inventory.GetInventoryItem(itemName);
  411. Assert.That(
  412. SceneObjectPartInventory.TryGetScriptInstanceRunning(m_scene, scriptItem, out running), Is.True);
  413. Assert.That(running, Is.False);
  414. }
  415. private void OnChatFromWorld(object sender, OSChatMessage oscm)
  416. {
  417. Console.WriteLine("Got chat [{0}]", oscm.Message);
  418. m_osChatMessageReceived = oscm;
  419. if (++m_chatMessagesReceived >= m_chatMessagesThreshold)
  420. {
  421. m_scene.EventManager.OnChatFromWorld -= OnChatFromWorld;
  422. m_chatEvent.Set();
  423. }
  424. }
  425. }
  426. }