/interpreter/tags/at_build150307/test/edu/vub/at/objects/mirrors/MirageTest.java
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}