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

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