PageRenderTime 25ms CodeModel.GetById 10ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist030708/test/edu/vub/at/objects/natives/ExceptionHandlingTest.java

http://ambienttalk.googlecode.com/
Java | 305 lines | 185 code | 25 blank | 95 comment | 0 complexity | 75be4208715fa6e2ddc6fa367fd63473 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * ExceptionHandlingTest.java created on Oct 10, 2006 at 10:34:10 PM
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.natives;
 29
 30import edu.vub.at.AmbientTalkTest;
 31import edu.vub.at.AmbientTalkTestCase;
 32import edu.vub.at.eval.Evaluator;
 33import edu.vub.at.exceptions.InterpreterException;
 34import edu.vub.at.exceptions.XSelectorNotFound;
 35import edu.vub.at.objects.ATObject;
 36import edu.vub.at.objects.coercion.NativeTypeTags;
 37import edu.vub.at.objects.natives.grammar.AGSymbol;
 38import edu.vub.at.objects.symbiosis.JavaClass;
 39
 40/**
 41 * This test documents and tests the behaviour of the exception handling primitives 
 42 * provided in Ambienttalk. In AmbientTalk any object can be used and thrown as an 
 43 * exception. Moreover, by relying on in types, all subtyping issues regarding to
 44 * handler selection are handled by the isTaggedAs meta operation.
 45 *
 46 * @author smostinc
 47 */
 48public class ExceptionHandlingTest extends AmbientTalkTestCase {
 49	
 50	/**
 51	 * Any AmbientTalk Language value can be used as an exception, proving there is nothing 
 52	 * special about exceptions. This test demonstrates this for a number of language values,
 53	 * such as numbers, tables, closures and type tags themselves. 
 54	 * 
 55	 * This test also shows that objects can be caught using the type they are tagged with.
 56	 * Note that most often, exceptions will be isolates, as these are objects (which can be
 57	 * made arbitrarily complex) which are passed by copy between actors. However, as the 
 58	 * type tags of a far reference are identical to the one of the original object, this 
 59	 * semantics is a default upon which can be varied.
 60	 */
 61	public void notestAllObjectsCanBeUsedAsExceptions() {
 62		try {
 63			evaluateInput(
 64					"def testCount := 0; \n" +
 65					"raise: 42 catch: Number do: { | exc | testCount := testCount + 1 }; \n" +
 66					"raise: [ 4, 8, 15, 16, 23, 42 ] catch: Table do: { | exc | testCount := testCount + 1 }; \n" +
 67					"raise: { testCount := testCount + 1 } catch: Closure do: { | exc | exc.apply([]) }; \n" +
 68					"raise: Number catch: TypeTag do: { | exc | testCount := testCount + 1 }; \n" +
 69					"if: testCount != 4 then: { fail() }", ctx_);
 70		} catch (InterpreterException e) {
 71			e.printStackTrace();
 72			fail("exception: "+ e);
 73		}
 74	};
 75	
 76	/**
 77	 * Handler selection in AmbientTalk is purely based on the types an object is tagged with.
 78	 * As such the correct handler selection can be delegated to the type system which ensures
 79	 * correct handler selection.
 80	 */
 81	public void testTypeBasedHandlerSelection() {
 82		try {
 83			evaluateInput(
 84					"deftype MyExceptions; \n" +
 85					"deftype IncorrectArguments <: MyExceptions; \n" +
 86					"def testCount := 0; \n" +
 87					"try: { \n" +
 88					"  raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ IncorrectArguments ]) catch: IncorrectArguments do: { | exc | testCount := testCount + 1 }; \n" +
 89					"  raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ IncorrectArguments ]) catch: MyExceptions do: { | exc | testCount := testCount + 1 }; \n" +
 90					"  raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ MyExceptions ]) catch: MyExceptions do: { | exc | testCount := testCount + 1 }; \n" +
 91					"  raise: (object: { def [ message, stackTrace ] := [ nil, nil ] } taggedAs: [ MyExceptions ]) catch: IncorrectArguments do: { | exc | testCount := testCount + 1 }; \n" +
 92					"} catch: MyExceptions using: { | exc | \n" +
 93					// the last test will result in an uncaught exception, but all previous ones should have been handled.
 94					"  if: testCount != 3 then: { fail() } \n" +
 95					"}", ctx_);
 96		} catch (InterpreterException e) {
 97			e.printStackTrace();
 98			fail("exception: "+ e);
 99		}
100		
101	}
102	
103	/**
104	 * The exceptions thrown by the interpreter can be intercepted by a program as they are also
105	 * typed objects, typed to identify their function. This test illustrates how to use this 
106	 * mechanism to build a python-like object model where non-existant field are silently added
107	 * to the object upon use.
108	 * 
109	 * Note that an altogether cleaner mechanism can be conceived by using the doesNotUnderstand 
110	 * meta hook to achieve similar behaviour.
111	 */
112	public void testInterpreterExceptionHandling() {
113		try {
114			evaluateInput(
115					"def defaultValue := 42;" +
116					"def pythonObjectMirror := \n" +
117					"  mirror: { \n" +
118					"    def invokeField(receiver, symbol) {" +
119					"      self.invoke(receiver, `(.#(symbol)()))" +
120					"    };" +
121					"    def invoke( receiver, invocation ) { \n" +
122					"      try: { \n" +
123					"        super^invoke( receiver, invocation ); \n" +
124					"      } catch: SelectorNotFound using: { | e | \n" +
125					"        super^defineField( invocation.selector, defaultValue ); \n" +
126					"        defaultValue; \n" +
127					"      } \n" +
128					"    } \n" +
129					"  }; \n" +
130					"def test := object: { nil } \n" +
131					"  mirroredBy: pythonObjectMirror; \n" +
132					"if: (test.x != defaultValue) then: { fail(); };",
133					ctx_);			
134		} catch (InterpreterException e) {
135			e.printStackTrace();
136			fail("exception: "+ e);
137		}
138	}
139	
140	/**
141	 * To avoid improper interference with the interpreter, user code should never throw  
142	 * interpreter exceptions. However, in the light that various components of the language 
143	 * may be reimplemented in the language itself, this functionality is supported by the 
144	 * interpreter anyhow.
145	 * 
146	 * Note that given the ability to catch interpreter exceptions, the programmer automatically
147	 * has the right to throw them as well, either by rethrowing them, or by storing it as a 
148	 * prototype, of which new clones can be instatiated whenever he feels like it.
149	 * 
150	 * This test consist of an object model where access to a field can be forbidden by throwing
151	 * an interpreter exception (tagged with SelectorNotFound)
152	 */
153	public void testInterpreterExceptionThrowing() {
154		try {
155			AmbientTalkTest.evalSnippet(ExceptionHandlingTest.class, "snippet1", ctx_);
156			// fail if no exception was thrown by the code. 
157			fail();
158		} catch (XSelectorNotFound e) {
159			// 1. Raising a Java Exception Successfull
160		} catch (InterpreterException e) {
161			e.printStackTrace();
162			fail("exception: "+ e);
163		}
164	}
165	
166	/**
167	 * When rethrowing an exception from a handler, the expected semantics apply : no handlers
168	 * from the same try block are tried, even if they also match the thrown exception. Handlers
169	 * from a try block higher up the stack can however apply.
170	 *
171	 */
172	public void testRethrownExceptions() {
173		try {
174			evaluateInput(
175					"deftype MyExceptions; \n" +
176					"deftype IncorrectArguments <: MyExceptions; \n" +
177					"\n" +
178					"def result := false;" +
179					"def test := object: { \n" +
180					"    def [ message, stackTrace ] := [ nil, nil ]; \n" +
181					"    def test := 0; \n" +
182					"} taggedAs: [ IncorrectArguments ]; \n" +
183					"\n" +
184					"try: { \n" +
185					"  try: { \n" +
186					"    raise: test; \n" +
187					"  } catch: MyExceptions using: { | exc | \n" +
188					"      result := true; \n" +
189					"      raise: exc; \n" +
190					"  } catch: IncorrectArguments using: { | exc |" +
191					"      fail();" +
192					"  }" +
193					"} catch: SelectorNotFound using: { | exc | \n" +
194					"  fail(); \n" +
195					"} catch: IncorrectArguments using: { | exc | \n" +
196					"  result := result.and: { true }; \n" +
197					"}; \n" +
198					"\n" +
199					"if: (! result) then: { fail(); }",					
200					ctx_);
201		} catch (InterpreterException e) {
202			e.printStackTrace();
203			fail("exception: "+ e);
204		}		
205	}
206	
207	/**
208	 * Tests whether finally: blocks are called when <ol>
209	 * <li>the try block terminates normally
210	 * <li>a handler was invoked
211	 * <li>no matching handler was found
212	 * <li>the escape construct was used
213	 * </ol>
214	 */
215	public void testFinallyConstruct() throws InterpreterException {
216		evaluateInput(
217				"{ | return | \n" +
218				"  try: { nil } \n" +
219				"  catch: SelectorNotFound using: { fail() } \n" +
220				"  finally: { return(true) }; \n" +
221				"  fail() \n" +
222				"}.escape();", ctx_);
223		evaluateInput(
224				"def result1 := { | return | \n" +
225				"  try: { self.result } \n" +
226				"  catch: SelectorNotFound using: { false } \n" +
227				"  finally: { return(true) }; \n" +
228				"}.escape(); \n" +
229				"result1.ifFalse: { fail() };", ctx_);
230		evaluateInput(
231				"deftype WrongType;" +
232				"def result2 := { | return | \n" +
233				"  try: { self.result } \n" +
234				"  catch: WrongType using: { fail() } \n" +
235				"  finally: { return(true) }; \n" +
236				"  false \n" +
237				"}.escape(); \n" +
238				"result2.ifFalse: { fail() };", ctx_);
239		evaluateInput(
240				"def result3 := { | return | \n" +
241				"  try: { self.result } \n" +
242				"  catch: SelectorNotFound using: { return(false) } \n" +
243				"  finally: { return(true) }; \n" +
244				"  false \n" +
245				"}.escape(); \n" +
246				"result3.ifFalse: { fail() };", ctx_);
247	}
248	
249	public static void main(String[] args) {
250		junit.swingui.TestRunner.run(ExceptionHandlingTest.class);
251	}
252		
253	// For testing purposes we need access to a set of native type tags
254	// These are introduced into the global lexical scope of the test 
255	private void setUpTestTypes(ATObject testScope) throws Exception {
256		
257		// Primitive type tags used to test all objects can be thrown
258		testScope.meta_defineField(
259				AGSymbol.jAlloc("Number"),
260				NativeTypeTags._NUMBER_);
261
262		testScope.meta_defineField(
263				AGSymbol.jAlloc("Table"),
264				NativeTypeTags._TABLE_);
265
266		testScope.meta_defineField(
267				AGSymbol.jAlloc("Closure"),
268				NativeTypeTags._CLOSURE_);
269
270		testScope.meta_defineField(
271				AGSymbol.jAlloc("TypeTag"),
272				NativeTypeTags._TYPETAG_);
273		
274		testScope.meta_defineField(
275				AGSymbol.jAlloc("SelectorNotFound"),
276				NativeTypeTags._SELECTORNOTFOUND_);
277	}
278	
279	public void setUp() throws Exception {
280		ATObject globalLexScope = Evaluator.getGlobalLexicalScope();
281		ATObject testScope = new NATCallframe(globalLexScope);
282				
283		setUpTestTypes(testScope);
284
285		// For throwing InterpreterExceptions, we provide a useful prototype to start from
286		testScope.meta_defineField(
287				AGSymbol.jAlloc("doesNotUnderstandX"),
288				JavaClass.wrapperFor(XSelectorNotFound.class));
289		
290		ctx_ = new NATContext(testScope, globalLexScope);
291
292		// Aux method to aid in shortening the test code
293		evaluateInput(
294				"def raise: exception catch: typetag do: closure { \n" +
295				"  try: {" +
296				"    raise: exception;" +
297				"  } catch: typetag using: closure; \n" +
298				"}",
299				ctx_);
300	}
301	
302	
303	
304
305}