/interpreter/branches/protocol-aware-callbacks/test/edu/vub/at/actors/natives/DistributionTest.java

http://ambienttalk.googlecode.com/ · Java · 464 lines · 287 code · 102 blank · 75 comment · 12 complexity · e0e68d2f8cab5d418cb689f3f7c5d2d8 MD5 · raw file

  1. package edu.vub.at.actors.natives;
  2. import edu.vub.at.actors.eventloops.Callable;
  3. import edu.vub.at.actors.net.ConnectionListener;
  4. import edu.vub.at.eval.Evaluator;
  5. import edu.vub.at.exceptions.InterpreterException;
  6. import edu.vub.at.objects.ATObject;
  7. import edu.vub.at.objects.ATTable;
  8. import edu.vub.at.objects.mirrors.NativeClosure;
  9. import edu.vub.at.objects.natives.NATNil;
  10. import edu.vub.at.objects.natives.NATObjectClosureTest;
  11. import edu.vub.at.objects.natives.grammar.AGSymbol;
  12. import edu.vub.at.parser.NATParser;
  13. import junit.framework.TestCase;
  14. public class DistributionTest extends TestCase {
  15. private ELVirtualMachine virtual1_;
  16. private ELVirtualMachine virtual2_;
  17. /**
  18. * the VMs from this unit test join a dedicated test group (not the default group)
  19. * to avoid as much interference as possible with other running AmbientTalk interpreters.
  20. */
  21. private static final String _TEST_GROUP_NAME_ = "AmbientTalkTest";
  22. private static final int _TIMEOUT_ = 10000;
  23. private boolean testResult_ = false;
  24. // used to avoid 'final' restriction for nested classes
  25. protected synchronized void setTestResult(boolean value) {
  26. testResult_ = value;
  27. // typically, we wait for a given timeout but can resume earlier when this event takes place
  28. this.notify();
  29. }
  30. // used to avoid 'final' restriction for nested classes
  31. protected boolean getTestResult() {
  32. return testResult_;
  33. }
  34. public static void main(String[] args) {
  35. junit.swingui.TestRunner.run(NATObjectClosureTest.class);
  36. }
  37. protected void setUp() throws Exception {
  38. super.setUp();
  39. virtual1_ = new ELVirtualMachine(NATNil._INSTANCE_, new SharedActorField[] { }, _TEST_GROUP_NAME_);
  40. virtual2_ = new ELVirtualMachine(NATNil._INSTANCE_, new SharedActorField[] { }, _TEST_GROUP_NAME_);
  41. }
  42. protected void tearDown() throws Exception {
  43. if(virtual1_ != null) {
  44. virtual1_.event_goOffline();
  45. virtual1_.stopProcessing();
  46. }
  47. if(virtual2_ != null) {
  48. virtual2_.event_goOffline();
  49. virtual2_.stopProcessing();
  50. }
  51. }
  52. // Creates an ELActor, hosted on the provided VM.
  53. private ELActor setUpActor(ELVirtualMachine host) throws InterpreterException {
  54. return NATActorMirror.createEmptyActor(host, new NATActorMirror(host)).getFarHost();
  55. }
  56. // installs a closure in a particular actor's scope which allows signalling a return value
  57. // with the added bonus of waking up the test thread from waiting.
  58. private void setUpSuccessTrigger(ELActor processor) throws Exception {
  59. processor.sync_event_performTest(new Callable() {
  60. public Object call(Object argument)throws InterpreterException {
  61. return Evaluator.getGlobalLexicalScope().meta_defineField(
  62. AGSymbol.jAlloc("success"),
  63. new NativeClosure(NATNil._INSTANCE_) {
  64. public ATObject base_apply(ATTable arguments) throws InterpreterException {
  65. setTestResult(true);
  66. return NATNil._INSTANCE_;
  67. }
  68. });
  69. }
  70. });
  71. }
  72. // Joint code for the various test suites to test the behaviour of the AT connection observers
  73. private void setUpConnectionObservers() throws Exception {
  74. ELActor subscriber = setUpActor(virtual1_);
  75. ELActor provider = setUpActor(virtual2_);
  76. // We define a closure to inform us the test succeeded
  77. setUpSuccessTrigger(subscriber);
  78. subscriber.sync_event_eval(
  79. NATParser.parse("DistributionTest#setUpConnectionObservers()",
  80. "defstripe Service; \n" +
  81. "when: Service discovered: { | ref |" +
  82. " when: ref disconnected: { success(); }; \n" +
  83. " when: ref reconnected: { success(); }; \n" +
  84. // explicitly triggering success, although we are not testing service discovery
  85. // allows to minimize the waiting time until we can go offline
  86. " success(); " +
  87. "} \n;"));
  88. provider.sync_event_eval(
  89. NATParser.parse("DistributionTest#setUpConnectionObservers()",
  90. "defstripe Service; \n" +
  91. "export: (object: { nil }) as: Service"));
  92. }
  93. /**
  94. * When a virtual machine joins the AmbientTalk overlay network, all virtual machines are
  95. * notified of this fact by the underlying distribution layer. Messages are sent to the
  96. * DiscoveryManager to notify it of a new vm which may host required services and to all
  97. * (far reference) listeners waiting for the appearance of that VM.
  98. *
  99. * This test registers a dedicated listener to test the dispatching of connected and
  100. * disconnected messages to such listeners when appropriate.
  101. */
  102. public synchronized void testVirtualMachineDiscovery() {
  103. // We prevent any race conditions between the going online and offline, forcing both
  104. // handlers to be called. Therefore the test fails unless the disconnected handler
  105. // determines it was successful
  106. setTestResult(false);
  107. virtual1_.connectionManager_.addConnectionListener(
  108. virtual2_.getGUID(),
  109. new ConnectionListener() {
  110. public void connected() {
  111. setTestResult(true);
  112. }
  113. public void disconnected() { }
  114. });
  115. virtual1_.event_goOnline();
  116. virtual2_.event_goOnline();
  117. try {
  118. this.wait( _TIMEOUT_ );
  119. } catch (InterruptedException e) {};
  120. if(! getTestResult())
  121. fail("DiscoveryBus notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  122. }
  123. /**
  124. * When a virtual machine leaves the AmbientTalk overlay network, all virtual machines are
  125. * notified of this fact by the underlying distribution layer. Messages are sent to the
  126. * DiscoveryManager and to all (far reference) listeners connected to that VM.
  127. *
  128. * This test registers a dedicated listener to test the dispatching of disconnected
  129. * messages to such listeners when appropriate.
  130. */
  131. public synchronized void testVirtualMachineDisconnection() {
  132. setTestResult(false);
  133. virtual2_.connectionManager_.addConnectionListener(
  134. virtual1_.getGUID(),
  135. new ConnectionListener() {
  136. public void connected() { }
  137. public void disconnected() {
  138. setTestResult(true);
  139. }
  140. });
  141. virtual1_.event_goOnline();
  142. virtual2_.event_goOnline();
  143. try {
  144. this.wait( _TIMEOUT_ );
  145. } catch (InterruptedException e) {};
  146. virtual1_.event_goOffline();
  147. try {
  148. this.wait( _TIMEOUT_ );
  149. } catch (InterruptedException e) {};
  150. if(! getTestResult())
  151. fail("Disconnection notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  152. }
  153. /**
  154. * This test registers a dedicated listener to test the dispatching of disconnected
  155. * messages. When the time lapse betwen connection and disconnection is too small,
  156. * the test may be subject to race conditions, hence we provide a version where
  157. * no wait is performed, to provoke them.
  158. */
  159. public synchronized void testVirtualMachineDisconnectionRace() {
  160. // If the race occurs and neither the connected, nor the disconnected event listener
  161. // are triggered, the test should succeed, unless exceptions were raised.
  162. setTestResult(true);
  163. virtual2_.connectionManager_.addConnectionListener(
  164. virtual1_.getGUID(),
  165. new ConnectionListener() {
  166. public void connected() {
  167. setTestResult(false);
  168. }
  169. public void disconnected() {
  170. setTestResult(true);
  171. }
  172. });
  173. virtual1_.event_goOnline();
  174. virtual2_.event_goOnline();
  175. virtual1_.event_goOffline();
  176. try {
  177. this.wait( _TIMEOUT_ );
  178. } catch (InterruptedException e) {};
  179. if(! getTestResult())
  180. fail("Disconnection notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  181. }
  182. /**
  183. * Uses the when: discovered: and export: as: constructs to make an object on one virtual
  184. * machine accessible to another virtual machine.
  185. * @throws Exception
  186. */
  187. public synchronized void testServiceDiscovery() throws Exception {
  188. setTestResult(false);
  189. setUpConnectionObservers();
  190. virtual1_.event_goOnline();
  191. virtual2_.event_goOnline();
  192. try {
  193. this.wait( _TIMEOUT_ );
  194. } catch (InterruptedException e) {};
  195. if(! getTestResult())
  196. fail("Service DiscoveryBus notification has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  197. }
  198. /**
  199. * This test uses the when: disconnected: to detect when a far reference has become
  200. * disconnected. We distinguish between two tests, depending on the role of the device
  201. * that falls away. If the provider disconnects, the subscriber hosting the far reference
  202. * is notified of this event through by the distribution layer.
  203. *
  204. * @throws Exception
  205. */
  206. public synchronized void testProviderDisconnection() throws Exception {
  207. setUpConnectionObservers();
  208. virtual1_.event_goOnline();
  209. virtual2_.event_goOnline();
  210. try {
  211. this.wait( _TIMEOUT_ );
  212. } catch (InterruptedException e) {};
  213. // reset the test condition
  214. setTestResult(false);
  215. virtual2_.event_goOffline();
  216. try {
  217. this.wait( _TIMEOUT_ );
  218. } catch (InterruptedException e) {};
  219. if(! getTestResult())
  220. fail("Disconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  221. // reset the test condition
  222. setTestResult(false);
  223. virtual2_.event_goOnline();
  224. try {
  225. this.wait( );
  226. } catch (InterruptedException e) {};
  227. if(! getTestResult())
  228. fail("Reconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  229. }
  230. /**
  231. * This test uses the when: disconnected: to detect when a far reference has become
  232. * disconnected. We distinguish between two tests, depending on the role of the device
  233. * that goes offline.
  234. *
  235. * @throws Exception
  236. */
  237. public synchronized void testSubscriberDisconnection() throws Exception {
  238. setUpConnectionObservers();
  239. virtual1_.event_goOnline();
  240. virtual2_.event_goOnline();
  241. try {
  242. this.wait( _TIMEOUT_ );
  243. } catch (InterruptedException e) {};
  244. // reset the test condition
  245. setTestResult(false);
  246. virtual1_.event_goOffline();
  247. try {
  248. this.wait( _TIMEOUT_ );
  249. } catch (InterruptedException e) {};
  250. if(! getTestResult())
  251. fail("Disconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  252. // reset the test condition
  253. setTestResult(false);
  254. virtual1_.event_goOnline();
  255. try {
  256. this.wait( );
  257. } catch (InterruptedException e) {};
  258. if(! getTestResult())
  259. fail("Reconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  260. }
  261. public synchronized void testRetract() throws Exception {
  262. final ELActor provider = setUpActor(virtual1_);
  263. final ELActor subscriber = setUpActor(virtual2_);
  264. // We define a closure to inform us the test succeeded
  265. setUpSuccessTrigger(subscriber);
  266. subscriber.sync_event_eval(
  267. NATParser.parse("DistributionTest#testRetract()",
  268. "def messages := nil;" +
  269. "def far := nil;" +
  270. "defstripe Service; \n" +
  271. "when: Service discovered: { | ref | \n" +
  272. " far := ref; \n" +
  273. " when: ref disconnected: { \n" +
  274. " messages := retract: ref; \n" +
  275. " success(); \n" +
  276. " }; \n" +
  277. " success(); " +
  278. "} \n;"));
  279. provider.sync_event_eval(
  280. NATParser.parse("DistributionTest#testRetract()",
  281. "defstripe Service; \n" +
  282. "export: (object: { def inc(num) { num + 1; }; }) as: Service"));
  283. virtual1_.event_goOnline();
  284. virtual2_.event_goOnline();
  285. try {
  286. this.wait( _TIMEOUT_ );
  287. } catch (InterruptedException e) {};
  288. // reset the test condition
  289. setTestResult(false);
  290. // Spawn a new thread to allow the disconnection of the provider to be in
  291. // parallel with the sending of messages by the subscriber
  292. Thread sender = new Thread() {
  293. public void run() {
  294. try {
  295. subscriber.sync_event_eval(
  296. NATParser.parse("DistributionTest#testRetract()",
  297. "1.to: 5 do: { | i | \n" +
  298. " far<-inc(i); \n" +
  299. "}; \n" +
  300. // Stop waiting after five messages were scheduled to ensure
  301. // some messages may still be in the outbox
  302. "success(); \n"+
  303. "6.to: 10 do: { | i | \n" +
  304. " far<-inc(i); \n" +
  305. "}; \n"));
  306. } catch (InterpreterException e) {
  307. e.printStackTrace();
  308. fail("exception: " + e);
  309. }
  310. };
  311. };
  312. sender.start();
  313. // wait till some messages were sent
  314. try {
  315. this.wait( _TIMEOUT_ );
  316. } catch (InterruptedException e) {};
  317. virtual1_.event_goOffline();
  318. // wait till disconnection event was processed
  319. try {
  320. this.wait( _TIMEOUT_ );
  321. } catch (InterruptedException e) {};
  322. // ELActor must not be busy while retracting the messages
  323. sender.join();
  324. ATObject messages = subscriber.sync_event_eval(
  325. NATParser.parse("DistributionTest#testRetract()",
  326. "messages"));
  327. }
  328. public void notestSimple() {
  329. try {
  330. ELActor alice = setUpActor(virtual1_);
  331. ELActor bob = setUpActor(virtual2_);
  332. alice.sync_event_eval(
  333. NATParser.parse("CrossVMCommunicationTest#testSimple()",
  334. "defstripe HelloWorld; \n" +
  335. "whenever: HelloWorld discovered: { | ref | \n" +
  336. " ref <- hello(\"alice\"); \n" +
  337. "}; \n"));
  338. bob.sync_event_eval(
  339. NATParser.parse("CrossVMCommunicationTest#testSimple()",
  340. "defstripe HelloWorld; \n" +
  341. "def english := object: { \n" +
  342. " def hello( name ) { \"hello \" + name }; \n" +
  343. "}; \n" +
  344. "def spanish := object: { \n" +
  345. " def hello( name ) { \"hola \" + name }; \n" +
  346. "}; \n" +
  347. "export: english as: HelloWorld; \n" +
  348. "export: spanish as: HelloWorld; \n"));
  349. alice.host_.event_goOnline();
  350. bob.host_.event_goOnline();
  351. synchronized (this) {
  352. try {
  353. this.wait(10000);
  354. } catch (InterruptedException e) {
  355. e.printStackTrace();
  356. }
  357. }
  358. } catch (InterpreterException e) {
  359. e.printStackTrace();
  360. }
  361. }
  362. }