1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.databinding.tool.expr; 18 19 import android.databinding.Bindable; 20 import android.databinding.Observable; 21 import android.databinding.tool.LayoutBinder; 22 import android.databinding.tool.MockLayoutBinder; 23 import android.databinding.tool.reflection.ModelAnalyzer; 24 import android.databinding.tool.reflection.ModelClass; 25 import android.databinding.tool.reflection.java.JavaAnalyzer; 26 import android.databinding.tool.store.Location; 27 import android.databinding.tool.util.L; 28 import android.databinding.tool.writer.KCode; 29 30 import org.junit.Before; 31 import org.junit.Rule; 32 import org.junit.Test; 33 import org.junit.rules.TestWatcher; 34 import org.junit.runner.Description; 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.BitSet; 39 import java.util.Collections; 40 import java.util.List; 41 42 import static org.junit.Assert.assertEquals; 43 import static org.junit.Assert.assertFalse; 44 import static org.junit.Assert.assertNotNull; 45 import static org.junit.Assert.assertNotSame; 46 import static org.junit.Assert.assertNull; 47 import static org.junit.Assert.assertSame; 48 import static org.junit.Assert.assertTrue; 49 50 @SuppressWarnings("Duplicates") 51 public class ExprModelTest { 52 53 private static class DummyExpr extends Expr { 54 55 String mKey; 56 DummyExpr(String key, DummyExpr... children)57 public DummyExpr(String key, DummyExpr... children) { 58 super(children); 59 mKey = key; 60 } 61 62 @Override resolveType(ModelAnalyzer modelAnalyzer)63 protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 64 return modelAnalyzer.findClass(Integer.class); 65 } 66 67 @Override constructDependencies()68 protected List<Dependency> constructDependencies() { 69 return constructDynamicChildrenDependencies(); 70 } 71 72 @Override computeUniqueKey()73 protected String computeUniqueKey() { 74 return mKey + super.computeUniqueKey(); 75 } 76 77 @Override generateCode()78 protected KCode generateCode() { 79 return new KCode(); 80 } 81 82 @Override cloneToModel(ExprModel model)83 public Expr cloneToModel(ExprModel model) { 84 return this; 85 } 86 87 @Override getInvertibleError()88 protected String getInvertibleError() { 89 return "DummyExpr cannot be 2-way."; 90 } 91 } 92 93 ExprModel mExprModel; 94 95 @Rule 96 public TestWatcher mTestWatcher = new TestWatcher() { 97 @Override 98 protected void failed(Throwable e, Description description) { 99 if (mExprModel != null && mExprModel.getFlagMapping() != null) { 100 final String[] mapping = mExprModel.getFlagMapping(); 101 for (int i = 0; i < mapping.length; i++) { 102 L.d("flag %d: %s", i, mapping[i]); 103 } 104 } 105 } 106 }; 107 108 @Before setUp()109 public void setUp() throws Exception { 110 JavaAnalyzer.initForTests(); 111 mExprModel = new ExprModel(); 112 } 113 114 @Test testAddNormal()115 public void testAddNormal() { 116 final DummyExpr d = new DummyExpr("a"); 117 assertSame(d, mExprModel.register(d)); 118 assertSame(d, mExprModel.register(d)); 119 assertEquals(1, mExprModel.mExprMap.size()); 120 } 121 122 @Test testAddDupe1()123 public void testAddDupe1() { 124 final DummyExpr d = new DummyExpr("a"); 125 assertSame(d, mExprModel.register(d)); 126 assertSame(d, mExprModel.register(new DummyExpr("a"))); 127 assertEquals(1, mExprModel.mExprMap.size()); 128 } 129 130 @Test testAddMultiple()131 public void testAddMultiple() { 132 mExprModel.register(new DummyExpr("a")); 133 mExprModel.register(new DummyExpr("b")); 134 assertEquals(2, mExprModel.mExprMap.size()); 135 } 136 137 138 @Test testAddWithChildren()139 public void testAddWithChildren() { 140 DummyExpr a = new DummyExpr("a"); 141 DummyExpr b = new DummyExpr("b"); 142 DummyExpr c = new DummyExpr("c", a, b); 143 mExprModel.register(c); 144 DummyExpr a2 = new DummyExpr("a"); 145 DummyExpr b2 = new DummyExpr("b"); 146 DummyExpr c2 = new DummyExpr("c", a, b); 147 assertEquals(c, mExprModel.register(c2)); 148 } 149 150 @Test testShouldRead()151 public void testShouldRead() { 152 MockLayoutBinder lb = new MockLayoutBinder(); 153 mExprModel = lb.getModel(); 154 IdentifierExpr a = lb.addVariable("a", "java.lang.String", null); 155 IdentifierExpr b = lb.addVariable("b", "java.lang.String", null); 156 IdentifierExpr c = lb.addVariable("c", "java.lang.String", null); 157 lb.parse("a == null ? b : c", null, null); 158 mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class)); 159 lb.getModel().seal(); 160 List<Expr> shouldRead = getShouldRead(); 161 // a and a == null 162 assertEquals(2, shouldRead.size()); 163 final List<Expr> readFirst = getReadFirst(shouldRead, null); 164 assertEquals(1, readFirst.size()); 165 final Expr first = readFirst.get(0); 166 assertSame(a, first); 167 // now , assume we've read this 168 final BitSet shouldReadFlags = first.getShouldReadFlags(); 169 assertNotNull(shouldReadFlags); 170 } 171 172 @Test testReadConstantTernary()173 public void testReadConstantTernary() { 174 MockLayoutBinder lb = new MockLayoutBinder(); 175 mExprModel = lb.getModel(); 176 IdentifierExpr a = lb.addVariable("a", "java.lang.String", null); 177 IdentifierExpr b = lb.addVariable("b", "java.lang.String", null); 178 TernaryExpr ternaryExpr = parse(lb, "true ? a : b", TernaryExpr.class); 179 mExprModel.seal(); 180 List<Expr> shouldRead = getShouldRead(); 181 assertExactMatch(shouldRead, ternaryExpr.getPred()); 182 List<Expr> first = getReadFirst(shouldRead); 183 assertExactMatch(first, ternaryExpr.getPred()); 184 mExprModel.markBitsRead(); 185 shouldRead = getShouldRead(); 186 assertExactMatch(shouldRead, a, b, ternaryExpr); 187 first = getReadFirst(shouldRead); 188 assertExactMatch(first, a, b); 189 List<Expr> justRead = new ArrayList<Expr>(); 190 justRead.add(a); 191 justRead.add(b); 192 first = filterOut(getReadFirst(shouldRead, justRead), justRead); 193 assertExactMatch(first, ternaryExpr); 194 assertFalse(mExprModel.markBitsRead()); 195 } 196 197 @Test testTernaryWithPlus()198 public void testTernaryWithPlus() { 199 MockLayoutBinder lb = new MockLayoutBinder(); 200 mExprModel = lb.getModel(); 201 IdentifierExpr user = lb 202 .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User", 203 null); 204 MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class); 205 mExprModel.seal(); 206 List<Expr> toRead = getShouldRead(); 207 List<Expr> readNow = getReadFirst(toRead); 208 assertEquals(1, readNow.size()); 209 assertSame(user, readNow.get(0)); 210 List<Expr> justRead = new ArrayList<Expr>(); 211 justRead.add(user); 212 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 213 assertEquals(2, readNow.size()); //user.name && user.lastName 214 justRead.addAll(readNow); 215 // user.lastname (T, F), user.name + " " 216 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 217 assertEquals(2, readNow.size()); //user.name && user.lastName 218 justRead.addAll(readNow); 219 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 220 assertEquals(0, readNow.size()); 221 mExprModel.markBitsRead(); 222 223 toRead = getShouldRead(); 224 assertEquals(2, toRead.size()); 225 justRead.clear(); 226 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 227 assertEquals(1, readNow.size()); 228 assertSame(parsed.getRight(), readNow.get(0)); 229 justRead.addAll(readNow); 230 231 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 232 assertEquals(1, readNow.size()); 233 assertSame(parsed, readNow.get(0)); 234 justRead.addAll(readNow); 235 236 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 237 assertEquals(0, readNow.size()); 238 mExprModel.markBitsRead(); 239 assertEquals(0, getShouldRead().size()); 240 } 241 filterOut(List<Expr> itr, final List<Expr> exclude)242 private List<Expr> filterOut(List<Expr> itr, final List<Expr> exclude) { 243 List<Expr> result = new ArrayList<Expr>(); 244 for (Expr expr : itr) { 245 if (!exclude.contains(expr)) { 246 result.add(expr); 247 } 248 } 249 return result; 250 } 251 252 @Test testTernaryInsideTernary()253 public void testTernaryInsideTernary() { 254 MockLayoutBinder lb = new MockLayoutBinder(); 255 mExprModel = lb.getModel(); 256 IdentifierExpr cond1 = lb.addVariable("cond1", "boolean", null); 257 IdentifierExpr cond2 = lb.addVariable("cond2", "boolean", null); 258 259 IdentifierExpr a = lb.addVariable("a", "boolean", null); 260 IdentifierExpr b = lb.addVariable("b", "boolean", null); 261 IdentifierExpr c = lb.addVariable("c", "boolean", null); 262 263 final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class); 264 final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue(); 265 mExprModel.seal(); 266 267 List<Expr> toRead = getShouldRead(); 268 assertEquals(1, toRead.size()); 269 assertEquals(ternaryExpr.getPred(), toRead.get(0)); 270 271 List<Expr> readNow = getReadFirst(toRead); 272 assertEquals(1, readNow.size()); 273 assertEquals(ternaryExpr.getPred(), readNow.get(0)); 274 int cond1True = ternaryExpr.getRequirementFlagIndex(true); 275 int cond1False = ternaryExpr.getRequirementFlagIndex(false); 276 // ok, it is read now. 277 mExprModel.markBitsRead(); 278 279 // now it should read cond2 or c, depending on the flag from first 280 toRead = getShouldRead(); 281 assertEquals(2, toRead.size()); 282 assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred()); 283 assertFlags(ternaryExpr.getIfFalse(), cond1False); 284 assertFlags(ternaryExpr.getIfTrue(), cond1True); 285 286 mExprModel.markBitsRead(); 287 288 // now it should read a or b, innerTernary, outerTernary 289 toRead = getShouldRead(); 290 assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr, 291 innerTernary); 292 assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true)); 293 assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false)); 294 assertFalse(mExprModel.markBitsRead()); 295 } 296 297 @Test testRequirementFlags()298 public void testRequirementFlags() { 299 MockLayoutBinder lb = new MockLayoutBinder(); 300 mExprModel = lb.getModel(); 301 IdentifierExpr a = lb.addVariable("a", "java.lang.String", null); 302 IdentifierExpr b = lb.addVariable("b", "java.lang.String", null); 303 IdentifierExpr c = lb.addVariable("c", "java.lang.String", null); 304 IdentifierExpr d = lb.addVariable("d", "java.lang.String", null); 305 IdentifierExpr e = lb.addVariable("e", "java.lang.String", null); 306 final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", null, null); 307 assertTrue(aTernary instanceof TernaryExpr); 308 final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue(); 309 assertTrue(bTernary instanceof TernaryExpr); 310 final Expr aIsNull = mExprModel 311 .comparison("==", a, mExprModel.symbol("null", Object.class)); 312 final Expr bIsNull = mExprModel 313 .comparison("==", b, mExprModel.symbol("null", Object.class)); 314 lb.getModel().seal(); 315 List<Expr> shouldRead = getShouldRead(); 316 // a and a == null 317 assertEquals(2, shouldRead.size()); 318 assertFalse(a.getShouldReadFlags().isEmpty()); 319 assertTrue(a.getShouldReadFlags().get(a.getId())); 320 assertTrue(b.getShouldReadFlags().isEmpty()); 321 assertTrue(c.getShouldReadFlags().isEmpty()); 322 assertTrue(d.getShouldReadFlags().isEmpty()); 323 assertTrue(e.getShouldReadFlags().isEmpty()); 324 325 List<Expr> readFirst = getReadFirst(shouldRead, null); 326 assertEquals(1, readFirst.size()); 327 final Expr first = readFirst.get(0); 328 assertSame(a, first); 329 assertTrue(mExprModel.markBitsRead()); 330 for (Expr expr : mExprModel.getPendingExpressions()) { 331 assertNull(expr.mShouldReadFlags); 332 } 333 shouldRead = getShouldRead(); 334 assertExactMatch(shouldRead, e, b, bIsNull); 335 336 assertFlags(e, aTernary.getRequirementFlagIndex(false)); 337 338 assertFlags(b, aTernary.getRequirementFlagIndex(true)); 339 assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true)); 340 assertTrue(mExprModel.markBitsRead()); 341 shouldRead = getShouldRead(); 342 assertEquals(4, shouldRead.size()); 343 assertTrue(shouldRead.contains(c)); 344 assertTrue(shouldRead.contains(d)); 345 assertTrue(shouldRead.contains(aTernary)); 346 assertTrue(shouldRead.contains(bTernary)); 347 348 assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true))); 349 assertEquals(1, c.getShouldReadFlags().cardinality()); 350 351 assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false))); 352 assertEquals(1, d.getShouldReadFlags().cardinality()); 353 354 assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true))); 355 assertEquals(1, bTernary.getShouldReadFlags().cardinality()); 356 // +1 for invalidate all flag 357 assertEquals(6, aTernary.getShouldReadFlags().cardinality()); 358 for (Expr expr : new Expr[]{a, b, c, d, e}) { 359 assertTrue(aTernary.getShouldReadFlags().get(expr.getId())); 360 } 361 362 readFirst = getReadFirst(shouldRead); 363 assertEquals(2, readFirst.size()); 364 assertTrue(readFirst.contains(c)); 365 assertTrue(readFirst.contains(d)); 366 assertFalse(mExprModel.markBitsRead()); 367 } 368 369 @Test testPostConditionalDependencies()370 public void testPostConditionalDependencies() { 371 MockLayoutBinder lb = new MockLayoutBinder(); 372 mExprModel = lb.getModel(); 373 374 IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName(), null); 375 IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName(), null); 376 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(), null); 377 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(), null); 378 IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName(), null); 379 IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName(), null); 380 IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName(), null); 381 TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class); 382 TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`" 383 + " + u2.getCond(e) ", TernaryExpr.class); 384 Expr abCmp = abTernary.getPred(); 385 Expr bcCmp = bcTernary.getPred(); 386 Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred(); 387 final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse(); 388 Expr u2GetCondE = xxPlusU2getCondE.getRight(); 389 Expr u1Name = abTernary.getIfTrue(); 390 Expr u2Name = abTernary.getIfFalse(); 391 Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue(); 392 Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse(); 393 394 mExprModel.seal(); 395 List<Expr> shouldRead = getShouldRead(); 396 397 assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp); 398 399 List<Expr> firstRead = getReadFirst(shouldRead); 400 401 assertExactMatch(firstRead, a, b, c); 402 403 assertFlags(a, a, b, u1, u2, u1Name, u2Name); 404 assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e); 405 assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e); 406 assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name); 407 assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e); 408 409 assertTrue(mExprModel.markBitsRead()); 410 411 shouldRead = getShouldRead(); 412 Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary, 413 abTernary.getIfTrue(), abTernary.getIfFalse()}; 414 assertExactMatch(shouldRead, batch); 415 firstRead = getReadFirst(shouldRead); 416 assertExactMatch(firstRead, d, e, u1, u2); 417 418 assertFlags(d, bcTernary.getRequirementFlagIndex(true)); 419 assertFlags(e, bcTernary.getRequirementFlagIndex(false)); 420 assertFlags(u1, bcTernary.getRequirementFlagIndex(true), 421 abTernary.getRequirementFlagIndex(true)); 422 assertFlags(u2, bcTernary.getRequirementFlagIndex(false), 423 abTernary.getRequirementFlagIndex(false)); 424 425 assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true)); 426 assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false)); 427 assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false)); 428 assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name); 429 assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true)); 430 assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false)); 431 432 assertTrue(mExprModel.markBitsRead()); 433 434 shouldRead = getShouldRead(); 435 436 assertExactMatch(shouldRead, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary); 437 firstRead = getReadFirst(shouldRead); 438 assertExactMatch(firstRead, u1LastName, u2); 439 assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false)); 440 assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true)); 441 assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false)); 442 443 assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true)); 444 assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e); 445 446 assertFalse(mExprModel.markBitsRead()); 447 } 448 449 @Test testCircularDependency()450 public void testCircularDependency() { 451 MockLayoutBinder lb = new MockLayoutBinder(); 452 mExprModel = lb.getModel(); 453 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(), 454 null); 455 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(), 456 null); 457 final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class); 458 mExprModel.seal(); 459 List<Expr> shouldRead = getShouldRead(); 460 assertExactMatch(shouldRead, a, abTernary.getPred()); 461 assertTrue(mExprModel.markBitsRead()); 462 shouldRead = getShouldRead(); 463 assertExactMatch(shouldRead, b, abTernary); 464 assertFalse(mExprModel.markBitsRead()); 465 } 466 467 @Test testNestedCircularDependency()468 public void testNestedCircularDependency() { 469 MockLayoutBinder lb = new MockLayoutBinder(); 470 mExprModel = lb.getModel(); 471 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(), 472 null); 473 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(), 474 null); 475 IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName(), 476 null); 477 final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class); 478 final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue(); 479 mExprModel.seal(); 480 List<Expr> shouldRead = getShouldRead(); 481 assertExactMatch(shouldRead, a, a3Ternary.getPred()); 482 assertTrue(mExprModel.markBitsRead()); 483 shouldRead = getShouldRead(); 484 assertExactMatch(shouldRead, c, c4Ternary.getPred()); 485 assertFlags(c, a3Ternary.getRequirementFlagIndex(true), 486 a3Ternary.getRequirementFlagIndex(false)); 487 assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true)); 488 } 489 490 @Test testInterExprDependency()491 public void testInterExprDependency() { 492 MockLayoutBinder lb = new MockLayoutBinder(); 493 mExprModel = lb.getModel(); 494 IdentifierExpr u = lb.addVariable("u", User.class.getCanonicalName(), 495 null); 496 final Expr uComment = parse(lb, "u.comment", FieldAccessExpr.class); 497 final TernaryExpr uTernary = parse(lb, "u.getUseComment ? u.comment : `xx`", TernaryExpr.class); 498 mExprModel.seal(); 499 assertTrue(uTernary.getPred().canBeInvalidated()); 500 List<Expr> shouldRead = getShouldRead(); 501 assertExactMatch(shouldRead, u, uComment, uTernary.getPred()); 502 assertTrue(mExprModel.markBitsRead()); 503 shouldRead = getShouldRead(); 504 assertExactMatch(shouldRead, uComment, uTernary); 505 } 506 507 @Test testInterExprCircularDependency()508 public void testInterExprCircularDependency() { 509 MockLayoutBinder lb = new MockLayoutBinder(); 510 mExprModel = lb.getModel(); 511 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(), 512 null); 513 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(), 514 null); 515 final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class); 516 final TernaryExpr abTernary2 = parse(lb, "b > 3 ? b : a", TernaryExpr.class); 517 mExprModel.seal(); 518 List<Expr> shouldRead = getShouldRead(); 519 assertExactMatch(shouldRead, a, b, abTernary.getPred(), abTernary2.getPred()); 520 assertTrue(mExprModel.markBitsRead()); 521 shouldRead = getShouldRead(); 522 assertExactMatch(shouldRead, abTernary, abTernary2); 523 } 524 525 @Test testInterExprCircularDependency2()526 public void testInterExprCircularDependency2() { 527 MockLayoutBinder lb = new MockLayoutBinder(); 528 mExprModel = lb.getModel(); 529 IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), 530 null); 531 IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), 532 null); 533 final TernaryExpr abTernary = parse(lb, "a ? b : true", TernaryExpr.class); 534 final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class); 535 mExprModel.seal(); 536 List<Expr> shouldRead = getShouldRead(); 537 assertExactMatch(shouldRead, a, b); 538 assertFlags(a, a, b); 539 assertFlags(b, a, b); 540 List<Expr> readFirst = getReadFirst(shouldRead); 541 assertExactMatch(readFirst, a, b); 542 assertTrue(mExprModel.markBitsRead()); 543 shouldRead = getShouldRead(); 544 assertExactMatch(shouldRead, abTernary, baTernary); 545 readFirst = getReadFirst(shouldRead); 546 assertExactMatch(readFirst, abTernary, baTernary); 547 assertFalse(mExprModel.markBitsRead()); 548 shouldRead = getShouldRead(); 549 assertEquals(0, shouldRead.size()); 550 } 551 552 @Test testInterExprCircularDependency3()553 public void testInterExprCircularDependency3() { 554 MockLayoutBinder lb = new MockLayoutBinder(); 555 mExprModel = lb.getModel(); 556 IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), 557 null); 558 IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), 559 null); 560 IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), 561 null); 562 final TernaryExpr abTernary = parse(lb, "a ? b : c", TernaryExpr.class); 563 final TernaryExpr abTernary2 = parse(lb, "b ? a : c", TernaryExpr.class); 564 mExprModel.seal(); 565 List<Expr> shouldRead = getShouldRead(); 566 assertExactMatch(shouldRead, a, b); 567 assertTrue(mExprModel.markBitsRead()); 568 shouldRead = getShouldRead(); 569 // read a and b again, this time for their dependencies and also the rest since everything 570 // is ready to be read 571 assertExactMatch(shouldRead, c, abTernary, abTernary2); 572 mExprModel.markBitsRead(); 573 shouldRead = getShouldRead(); 574 assertEquals(0, shouldRead.size()); 575 } 576 577 @Test testInterExprCircularDependency4()578 public void testInterExprCircularDependency4() { 579 MockLayoutBinder lb = new MockLayoutBinder(); 580 mExprModel = lb.getModel(); 581 IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), 582 null); 583 IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), 584 null); 585 IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), 586 null); 587 IdentifierExpr d = lb.addVariable("d", boolean.class.getCanonicalName(), 588 null); 589 final TernaryExpr cTernary = parse(lb, "c ? (a ? d : false) : false", TernaryExpr.class); 590 final TernaryExpr abTernary = parse(lb, "a ? b : true", TernaryExpr.class); 591 final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class); 592 mExprModel.seal(); 593 List<Expr> shouldRead = getShouldRead(); 594 // check if a,b or c should be read. these are easily calculated from binding expressions' 595 // invalidation 596 assertExactMatch(shouldRead, c, a, b); 597 598 List<Expr> justRead = new ArrayList<Expr>(); 599 List<Expr> readFirst = getReadFirst(shouldRead); 600 assertExactMatch(readFirst, c, a, b); 601 Collections.addAll(justRead, a, b, c); 602 assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size()); 603 assertTrue(mExprModel.markBitsRead()); 604 605 shouldRead = getShouldRead(); 606 // if a and b are not invalid, a won't be read in the first step. But if c's expression 607 // is invalid and c == true, a must be read. Depending on a, d might be read as well. 608 // don't need to read b anymore because `a ? b : true` and `b ? a : false` has the same 609 // invalidation flags. 610 assertExactMatch(shouldRead, a, baTernary); 611 justRead.clear(); 612 613 readFirst = getReadFirst(shouldRead); 614 // first must read `a`. 615 assertExactMatch(readFirst, a); 616 Collections.addAll(justRead, a); 617 618 readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead); 619 assertExactMatch(readFirst, baTernary); 620 Collections.addAll(justRead, baTernary); 621 622 readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead); 623 assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size()); 624 assertTrue(mExprModel.markBitsRead()); 625 626 shouldRead = getShouldRead(); 627 // now we can read abTernary as well which had a conditional dependency on a but we did not 628 // elevate its dependency since we had other expressions elevated already 629 630 // now we can read adf ternary and c ternary 631 justRead.clear(); 632 assertExactMatch(shouldRead, abTernary, d, cTernary.getIfTrue(), cTernary); 633 readFirst = getReadFirst(shouldRead); 634 assertExactMatch(readFirst, d, abTernary); 635 Collections.addAll(justRead, d, abTernary); 636 readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead); 637 assertExactMatch(readFirst, cTernary.getIfTrue()); 638 Collections.addAll(justRead, cTernary.getIfTrue()); 639 640 readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead); 641 assertExactMatch(readFirst, cTernary); 642 Collections.addAll(justRead, cTernary); 643 644 assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size()); 645 646 assertFalse(mExprModel.markBitsRead()); 647 } 648 649 @Test testInterExprDeepDependency()650 public void testInterExprDeepDependency() { 651 MockLayoutBinder lb = new MockLayoutBinder(); 652 mExprModel = lb.getModel(); 653 IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null); 654 IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null); 655 IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), null); 656 final TernaryExpr t1 = parse(lb, "c ? (a ? b : true) : false", TernaryExpr.class); 657 final TernaryExpr t2 = parse(lb, "c ? (b ? a : false) : true", TernaryExpr.class); 658 final TernaryExpr abTernary = (TernaryExpr) t1.getIfTrue(); 659 final TernaryExpr baTernary = (TernaryExpr) t2.getIfTrue(); 660 mExprModel.seal(); 661 List<Expr> shouldRead = getShouldRead(); 662 assertExactMatch(shouldRead, c); 663 assertTrue(mExprModel.markBitsRead()); 664 shouldRead = getShouldRead(); 665 assertExactMatch(shouldRead, a, b); 666 assertTrue(mExprModel.markBitsRead()); 667 shouldRead = getShouldRead(); 668 assertExactMatch(shouldRead, a, b, t1.getIfTrue(), t2.getIfTrue(), t1, t2); 669 assertFlags(b, abTernary.getRequirementFlagIndex(true)); 670 assertFlags(a, baTernary.getRequirementFlagIndex(true)); 671 assertFalse(mExprModel.markBitsRead()); 672 } 673 674 @Test testInterExprDependencyNotReadyYet()675 public void testInterExprDependencyNotReadyYet() { 676 MockLayoutBinder lb = new MockLayoutBinder(); 677 mExprModel = lb.getModel(); 678 IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null); 679 IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null); 680 IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), null); 681 IdentifierExpr d = lb.addVariable("d", boolean.class.getCanonicalName(), null); 682 IdentifierExpr e = lb.addVariable("e", boolean.class.getCanonicalName(), null); 683 final TernaryExpr cTernary = parse(lb, "c ? (a ? d : false) : false", TernaryExpr.class); 684 final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class); 685 final TernaryExpr eaTernary = parse(lb, "e ? a : false", TernaryExpr.class); 686 mExprModel.seal(); 687 List<Expr> shouldRead = getShouldRead(); 688 assertExactMatch(shouldRead, b, c, e); 689 assertTrue(mExprModel.markBitsRead()); 690 shouldRead = getShouldRead(); 691 assertExactMatch(shouldRead, a, baTernary, eaTernary); 692 assertTrue(mExprModel.markBitsRead()); 693 shouldRead = getShouldRead(); 694 assertExactMatch(shouldRead, d, cTernary.getIfTrue(), cTernary); 695 assertFalse(mExprModel.markBitsRead()); 696 } 697 698 @Test testInvalidateConditional()699 public void testInvalidateConditional() { 700 MockLayoutBinder lb = new MockLayoutBinder(); 701 mExprModel = lb.getModel(); 702 final IdentifierExpr foo = lb.addVariable("foo", Foo.class.getCanonicalName(), null); 703 final IdentifierExpr c = lb.addVariable("c", "int", null); 704 final TernaryExpr ternary = parse(lb, "foo.a > 0 && (foo.b > 0 || c > 0)", 705 TernaryExpr.class); 706 final ComparisonExpr fooB0 = parse(lb, "foo.b > 0", ComparisonExpr.class); 707 final FieldAccessExpr fooB = (FieldAccessExpr) fooB0.getLeft(); 708 mExprModel.seal(); 709 final ComparisonExpr fooA0 = (ComparisonExpr) ternary.getPred(); 710 final FieldAccessExpr fooA = (FieldAccessExpr) fooA0.getLeft(); 711 712 // foo.b > 0 || c > 0 713 final TernaryExpr ternaryIfTrue = (TernaryExpr) ternary.getIfTrue(); 714 final ComparisonExpr c0 = (ComparisonExpr) ternaryIfTrue.getIfFalse(); 715 716 List<Expr> toRead = getShouldRead(); 717 assertExactMatch(toRead, foo, fooA, fooA0, fooB, fooB0); 718 List<Expr> justRead = new ArrayList<Expr>(); 719 List<Expr> readNow = getReadFirst(toRead, justRead); 720 assertExactMatch(readNow, foo); 721 justRead.addAll(readNow); 722 723 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 724 assertExactMatch(readNow, fooA, fooB); 725 justRead.addAll(readNow); 726 727 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 728 assertExactMatch(readNow, fooA0, fooB0); 729 justRead.addAll(readNow); 730 731 assertTrue(mExprModel.markBitsRead()); 732 justRead.clear(); 733 toRead = getShouldRead(); 734 735 // there is a second path to calculate fooB, fooB0 where fooB is not invalid but fooA or 736 // c is invalid. 737 assertExactMatch(toRead, fooB, fooB0); 738 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 739 assertExactMatch(readNow, fooB); 740 justRead.add(fooB); 741 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 742 assertExactMatch(readNow, fooB0); 743 744 assertTrue(mExprModel.markBitsRead()); 745 justRead.clear(); 746 toRead = getShouldRead(); 747 748 assertExactMatch(toRead, c, c0, ternary.getIfTrue(), ternary); 749 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 750 assertExactMatch(readNow, c); 751 justRead.addAll(readNow); 752 753 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 754 assertExactMatch(readNow, c0); 755 justRead.addAll(readNow); 756 757 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 758 assertExactMatch(readNow, ternary.getIfTrue()); 759 justRead.addAll(readNow); 760 761 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 762 assertExactMatch(readNow, ternary); 763 justRead.addAll(readNow); 764 765 assertFalse(mExprModel.markBitsRead()); 766 } 767 768 @Test testNoFlagsForNonBindingStatic()769 public void testNoFlagsForNonBindingStatic() { 770 MockLayoutBinder lb = new MockLayoutBinder(); 771 mExprModel = lb.getModel(); 772 lb.addVariable("a", int.class.getCanonicalName(), null); 773 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 774 mExprModel.seal(); 775 // +1 for invalidate all flag 776 assertEquals(1, parsed.getRight().getInvalidFlags().cardinality()); 777 // +1 for invalidate all flag 778 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 779 // +1 for invalidate all flag 780 assertEquals(2, mExprModel.getInvalidateableFieldLimit()); 781 } 782 783 @Test testFlagsForBindingStatic()784 public void testFlagsForBindingStatic() { 785 MockLayoutBinder lb = new MockLayoutBinder(); 786 mExprModel = lb.getModel(); 787 lb.addVariable("a", int.class.getCanonicalName(), null); 788 final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class); 789 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 790 mExprModel.seal(); 791 assertTrue(staticParsed.isBindingExpression()); 792 // +1 for invalidate all flag 793 assertEquals(1, staticParsed.getInvalidFlags().cardinality()); 794 assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags()); 795 // +1 for invalidate all flag 796 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 797 // +1 for invalidate all flag 798 assertEquals(2, mExprModel.getInvalidateableFieldLimit()); 799 } 800 801 @Test testFinalFieldOfAVariable()802 public void testFinalFieldOfAVariable() { 803 MockLayoutBinder lb = new MockLayoutBinder(); 804 mExprModel = lb.getModel(); 805 IdentifierExpr user = lb.addVariable("user", User.class.getCanonicalName(), 806 null); 807 Expr fieldGet = parse(lb, "user.finalField", FieldAccessExpr.class); 808 mExprModel.seal(); 809 assertTrue(fieldGet.isDynamic()); 810 // read user 811 assertExactMatch(getShouldRead(), user, fieldGet); 812 mExprModel.markBitsRead(); 813 // no need to read user.finalField 814 assertEquals(0, getShouldRead().size()); 815 } 816 817 @Test testFinalFieldOfAField()818 public void testFinalFieldOfAField() { 819 MockLayoutBinder lb = new MockLayoutBinder(); 820 mExprModel = lb.getModel(); 821 lb.addVariable("user", User.class.getCanonicalName(), null); 822 Expr finalFieldGet = parse(lb, "user.subObj.finalField", FieldAccessExpr.class); 823 mExprModel.seal(); 824 assertTrue(finalFieldGet.isDynamic()); 825 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 826 // read user 827 List<Expr> shouldRead = getShouldRead(); 828 assertEquals(3, shouldRead.size()); 829 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 830 finalFieldGet); 831 mExprModel.markBitsRead(); 832 // no need to read user.subObj.finalField because it is final 833 assertEquals(0, getShouldRead().size()); 834 } 835 836 @Test testFinalFieldOfAMethod()837 public void testFinalFieldOfAMethod() { 838 MockLayoutBinder lb = new MockLayoutBinder(); 839 mExprModel = lb.getModel(); 840 lb.addVariable("user", User.class.getCanonicalName(), null); 841 Expr finalFieldGet = parse(lb, "user.anotherSubObj.finalField", FieldAccessExpr.class); 842 mExprModel.seal(); 843 assertTrue(finalFieldGet.isDynamic()); 844 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 845 // read user 846 List<Expr> shouldRead = getShouldRead(); 847 assertEquals(3, shouldRead.size()); 848 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 849 finalFieldGet); 850 mExprModel.markBitsRead(); 851 // no need to read user.subObj.finalField because it is final 852 assertEquals(0, getShouldRead().size()); 853 } 854 855 @Test testFinalOfAClass()856 public void testFinalOfAClass() { 857 MockLayoutBinder lb = new MockLayoutBinder(); 858 mExprModel = lb.getModel(); 859 mExprModel.addImport("View", "android.view.View", null); 860 FieldAccessExpr fieldAccess = parse(lb, "View.VISIBLE", FieldAccessExpr.class); 861 assertFalse(fieldAccess.isDynamic()); 862 mExprModel.seal(); 863 assertEquals(0, getShouldRead().size()); 864 } 865 866 @Test testStaticFieldOfInstance()867 public void testStaticFieldOfInstance() { 868 MockLayoutBinder lb = new MockLayoutBinder(); 869 mExprModel = lb.getModel(); 870 lb.addVariable("myView", "android.view.View", null); 871 FieldAccessExpr fieldAccess = parse(lb, "myView.VISIBLE", FieldAccessExpr.class); 872 assertFalse(fieldAccess.isDynamic()); 873 mExprModel.seal(); 874 assertEquals(0, getShouldRead().size()); 875 final Expr child = fieldAccess.getTarget(); 876 assertTrue(child instanceof StaticIdentifierExpr); 877 StaticIdentifierExpr id = (StaticIdentifierExpr) child; 878 assertEquals(id.getResolvedType().getCanonicalName(), "android.view.View"); 879 // on demand import 880 assertEquals("android.view.View", mExprModel.getImports().get("View")); 881 } 882 883 @Test testOnDemandImportConflict()884 public void testOnDemandImportConflict() { 885 MockLayoutBinder lb = new MockLayoutBinder(); 886 mExprModel = lb.getModel(); 887 final IdentifierExpr myView = lb.addVariable("u", "android.view.View", 888 null); 889 mExprModel.addImport("View", User.class.getCanonicalName(), null); 890 final StaticIdentifierExpr id = mExprModel.staticIdentifierFor(myView.getResolvedType()); 891 mExprModel.seal(); 892 // on demand import with conflict 893 assertEquals("android.view.View", mExprModel.getImports().get("View1")); 894 assertEquals("View1", id.getName()); 895 assertEquals("android.view.View", id.getUserDefinedType()); 896 } 897 898 @Test testOnDemandImportAlreadyImported()899 public void testOnDemandImportAlreadyImported() { 900 MockLayoutBinder lb = new MockLayoutBinder(); 901 mExprModel = lb.getModel(); 902 final StaticIdentifierExpr ux = mExprModel.addImport("UX", User.class.getCanonicalName(), 903 null); 904 final IdentifierExpr u = lb.addVariable("u", User.class.getCanonicalName(), 905 null); 906 final StaticIdentifierExpr id = mExprModel.staticIdentifierFor(u.getResolvedType()); 907 mExprModel.seal(); 908 // on demand import with conflict 909 assertSame(ux, id); 910 } 911 912 @Test testStaticMethodOfInstance()913 public void testStaticMethodOfInstance() { 914 MockLayoutBinder lb = new MockLayoutBinder(); 915 mExprModel = lb.getModel(); 916 lb.addVariable("user", User.class.getCanonicalName(), null); 917 MethodCallExpr methodCall = parse(lb, "user.ourStaticMethod()", MethodCallExpr.class); 918 assertTrue(methodCall.isDynamic()); 919 mExprModel.seal(); 920 final Expr child = methodCall.getTarget(); 921 assertTrue(child instanceof StaticIdentifierExpr); 922 StaticIdentifierExpr id = (StaticIdentifierExpr) child; 923 assertEquals(id.getResolvedType().getCanonicalName(), User.class.getCanonicalName()); 924 } 925 926 @Test testVoid()927 public void testVoid() { 928 MockLayoutBinder lb = new MockLayoutBinder(); 929 mExprModel = lb.getModel(); 930 final LambdaExpr lambda = parse(lb, "(v) -> cond1 ? obj4.clicked(v) : void", LambdaExpr.class); 931 assertNotSame(mExprModel, lambda.getCallbackExprModel()); 932 } 933 934 @Test testVoid2()935 public void testVoid2() { 936 MockLayoutBinder lb = new MockLayoutBinder(); 937 mExprModel = lb.getModel(); 938 final LambdaExpr lambda = parse(lb, "(v) -> cond1 ? obj4.clicked(v) : Void", LambdaExpr.class); 939 assertNotSame(mExprModel, lambda.getCallbackExprModel()); 940 } 941 942 @Test testShruggy()943 public void testShruggy() { 944 MockLayoutBinder lb = new MockLayoutBinder(); 945 mExprModel = lb.getModel(); 946 final LambdaExpr lambda = parse(lb, "(v) -> cond1 ? obj4.clicked(v) : ¯\\_(ツ)_/¯", LambdaExpr.class); 947 assertNotSame(mExprModel, lambda.getCallbackExprModel()); 948 } 949 950 @Test testParseNoArgLambda()951 public void testParseNoArgLambda() { 952 MockLayoutBinder lb = new MockLayoutBinder(); 953 mExprModel = lb.getModel(); 954 final LambdaExpr lambda = parse(lb, "() -> user.name", LambdaExpr.class); 955 assertNotSame(mExprModel, lambda.getCallbackExprModel()); 956 } 957 958 @Test testParseLambdaWithSingleArg()959 public void testParseLambdaWithSingleArg() { 960 MockLayoutBinder lb = new MockLayoutBinder(); 961 mExprModel = lb.getModel(); 962 final LambdaExpr lambda = parse(lb, "a -> user.name", LambdaExpr.class); 963 assertNotSame(mExprModel, lambda.getCallbackExprModel()); 964 assertTrue("should have user", hasIdentifier(mExprModel, "user")); 965 assertTrue("should have user", hasIdentifier(lambda.getCallbackExprModel(), "user")); 966 assertSame("should have the same user", getIdentifier(mExprModel, "user"), 967 getIdentifier(lambda.getCallbackExprModel(), "user")); 968 assertTrue("should have a", hasCallbackIdentifier(lambda.getCallbackExprModel(), 0, "a")); 969 assertFalse("should not have a", hasCallbackIdentifier(mExprModel, 0, "a")); 970 } 971 972 @Test testParseLambdaWithArguments()973 public void testParseLambdaWithArguments() { 974 MockLayoutBinder lb = new MockLayoutBinder(); 975 mExprModel = lb.getModel(); 976 final LambdaExpr lambda = parse(lb, "(a, b, c, d) -> user.name", LambdaExpr.class); 977 final CallbackExprModel callbackModel = lambda.getCallbackExprModel(); 978 assertNotSame(mExprModel, callbackModel); 979 assertTrue("should have user", hasIdentifier(mExprModel, "user")); 980 int index = 0; 981 for (String s : new String[]{"a", "b", "c", "d"}) { 982 assertTrue("should have " + s, hasCallbackIdentifier(callbackModel, index, s)); 983 assertFalse("should not have " + s, hasIdentifier(mExprModel, s)); 984 assertFalse("should not have " + s, hasCallbackIdentifier(mExprModel, index, s)); 985 index ++; 986 } 987 } 988 hasIdentifier(ExprModel model, String name)989 private boolean hasIdentifier(ExprModel model, String name) { 990 return getIdentifier(model, name) != null; 991 } 992 getIdentifier(ExprModel model, String name)993 private Expr getIdentifier(ExprModel model, String name) { 994 return model.getExprMap().get(new IdentifierExpr(name).getUniqueKey()); 995 } 996 hasCallbackIdentifier(ExprModel model, int index, String name)997 private boolean hasCallbackIdentifier(ExprModel model, int index, String name) { 998 return getCallbackIdentifier(model, index, name) != null; 999 } 1000 getCallbackIdentifier(ExprModel model, int index, String name)1001 private Expr getCallbackIdentifier(ExprModel model, int index, String name) { 1002 return model.getExprMap().get(new CallbackArgExpr(index, name).getUniqueKey()); 1003 } 1004 1005 1006 @Test testFinalOfStaticField()1007 public void testFinalOfStaticField() { 1008 MockLayoutBinder lb = new MockLayoutBinder(); 1009 mExprModel = lb.getModel(); 1010 mExprModel.addImport("UX", User.class.getCanonicalName(), null); 1011 FieldAccessExpr fieldAccess = parse(lb, "UX.innerStaticInstance.finalStaticField", 1012 FieldAccessExpr.class); 1013 assertFalse(fieldAccess.isDynamic()); 1014 mExprModel.seal(); 1015 // nothing to read since it is all final and static 1016 assertEquals(0, getShouldRead().size()); 1017 } 1018 1019 @Test testFinalOfFinalStaticField()1020 public void testFinalOfFinalStaticField() { 1021 MockLayoutBinder lb = new MockLayoutBinder(); 1022 mExprModel = lb.getModel(); 1023 mExprModel.addImport("User", User.class.getCanonicalName(), null); 1024 FieldAccessExpr fieldAccess = parse(lb, "User.innerFinalStaticInstance.finalStaticField", 1025 FieldAccessExpr.class); 1026 assertFalse(fieldAccess.isDynamic()); 1027 mExprModel.seal(); 1028 assertEquals(0, getShouldRead().size()); 1029 } 1030 1031 @Test testLocationTracking()1032 public void testLocationTracking() { 1033 MockLayoutBinder lb = new MockLayoutBinder(); 1034 mExprModel = lb.getModel(); 1035 final String input = "a > 3 ? b : c"; 1036 TernaryExpr ternaryExpr = parse(lb, input, TernaryExpr.class); 1037 final Location location = ternaryExpr.getLocations().get(0); 1038 assertNotNull(location); 1039 assertEquals(0, location.startLine); 1040 assertEquals(0, location.startOffset); 1041 assertEquals(0, location.endLine); 1042 assertEquals(input.length() - 1, location.endOffset); 1043 1044 final ComparisonExpr comparison = (ComparisonExpr) ternaryExpr.getPred(); 1045 final Location predLoc = comparison.getLocations().get(0); 1046 assertNotNull(predLoc); 1047 assertEquals(0, predLoc.startLine); 1048 assertEquals(0, predLoc.startOffset); 1049 assertEquals(0, predLoc.endLine); 1050 assertEquals(4, predLoc.endOffset); 1051 1052 final Location aLoc = comparison.getLeft().getLocations().get(0); 1053 assertNotNull(aLoc); 1054 assertEquals(0, aLoc.startLine); 1055 assertEquals(0, aLoc.startOffset); 1056 assertEquals(0, aLoc.endLine); 1057 assertEquals(0, aLoc.endOffset); 1058 1059 final Location tLoc = comparison.getRight().getLocations().get(0); 1060 assertNotNull(tLoc); 1061 assertEquals(0, tLoc.startLine); 1062 assertEquals(4, tLoc.startOffset); 1063 assertEquals(0, tLoc.endLine); 1064 assertEquals(4, tLoc.endOffset); 1065 1066 final Location bLoc = ternaryExpr.getIfTrue().getLocations().get(0); 1067 assertNotNull(bLoc); 1068 assertEquals(0, bLoc.startLine); 1069 assertEquals(8, bLoc.startOffset); 1070 assertEquals(0, bLoc.endLine); 1071 assertEquals(8, bLoc.endOffset); 1072 1073 final Location cLoc = ternaryExpr.getIfFalse().getLocations().get(0); 1074 assertNotNull(cLoc); 1075 assertEquals(0, cLoc.startLine); 1076 assertEquals(12, cLoc.startOffset); 1077 assertEquals(0, cLoc.endLine); 1078 assertEquals(12, cLoc.endOffset); 1079 } 1080 1081 // TODO uncomment when we have inner static access 1082 // @Test 1083 // public void testFinalOfInnerStaticClass() { 1084 // MockLayoutBinder lb = new MockLayoutBinder(); 1085 // mExprModel = lb.getModel(); 1086 // mExprModel.addImport("User", User.class.getCanonicalName()); 1087 // FieldAccessExpr fieldAccess = parse(lb, "User.InnerStaticClass.finalStaticField", FieldAccessExpr.class); 1088 // assertFalse(fieldAccess.isDynamic()); 1089 // mExprModel.seal(); 1090 // assertEquals(0, getShouldRead().size()); 1091 // } 1092 assertFlags(Expr a, int... flags)1093 private void assertFlags(Expr a, int... flags) { 1094 BitSet bitset = new BitSet(); 1095 for (int flag : flags) { 1096 bitset.set(flag); 1097 } 1098 assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags()); 1099 } 1100 assertFlags(Expr a, Expr... exprs)1101 private void assertFlags(Expr a, Expr... exprs) { 1102 BitSet bitSet = a.getShouldReadFlags(); 1103 for (Expr expr : exprs) { 1104 BitSet clone = (BitSet) bitSet.clone(); 1105 clone.and(expr.getInvalidFlags()); 1106 assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr 1107 .getUniqueKey(), expr.getInvalidFlags(), clone); 1108 } 1109 1110 BitSet composite = new BitSet(); 1111 for (Expr expr : exprs) { 1112 composite.or(expr.getInvalidFlags()); 1113 } 1114 assertEquals("composite flags should match", composite, bitSet); 1115 } 1116 assertExactMatch(List<Expr> iterable, Expr... exprs)1117 private void assertExactMatch(List<Expr> iterable, Expr... exprs) { 1118 int i = 0; 1119 String listLog = Arrays.toString(iterable.toArray()); 1120 String itemsLog = Arrays.toString(exprs); 1121 String log = "list: " + listLog + "\nitems: " + itemsLog; 1122 log("list", iterable); 1123 for (Expr expr : exprs) { 1124 assertTrue((i++) + ":must contain " + expr.getUniqueKey() + "\n" + log, 1125 iterable.contains(expr)); 1126 } 1127 i = 0; 1128 for (Expr expr : iterable) { 1129 assertTrue((i++) + ":must be expected " + expr.getUniqueKey() + "\n" + log, 1130 Arrays.asList(exprs).contains(expr)); 1131 } 1132 } 1133 parse(LayoutBinder binder, String input, Class<T> klass)1134 private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) { 1135 final Expr parsed = binder.parse(input, null, null); 1136 assertTrue(klass.isAssignableFrom(parsed.getClass())); 1137 return (T) parsed; 1138 } 1139 log(String s, List<Expr> iterable)1140 private void log(String s, List<Expr> iterable) { 1141 L.d(s); 1142 for (Expr e : iterable) { 1143 L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(), 1144 e.getShouldReadFlagsWithConditionals(), e.getReadSoFar()); 1145 } 1146 L.d("end of %s", s); 1147 } 1148 getReadFirst(List<Expr> shouldRead)1149 private List<Expr> getReadFirst(List<Expr> shouldRead) { 1150 return getReadFirst(shouldRead, null); 1151 } 1152 getReadFirst(List<Expr> shouldRead, final List<Expr> justRead)1153 private List<Expr> getReadFirst(List<Expr> shouldRead, final List<Expr> justRead) { 1154 List<Expr> result = new ArrayList<Expr>(); 1155 for (Expr expr : shouldRead) { 1156 if (expr.shouldReadNow(justRead)) { 1157 result.add(expr); 1158 } 1159 } 1160 return result; 1161 } 1162 getShouldRead()1163 private List<Expr> getShouldRead() { 1164 return ExprModel.filterShouldRead(mExprModel.getPendingExpressions()); 1165 } 1166 1167 public static class Foo { 1168 public final int a = 1; 1169 public final int b = 1; 1170 } 1171 1172 public static class User implements Observable { 1173 1174 String name; 1175 1176 String lastName; 1177 1178 public final int finalField = 5; 1179 1180 public static InnerStaticClass innerStaticInstance = new InnerStaticClass(); 1181 1182 public static final InnerStaticClass innerFinalStaticInstance = new InnerStaticClass(); 1183 1184 public SubObj subObj = new SubObj(); 1185 getName()1186 public String getName() { 1187 return name; 1188 } 1189 getLastName()1190 public String getLastName() { 1191 return lastName; 1192 } 1193 getCond(int i)1194 public boolean getCond(int i) { 1195 return true; 1196 } 1197 getAnotherSubObj()1198 public SubObj getAnotherSubObj() { 1199 return new SubObj(); 1200 } 1201 ourStaticMethod()1202 public static boolean ourStaticMethod() { 1203 return true; 1204 } 1205 1206 public String comment; 1207 1208 @Bindable getUseComment()1209 public boolean getUseComment() { 1210 return true; 1211 } 1212 1213 @Override addOnPropertyChangedCallback(OnPropertyChangedCallback callback)1214 public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) { 1215 1216 } 1217 1218 @Override removeOnPropertyChangedCallback(OnPropertyChangedCallback callback)1219 public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) { 1220 1221 } 1222 1223 public static class InnerStaticClass { 1224 1225 public static final int finalField = 3; 1226 1227 public static final int finalStaticField = 3; 1228 } 1229 } 1230 1231 public static class SubObj { 1232 1233 public final int finalField = 5; 1234 } 1235 1236 } 1237