/interpreter/tags/at_build150307/test/edu/vub/at/actors/natives/DistributionTest.java

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