/interpreter/tags/at2-build270707/test/edu/vub/at/actors/natives/DistributionTest.java

http://ambienttalk.googlecode.com/ · Java · 485 lines · 290 code · 107 blank · 88 comment · 12 complexity · ee9430085303b9f91b2c1683bd777f0e 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.OBJNil;
  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(OBJNil._INSTANCE_, new SharedActorField[] { }, _TEST_GROUP_NAME_);
  40. virtual2_ = new ELVirtualMachine(OBJNil._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(OBJNil._INSTANCE_) {
  64. public ATObject base_apply(ATTable arguments) throws InterpreterException {
  65. setTestResult(true);
  66. return OBJNil._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. "deftype 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. "deftype 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. ConnectionListener aConnectionListener = new ConnectionListener() {
  108. public void connected() {
  109. setTestResult(true);
  110. }
  111. public void disconnected() { }
  112. public void takenOffline() { }
  113. };
  114. //Since ConnectionListenerManager keeps a map of WeakReferences, aDummyRef is created to prevent aConnectionListener from gc.
  115. ConnectionListener aDummyRef = aConnectionListener;
  116. virtual1_.connectionManager_.addConnectionListener( virtual2_.getGUID(), aConnectionListener);
  117. virtual1_.event_goOnline();
  118. virtual2_.event_goOnline();
  119. try {
  120. this.wait( _TIMEOUT_ );
  121. } catch (InterruptedException e) {};
  122. if(! getTestResult())
  123. fail("DiscoveryBus notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  124. }
  125. /**
  126. * When a virtual machine leaves the AmbientTalk overlay network, all virtual machines are
  127. * notified of this fact by the underlying distribution layer. Messages are sent to the
  128. * DiscoveryManager and to all (far reference) listeners connected to that VM.
  129. *
  130. * This test registers a dedicated listener to test the dispatching of disconnected
  131. * messages to such listeners when appropriate.
  132. */
  133. public synchronized void testVirtualMachineDisconnection() {
  134. setTestResult(false);
  135. ConnectionListener aConnectionListener = new ConnectionListener() {
  136. public void connected() { }
  137. public void disconnected() {
  138. setTestResult(true);
  139. }
  140. public void takenOffline() { }
  141. };
  142. //Since ConnectionListenerManager keeps a map of WeakReferences, aDummyRef is created to prevent aConnectionListener from gc.
  143. ConnectionListener aDummyRef = aConnectionListener;
  144. virtual2_.connectionManager_.addConnectionListener( virtual1_.getGUID(), aConnectionListener);
  145. virtual1_.event_goOnline();
  146. virtual2_.event_goOnline();
  147. try {
  148. this.wait( _TIMEOUT_ );
  149. } catch (InterruptedException e) {};
  150. virtual1_.event_goOffline();
  151. try {
  152. this.wait( _TIMEOUT_ );
  153. } catch (InterruptedException e) {};
  154. if(! getTestResult())
  155. fail("Disconnection notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  156. }
  157. /**
  158. * This test registers a dedicated listener to test the dispatching of disconnected
  159. * messages. When the time lapse betwen connection and disconnection is too small,
  160. * the test may be subject to race conditions, hence we provide a version where
  161. * no wait is performed, to provoke them.
  162. */
  163. public synchronized void testVirtualMachineDisconnectionRace() {
  164. // If the race occurs and neither the connected, nor the disconnected event listener
  165. // are triggered, the test should succeed, unless exceptions were raised.
  166. setTestResult(true);
  167. ConnectionListener aConnectionListener = new ConnectionListener() {
  168. public void connected() {
  169. setTestResult(false);
  170. }
  171. public void disconnected() {
  172. setTestResult(true);
  173. }
  174. public void takenOffline() { }
  175. };
  176. //Since membershipNotifier is a map of WeakReferences, aDummyRef is created to prevent aConnectionListener from gc.
  177. ConnectionListener aDummyRef = aConnectionListener;
  178. virtual2_.connectionManager_.addConnectionListener( virtual1_.getGUID(), aConnectionListener);
  179. /*virtual1_.getGUID(),
  180. new ConnectionListener() {
  181. public void connected() {
  182. setTestResult(false);
  183. }
  184. public void disconnected() {
  185. setTestResult(true);
  186. }
  187. public void takenOffline() { }
  188. });*/
  189. virtual1_.event_goOnline();
  190. virtual2_.event_goOnline();
  191. virtual1_.event_goOffline();
  192. try {
  193. this.wait( _TIMEOUT_ );
  194. } catch (InterruptedException e) {};
  195. if(! getTestResult())
  196. fail("Disconnection notification of the VM has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  197. }
  198. /**
  199. * Uses the when: discovered: and export: as: constructs to make an object on one virtual
  200. * machine accessible to another virtual machine.
  201. * @throws Exception
  202. */
  203. public synchronized void testServiceDiscovery() throws Exception {
  204. setTestResult(false);
  205. setUpConnectionObservers();
  206. virtual1_.event_goOnline();
  207. virtual2_.event_goOnline();
  208. try {
  209. this.wait( _TIMEOUT_ );
  210. } catch (InterruptedException e) {};
  211. if(! getTestResult())
  212. fail("Service DiscoveryBus notification has failed to arrive within " + _TIMEOUT_ /1000 + " sec.");
  213. }
  214. /**
  215. * This test uses the when: disconnected: to detect when a far reference has become
  216. * disconnected. We distinguish between two tests, depending on the role of the device
  217. * that falls away. If the provider disconnects, the subscriber hosting the far reference
  218. * is notified of this event through by the distribution layer.
  219. *
  220. * @throws Exception
  221. */
  222. public synchronized void testProviderDisconnection() throws Exception {
  223. setUpConnectionObservers();
  224. virtual1_.event_goOnline();
  225. virtual2_.event_goOnline();
  226. try {
  227. this.wait( _TIMEOUT_ );
  228. } catch (InterruptedException e) {};
  229. // reset the test condition
  230. setTestResult(false);
  231. virtual2_.event_goOffline();
  232. try {
  233. this.wait( _TIMEOUT_ );
  234. } catch (InterruptedException e) {};
  235. if(! getTestResult())
  236. fail("Disconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  237. // reset the test condition
  238. setTestResult(false);
  239. virtual2_.event_goOnline();
  240. try {
  241. this.wait( );
  242. } catch (InterruptedException e) {};
  243. if(! getTestResult())
  244. fail("Reconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  245. }
  246. /**
  247. * This test uses the when: disconnected: to detect when a far reference has become
  248. * disconnected. We distinguish between two tests, depending on the role of the device
  249. * that goes offline.
  250. *
  251. * @throws Exception
  252. */
  253. public synchronized void testSubscriberDisconnection() throws Exception {
  254. setUpConnectionObservers();
  255. virtual1_.event_goOnline();
  256. virtual2_.event_goOnline();
  257. try {
  258. this.wait( _TIMEOUT_ );
  259. } catch (InterruptedException e) {};
  260. // reset the test condition
  261. setTestResult(false);
  262. virtual1_.event_goOffline();
  263. try {
  264. this.wait( _TIMEOUT_ );
  265. } catch (InterruptedException e) {};
  266. if(! getTestResult())
  267. fail("Disconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  268. // reset the test condition
  269. setTestResult(false);
  270. virtual1_.event_goOnline();
  271. try {
  272. this.wait( );
  273. } catch (InterruptedException e) {};
  274. if(! getTestResult())
  275. fail("Reconnection observer has failed to trigger within " + _TIMEOUT_ /1000 + " sec.");
  276. }
  277. public synchronized void testRetract() throws Exception {
  278. final ELActor provider = setUpActor(virtual1_);
  279. final ELActor subscriber = setUpActor(virtual2_);
  280. // We define a closure to inform us the test succeeded
  281. setUpSuccessTrigger(subscriber);
  282. subscriber.sync_event_eval(
  283. NATParser.parse("DistributionTest#testRetract()",
  284. "def messages := nil;" +
  285. "def far := nil;" +
  286. "deftype Service; \n" +
  287. "when: Service discovered: { | ref | \n" +
  288. " far := ref; \n" +
  289. " when: ref disconnected: { \n" +
  290. " messages := retract: ref; \n" +
  291. " success(); \n" +
  292. " }; \n" +
  293. " success(); " +
  294. "} \n;"));
  295. provider.sync_event_eval(
  296. NATParser.parse("DistributionTest#testRetract()",
  297. "deftype Service; \n" +
  298. "export: (object: { def inc(num) { num + 1; }; }) as: Service"));
  299. virtual1_.event_goOnline();
  300. virtual2_.event_goOnline();
  301. try {
  302. this.wait( _TIMEOUT_ );
  303. } catch (InterruptedException e) {};
  304. // reset the test condition
  305. setTestResult(false);
  306. // Spawn a new thread to allow the disconnection of the provider to be in
  307. // parallel with the sending of messages by the subscriber
  308. Thread sender = new Thread() {
  309. public void run() {
  310. try {
  311. subscriber.sync_event_eval(
  312. NATParser.parse("DistributionTest#testRetract()",
  313. "1.to: 5 do: { | i | \n" +
  314. " far<-inc(i); \n" +
  315. "}; \n" +
  316. // Stop waiting after five messages were scheduled to ensure
  317. // some messages may still be in the outbox
  318. "success(); \n"+
  319. "6.to: 10 do: { | i | \n" +
  320. " far<-inc(i); \n" +
  321. "}; \n"));
  322. } catch (InterpreterException e) {
  323. e.printStackTrace();
  324. fail("exception: " + e);
  325. }
  326. };
  327. };
  328. sender.start();
  329. // wait till some messages were sent
  330. try {
  331. this.wait( _TIMEOUT_ );
  332. } catch (InterruptedException e) {};
  333. virtual1_.event_goOffline();
  334. // wait till disconnection event was processed
  335. try {
  336. this.wait( _TIMEOUT_ );
  337. } catch (InterruptedException e) {};
  338. // ELActor must not be busy while retracting the messages
  339. sender.join();
  340. ATObject messages = subscriber.sync_event_eval(
  341. NATParser.parse("DistributionTest#testRetract()",
  342. "messages"));
  343. }
  344. public void notestSimple() {
  345. try {
  346. ELActor alice = setUpActor(virtual1_);
  347. ELActor bob = setUpActor(virtual2_);
  348. alice.sync_event_eval(
  349. NATParser.parse("CrossVMCommunicationTest#testSimple()",
  350. "deftype HelloWorld; \n" +
  351. "whenever: HelloWorld discovered: { | ref | \n" +
  352. " ref <- hello(\"alice\"); \n" +
  353. "}; \n"));
  354. bob.sync_event_eval(
  355. NATParser.parse("CrossVMCommunicationTest#testSimple()",
  356. "deftype HelloWorld; \n" +
  357. "def english := object: { \n" +
  358. " def hello( name ) { \"hello \" + name }; \n" +
  359. "}; \n" +
  360. "def spanish := object: { \n" +
  361. " def hello( name ) { \"hola \" + name }; \n" +
  362. "}; \n" +
  363. "export: english as: HelloWorld; \n" +
  364. "export: spanish as: HelloWorld; \n"));
  365. alice.host_.event_goOnline();
  366. bob.host_.event_goOnline();
  367. synchronized (this) {
  368. try {
  369. this.wait(10000);
  370. } catch (InterruptedException e) {
  371. e.printStackTrace();
  372. }
  373. }
  374. } catch (InterpreterException e) {
  375. e.printStackTrace();
  376. }
  377. }
  378. }