PageRenderTime 53ms CodeModel.GetById 13ms app.highlight 34ms RepoModel.GetById 2ms app.codeStats 0ms

/interpreter/tags/at_build150307/test/edu/vub/at/objects/mirrors/MirageTest.java

http://ambienttalk.googlecode.com/
Java | 296 lines | 176 code | 44 blank | 76 comment | 0 complexity | e507bcc2e4c77390a73b6adbb7c779fa MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * MirageTest.java created on Aug 11, 2006 at 10:54:29 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.mirrors;
 29
 30import edu.vub.at.AmbientTalkTest;
 31import edu.vub.at.exceptions.InterpreterException;
 32import edu.vub.at.objects.ATObject;
 33import edu.vub.at.objects.ATTable;
 34import edu.vub.at.objects.coercion.NativeStripes;
 35import edu.vub.at.objects.natives.NATBoolean;
 36import edu.vub.at.objects.natives.NATNil;
 37import edu.vub.at.objects.natives.NATNumber;
 38import edu.vub.at.objects.natives.NATText;
 39import edu.vub.at.objects.natives.grammar.AGSymbol;
 40
 41/**
 42 * MirageTest tests the creation of Mirages (objects with custom meta-behaviour) given
 43 * a NATIntercessiveMirror instance.
 44 * 
 45 * @author smostinc
 46 */
 47public class MirageTest extends AmbientTalkTest {
 48
 49	public static void main(String[] args) {
 50		junit.swingui.TestRunner.run(MirageTest.class);
 51	}
 52	
 53	protected void setUp() throws Exception {
 54		super.setUp();
 55		
 56		evalAndReturn("def let: clo { clo(); };");
 57	}
 58	
 59	
 60	/**
 61	 * This test verifies invariants with respect to the creation of mirages in AmbientTalk.
 62	 * First of all, it tests the relationship between the original mirror and the mirage's
 63	 * mirror (they should be non-identical yet cloned from each other).
 64	 */
 65	public void testMirageCreation() throws InterpreterException {
 66		ATObject mirrorP = evalAndReturn(
 67				"def mirrorP := mirror: { \n" +
 68				"  def iAm := \"the original\"; \n" +
 69				"  def init(@args) { \n" +
 70				"    iAm := \"a clone\"; \n" +
 71				"    super^init(@args); \n" +
 72				"  } \n" +
 73				"} \n");
 74		ATObject baseP   = evalAndReturn(
 75				"def baseP   := mirrorP.base;");
 76		
 77		// when creating a mirror, its base field is initialised to an empty mirage
 78		assertEquals(baseP.getClass(), NATMirage.class);
 79		evalAndCompareTo( // default objects have three primitve methods
 80				"mirrorP.listMethods()",
 81				"[<primitive method:new>, <primitive method:init>, <primitive method:==>]");
 82		evalAndCompareTo( // default objects have one primitive field super
 83				"mirrorP.listFields()",
 84				"[<field:super>]");
 85				
 86		// when creating an ex nihilo object, the init is not called
 87		evalAndCompareTo(
 88				"mirrorP.iAm",
 89				"\"the original\"");
 90
 91		ATObject subject = evalAndReturn(
 92				"def subject := object: { \n" +
 93				"  def field := `field; \n" +
 94				"  def canonical() { nil }; \n" +
 95				"  def keyworded: arg1 message: arg2 { nil }; \n" +
 96				"} mirroredBy: mirrorP; \n");
 97		ATObject mirror  = evalAndReturn(
 98				"def mirror  := reflect: subject;");
 99
100		// mirror should be a clone of the mirrorP prototype
101		assertNotSame(mirrorP, mirror);
102		assertTrue(mirror.meta_isCloneOf(mirrorP).asNativeBoolean().javaValue);
103		
104		// test for identical methods by printing the table of methods (both return a new table)
105		assertEquals( 
106				mirrorP.meta_listMethods().toString(),
107				mirror.meta_listMethods().toString());
108		
109		// the init method should have been called, which has set the iAm field
110		evalAndCompareTo(
111				"mirror.iAm",
112				"\"a clone\"");
113	}
114	
115
116	/**
117	 * This test verifies invariants with respect to the cloning of custom mirrors in AmbientTalk.
118	 * First of all, it tests the relationship between the original mirror and the clone to ensure
119	 * they are proper clones of each other. Secondly, when cloning custom mirrors their base field
120	 * is also cloned.
121	 *  
122	 * @throws InterpreterException
123	 */	public void testCustomMirrorCloning() throws InterpreterException {
124		ATObject subject = evalAndReturn(
125				"def subject  := object: { \n" +
126				"  def field  := `field; \n" +
127				"  def canonical() { nil }; \n" +
128				"  def keyworded: arg1 message: arg2 { nil }; \n" +
129				"} mirroredBy: (mirror: { nil }); \n");
130		ATObject mirror  = evalAndReturn(
131				"def mirror   := reflect: subject;");
132		
133		// clone the mirror
134		ATObject mirrorC  = evalAndReturn(
135				"def mirrorC  := clone: mirror;");
136
137		// mirror should be a clone of the mirrorP prototype
138		assertNotSame(mirror, mirrorC);
139		assertTrue(mirrorC.meta_isCloneOf(mirror).asNativeBoolean().javaValue);
140		
141		// test for identical methods by printing the table of methods (both return a new table)
142		assertEquals( 
143				mirror.meta_listMethods().toString(),
144				mirrorC.meta_listMethods().toString());
145		
146		ATObject subjectC = evalAndReturn(
147				"def subjectC := mirrorC.base;");
148		
149		// mirror should be a clone of the mirrorP prototype
150		assertNotSame(subject, subjectC);
151		assertTrue(subjectC.meta_isCloneOf(subject).asNativeBoolean().javaValue);
152		
153		// test for identical methods by printing the table of methods (both return a new table)
154		assertEquals( 
155				subject.meta_listMethods().toString(),
156				subjectC.meta_listMethods().toString());
157
158	}
159	
160	/**
161	 * This test ensures that default extensions of mirrors share the base object of the parent.
162	 */
163	public void testChildMirrorSharesBase() throws InterpreterException {
164		ATObject result = evalAndReturn(
165				"def meta := mirror:  { nil }; \n" +
166				"def base := object:  { nil } mirroredBy: meta; \n" +
167				"    meta := reflect: base; \n" +
168				"def extension := extend: meta with: { nil }; \n" +
169				"(extension.base == meta.base);"); 
170		assertTrue(result.asNativeBoolean().javaValue);
171	}
172	
173	/**
174	 * This test ensures that the 'base' field of a mirror is not modified
175	 * when the mirror is used to create a new mirage.
176	 */
177	public void testMirrorBaseNotModifiedOnClone() {
178		ATObject result = evalAndReturn(
179				"def origMirror := mirror:  { nil };\n" +
180				"def origBase := origMirror.base;\n" +
181				"def newBase := object: { nil } mirroredBy: origMirror; \n" +
182				"(origMirror.base) == origBase\n"); 
183		assertEquals(NATBoolean._TRUE_, result);
184	}
185	
186	public void testMirageInvocation() throws InterpreterException {
187		class BufferedEcho extends NativeClosure {
188			
189			StringBuffer output;
190			
191			public BufferedEcho() {
192				super(NATNil._INSTANCE_);
193				initialize();
194			}
195			
196			public ATObject base_apply(ATTable arguments) throws InterpreterException {
197				output.append(arguments.base_at(NATNumber.ONE).asNativeText().javaValue + "\n");
198				return scope_;
199			}
200			
201			public void initialize() {
202				output = new StringBuffer();
203			}
204			
205			public String readOutput() {
206				return output.toString();
207			}
208		}
209		
210		BufferedEcho buffer = new BufferedEcho();
211		
212		ctx_.base_getLexicalScope().meta_defineField(AGSymbol.jAlloc("echo:"), buffer);
213
214		
215		// define a simple logging mirror which uses a shared lexical scope to logs method invocations with proper indentation
216		evalAndReturn(
217				"def loggingMirror := let: { | defaultSpacing := 2 | \n" +
218				"	def indentLevel := 0;" +
219				"	def spaces() {" +
220				"		def result := \"\";" +
221				"		indentLevel.doTimes: { | i |" +
222				"			defaultSpacing.doTimes: { | j |" +
223				"				result := result + \" \";" +
224				"			}" +
225				"		};" +
226				"		result;" +
227				"	};" +
228				"" +
229				"	mirror: {" +
230				"		def invoke(receiver, selector, args) {" +
231				"			echo: (spaces() + \"Invocation of method \" + selector + \" with arguments \" + args + \" on \" + receiver + \"(\" + super.base + \")\");" +
232				"			indentLevel := indentLevel + 1;" +
233				"			def result := super^invoke(receiver, selector, args);" +
234				"			indentLevel := indentLevel - 1;" +
235				"			echo: (spaces() + \"Invocation of method \" + selector + \" yielded \" + result );" +
236				"			result;" +
237				"		}" +
238				"	};" +
239				"};");
240		
241		// Test setup 1: parent with logging mirror
242		ATObject parent = evalAndReturn(
243				"def mirroredParent := object: {" +
244				"  def m() { self.n() };" +
245				"  def n() { \"ok\" };" +
246				"} mirroredBy: loggingMirror;");
247		
248		evalAndReturn("echo: mirroredParent.m();");
249		
250		assertEquals(
251				"Invocation of method m with arguments [] on " + parent + "(" + parent + ")\n" +
252				"  Invocation of method n with arguments [] on " + parent + "(" + parent + ")\n" +
253				"  Invocation of method n yielded ok\n" +
254				"Invocation of method m yielded ok\n" +
255				"ok\n",
256				buffer.readOutput());
257		
258		buffer.initialize();
259		
260		ATObject child = evalAndReturn(
261				"def unmirroredChild := object: {" +
262				"  super := mirroredParent; " + 
263				"  def m() { " +
264				"    echo: \"My parent will start logging now\";" +
265				"    super^m();" + 
266				"  };" + 
267				"};");
268		
269		evalAndReturn("echo: unmirroredChild.m();");
270		
271		assertEquals(
272				"My parent will start logging now\n" +
273				"Invocation of method m with arguments [] on " + child + "(" + parent + ")\n" +
274				"  Invocation of method n with arguments [] on " + child + "(" + parent + ")\n" +
275				"  Invocation of method n yielded ok\n" +
276				"Invocation of method m yielded ok\n" +
277				"ok\n",
278				buffer.readOutput());
279		
280		buffer.initialize();
281		
282//		child = evalAndReturn(
283//				"def mirroredChild := object: {\n" +
284//				"  super := mirroredParent;\n" +
285//				"  def n() { \n" +
286//				"    echo: \"    Indentation of this call should be correct as the lexical scope is shared by both mirrors\"; \n" +
287//				"    super^n(); \n" +
288//				"  }; \n" +
289//				"} mirroredBy: loggingMirror;");
290//		
291//		evalAndReturn("echo: mirroredChild.m();");
292//		
293//		assertEquals("", buffer.readOutput());
294	}
295	
296}