ExprModelTest.java revision c96847768305d83c6bc4919432af9bd9bfe4c08e
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 17package android.databinding.tool.expr; 18 19import com.google.common.base.Predicate; 20import com.google.common.collect.Iterables; 21 22import org.apache.commons.lang3.ArrayUtils; 23import org.apache.commons.lang3.NotImplementedException; 24import org.junit.Before; 25import org.junit.Rule; 26import org.junit.Test; 27import org.junit.rules.TestWatcher; 28import org.junit.runner.Description; 29 30import android.databinding.BaseObservable; 31import android.databinding.tool.LayoutBinder; 32import android.databinding.tool.MockLayoutBinder; 33import android.databinding.tool.reflection.ModelAnalyzer; 34import android.databinding.tool.reflection.ModelClass; 35import android.databinding.tool.reflection.java.JavaAnalyzer; 36import android.databinding.tool.util.L; 37 38import java.lang.reflect.Field; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.BitSet; 42import java.util.List; 43 44import static org.junit.Assert.assertEquals; 45import static org.junit.Assert.assertFalse; 46import static org.junit.Assert.assertNotNull; 47import static org.junit.Assert.assertNull; 48import static org.junit.Assert.assertSame; 49import static org.junit.Assert.assertTrue; 50 51public class ExprModelTest { 52 53 private static class DummyExpr extends Expr { 54 55 String mKey; 56 57 public DummyExpr(String key, DummyExpr... children) { 58 super(children); 59 mKey = key; 60 } 61 62 @Override 63 protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 64 return modelAnalyzer.findClass(Integer.class); 65 } 66 67 @Override 68 protected List<Dependency> constructDependencies() { 69 return constructDynamicChildrenDependencies(); 70 } 71 72 @Override 73 protected String computeUniqueKey() { 74 return mKey + super.computeUniqueKey(); 75 } 76 } 77 78 ExprModel mExprModel; 79 80 @Rule 81 public TestWatcher mTestWatcher = new TestWatcher() { 82 @Override 83 protected void failed(Throwable e, Description description) { 84 if (mExprModel != null && mExprModel.getFlagMapping() != null) { 85 final String[] mapping = mExprModel.getFlagMapping(); 86 for (int i = 0; i < mapping.length; i++) { 87 L.d("flag %d: %s", i, mapping[i]); 88 } 89 } 90 } 91 }; 92 93 @Before 94 public void setUp() throws Exception { 95 JavaAnalyzer.initForTests(); 96 mExprModel = new ExprModel(); 97 } 98 99 @Test 100 public void testAddNormal() { 101 final DummyExpr d = new DummyExpr("a"); 102 assertSame(d, mExprModel.register(d)); 103 assertSame(d, mExprModel.register(d)); 104 assertEquals(1, mExprModel.mExprMap.size()); 105 } 106 107 @Test 108 public void testAddDupe1() { 109 final DummyExpr d = new DummyExpr("a"); 110 assertSame(d, mExprModel.register(d)); 111 assertSame(d, mExprModel.register(new DummyExpr("a"))); 112 assertEquals(1, mExprModel.mExprMap.size()); 113 } 114 115 @Test 116 public void testAddMultiple() { 117 mExprModel.register(new DummyExpr("a")); 118 mExprModel.register(new DummyExpr("b")); 119 assertEquals(2, mExprModel.mExprMap.size()); 120 } 121 122 123 @Test 124 public void testAddWithChildren() { 125 DummyExpr a = new DummyExpr("a"); 126 DummyExpr b = new DummyExpr("b"); 127 DummyExpr c = new DummyExpr("c", a, b); 128 mExprModel.register(c); 129 DummyExpr a2 = new DummyExpr("a"); 130 DummyExpr b2 = new DummyExpr("b"); 131 DummyExpr c2 = new DummyExpr("c", a, b); 132 assertEquals(c, mExprModel.register(c2)); 133 } 134 135 @Test 136 public void testShouldRead() { 137 LayoutBinder lb = new MockLayoutBinder(); 138 mExprModel = lb.getModel(); 139 IdentifierExpr a = lb.addVariable("a", "java.lang.String"); 140 IdentifierExpr b = lb.addVariable("b", "java.lang.String"); 141 IdentifierExpr c = lb.addVariable("c", "java.lang.String"); 142 lb.parse("a == null ? b : c"); 143 mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class)); 144 lb.getModel().seal(); 145 Iterable<Expr> shouldRead = getShouldRead(); 146 // a and a == null 147 assertEquals(2, Iterables.size(shouldRead)); 148 final Iterable<Expr> readFirst = getReadFirst(shouldRead, null); 149 assertEquals(1, Iterables.size(readFirst)); 150 final Expr first = Iterables.getFirst(readFirst, null); 151 assertSame(a, first); 152 // now , assume we've read this 153 final BitSet shouldReadFlags = first.getShouldReadFlags(); 154 assertNotNull(shouldReadFlags); 155 } 156 157 @Test 158 public void testTernaryWithPlus() { 159 LayoutBinder lb = new MockLayoutBinder(); 160 mExprModel = lb.getModel(); 161 IdentifierExpr user = lb 162 .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User"); 163 MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class); 164 mExprModel.seal(); 165 Iterable<Expr> toRead = getShouldRead(); 166 Iterable<Expr> readNow = getReadFirst(toRead); 167 assertEquals(1, Iterables.size(readNow)); 168 assertSame(user, Iterables.getFirst(readNow, null)); 169 List<Expr> justRead = new ArrayList<Expr>(); 170 justRead.add(user); 171 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 172 assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName 173 Iterables.addAll(justRead, readNow); 174 // user.lastname (T, F), user.name + " " 175 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 176 assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName 177 Iterables.addAll(justRead, readNow); 178 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 179 assertEquals(0, Iterables.size(readNow)); 180 mExprModel.markBitsRead(); 181 182 toRead = getShouldRead(); 183 assertEquals(2, Iterables.size(toRead)); 184 justRead.clear(); 185 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 186 assertEquals(1, Iterables.size(readNow)); 187 assertSame(parsed.getRight(), Iterables.getFirst(readNow, null)); 188 Iterables.addAll(justRead, readNow); 189 190 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 191 assertEquals(1, Iterables.size(readNow)); 192 assertSame(parsed, Iterables.getFirst(readNow, null)); 193 Iterables.addAll(justRead, readNow); 194 195 readNow = filterOut(getReadFirst(toRead, justRead), justRead); 196 assertEquals(0, Iterables.size(readNow)); 197 mExprModel.markBitsRead(); 198 assertEquals(0, Iterables.size(getShouldRead())); 199 } 200 201 private List<Expr> filterOut(Iterable itr, final Iterable exclude) { 202 return Arrays.asList(Iterables.toArray(Iterables.filter(itr, new Predicate() { 203 @Override 204 public boolean apply(Object input) { 205 return !Iterables.contains(exclude, input); 206 } 207 }), Expr.class)); 208 } 209 210 @Test 211 public void testTernaryInsideTernary() { 212 LayoutBinder lb = new MockLayoutBinder(); 213 mExprModel = lb.getModel(); 214 IdentifierExpr cond1 = lb.addVariable("cond1", "boolean"); 215 IdentifierExpr cond2 = lb.addVariable("cond2", "boolean"); 216 217 IdentifierExpr a = lb.addVariable("a", "boolean"); 218 IdentifierExpr b = lb.addVariable("b", "boolean"); 219 IdentifierExpr c = lb.addVariable("c", "boolean"); 220 221 final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class); 222 final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue(); 223 mExprModel.seal(); 224 225 Iterable<Expr> toRead = getShouldRead(); 226 assertEquals(1, Iterables.size(toRead)); 227 assertEquals(ternaryExpr.getPred(), Iterables.getFirst(toRead, null)); 228 229 Iterable<Expr> readNow = getReadFirst(toRead); 230 assertEquals(1, Iterables.size(readNow)); 231 assertEquals(ternaryExpr.getPred(), Iterables.getFirst(readNow, null)); 232 int cond1True = ternaryExpr.getRequirementFlagIndex(true); 233 int cond1False = ternaryExpr.getRequirementFlagIndex(false); 234 // ok, it is read now. 235 mExprModel.markBitsRead(); 236 237 // now it should read cond2 or c, depending on the flag from first 238 toRead = getShouldRead(); 239 assertEquals(2, Iterables.size(toRead)); 240 assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred()); 241 assertFlags(ternaryExpr.getIfFalse(), cond1False); 242 assertFlags(ternaryExpr.getIfTrue(), cond1True); 243 244 mExprModel.markBitsRead(); 245 246 // now it should read a or b, innerTernary, outerTernary 247 toRead = getShouldRead(); 248 assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr, 249 innerTernary); 250 assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true)); 251 assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false)); 252 assertFalse(mExprModel.markBitsRead()); 253 } 254 255 @Test 256 public void testRequirementFlags() { 257 LayoutBinder lb = new MockLayoutBinder(); 258 mExprModel = lb.getModel(); 259 IdentifierExpr a = lb.addVariable("a", "java.lang.String"); 260 IdentifierExpr b = lb.addVariable("b", "java.lang.String"); 261 IdentifierExpr c = lb.addVariable("c", "java.lang.String"); 262 IdentifierExpr d = lb.addVariable("d", "java.lang.String"); 263 IdentifierExpr e = lb.addVariable("e", "java.lang.String"); 264 final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e"); 265 assertTrue(aTernary instanceof TernaryExpr); 266 final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue(); 267 assertTrue(bTernary instanceof TernaryExpr); 268 final Expr aIsNull = mExprModel 269 .comparison("==", a, mExprModel.symbol("null", Object.class)); 270 final Expr bIsNull = mExprModel 271 .comparison("==", b, mExprModel.symbol("null", Object.class)); 272 lb.getModel().seal(); 273 Iterable<Expr> shouldRead = getShouldRead(); 274 // a and a == null 275 assertEquals(2, Iterables.size(shouldRead)); 276 assertFalse(a.getShouldReadFlags().isEmpty()); 277 assertTrue(a.getShouldReadFlags().get(a.getId())); 278 assertTrue(b.getShouldReadFlags().isEmpty()); 279 assertTrue(c.getShouldReadFlags().isEmpty()); 280 assertTrue(d.getShouldReadFlags().isEmpty()); 281 assertTrue(e.getShouldReadFlags().isEmpty()); 282 283 Iterable<Expr> readFirst = getReadFirst(shouldRead, null); 284 assertEquals(1, Iterables.size(readFirst)); 285 final Expr first = Iterables.getFirst(readFirst, null); 286 assertSame(a, first); 287 assertTrue(mExprModel.markBitsRead()); 288 for (Expr expr : mExprModel.getPendingExpressions()) { 289 assertNull(expr.mShouldReadFlags); 290 } 291 shouldRead = getShouldRead(); 292 assertExactMatch(shouldRead, e, b, bIsNull); 293 294 assertFlags(e, aTernary.getRequirementFlagIndex(false)); 295 296 assertFlags(b, aTernary.getRequirementFlagIndex(true)); 297 assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true)); 298 assertTrue(mExprModel.markBitsRead()); 299 shouldRead = getShouldRead(); 300 assertEquals(4, Iterables.size(shouldRead)); 301 assertTrue(Iterables.contains(shouldRead, c)); 302 assertTrue(Iterables.contains(shouldRead, d)); 303 assertTrue(Iterables.contains(shouldRead, aTernary)); 304 assertTrue(Iterables.contains(shouldRead, bTernary)); 305 306 assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true))); 307 assertEquals(1, c.getShouldReadFlags().cardinality()); 308 309 assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false))); 310 assertEquals(1, d.getShouldReadFlags().cardinality()); 311 312 assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true))); 313 assertEquals(1, bTernary.getShouldReadFlags().cardinality()); 314 // +1 for invalidate all flag 315 assertEquals(6, aTernary.getShouldReadFlags().cardinality()); 316 for (Expr expr : new Expr[]{a, b, c, d, e}) { 317 assertTrue(aTernary.getShouldReadFlags().get(expr.getId())); 318 } 319 320 readFirst = getReadFirst(shouldRead); 321 assertEquals(2, Iterables.size(readFirst)); 322 assertTrue(Iterables.contains(readFirst, c)); 323 assertTrue(Iterables.contains(readFirst, d)); 324 assertFalse(mExprModel.markBitsRead()); 325 } 326 327 @Test 328 public void testPostConditionalDependencies() { 329 LayoutBinder lb = new MockLayoutBinder(); 330 mExprModel = lb.getModel(); 331 332 IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName()); 333 IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName()); 334 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); 335 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); 336 IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName()); 337 IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName()); 338 IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName()); 339 TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class); 340 TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`" 341 + " + u2.getCond(e) ", TernaryExpr.class); 342 Expr abCmp = abTernary.getPred(); 343 Expr bcCmp = bcTernary.getPred(); 344 Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred(); 345 final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse(); 346 Expr u2GetCondE = xxPlusU2getCondE.getRight(); 347 Expr u1Name = abTernary.getIfTrue(); 348 Expr u2Name = abTernary.getIfFalse(); 349 Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue(); 350 Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse(); 351 352 mExprModel.seal(); 353 Iterable<Expr> shouldRead = getShouldRead(); 354 355 assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp); 356 357 Iterable<Expr> firstRead = getReadFirst(shouldRead); 358 359 assertExactMatch(firstRead, a, b, c); 360 361 assertFlags(a, a, b, u1, u2, u1Name, u2Name); 362 assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e); 363 assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e); 364 assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name); 365 assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e); 366 367 assertTrue(mExprModel.markBitsRead()); 368 369 shouldRead = getShouldRead(); 370 Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary, 371 abTernary.getIfTrue(), abTernary.getIfFalse()}; 372 assertExactMatch(shouldRead, batch); 373 firstRead = getReadFirst(shouldRead); 374 assertExactMatch(firstRead, d, e, u1, u2); 375 376 assertFlags(d, bcTernary.getRequirementFlagIndex(true)); 377 assertFlags(e, bcTernary.getRequirementFlagIndex(false)); 378 assertFlags(u1, bcTernary.getRequirementFlagIndex(true), 379 abTernary.getRequirementFlagIndex(true)); 380 assertFlags(u2, bcTernary.getRequirementFlagIndex(false), 381 abTernary.getRequirementFlagIndex(false)); 382 383 assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true)); 384 assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false)); 385 assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false)); 386 assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name); 387 assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true)); 388 assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false)); 389 390 assertTrue(mExprModel.markBitsRead()); 391 392 shouldRead = getShouldRead(); 393 // actually, there is no real case to read u1 anymore because if b>c was not true, 394 // u1.getCond(d) will never be set. Right now, we don't have mechanism to figure this out 395 // and also it does not affect correctness (just an unnecessary if stmt) 396 assertExactMatch(shouldRead, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary); 397 firstRead = getReadFirst(shouldRead); 398 assertExactMatch(firstRead, u1LastName, u2); 399 400 assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true)); 401 assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false)); 402 assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false)); 403 404 assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true)); 405 assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e); 406 } 407 408 @Test 409 public void testCircularDependency() { 410 LayoutBinder lb = new MockLayoutBinder(); 411 mExprModel = lb.getModel(); 412 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); 413 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); 414 final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class); 415 mExprModel.seal(); 416 Iterable<Expr> shouldRead = getShouldRead(); 417 assertExactMatch(shouldRead, a, abTernary.getPred()); 418 assertTrue(mExprModel.markBitsRead()); 419 shouldRead = getShouldRead(); 420 assertExactMatch(shouldRead, b, abTernary); 421 assertFalse(mExprModel.markBitsRead()); 422 } 423 424 @Test 425 public void testNestedCircularDependency() { 426 LayoutBinder lb = new MockLayoutBinder(); 427 mExprModel = lb.getModel(); 428 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); 429 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); 430 IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName()); 431 final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class); 432 final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue(); 433 mExprModel.seal(); 434 Iterable<Expr> shouldRead = getShouldRead(); 435 assertExactMatch(shouldRead, a, a3Ternary.getPred()); 436 assertTrue(mExprModel.markBitsRead()); 437 shouldRead = getShouldRead(); 438 assertExactMatch(shouldRead, c, c4Ternary.getPred()); 439 assertFlags(c, a3Ternary.getRequirementFlagIndex(true), 440 a3Ternary.getRequirementFlagIndex(false)); 441 assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true)); 442 } 443 444 @Test 445 public void testInterExprCircularDependency() { 446 LayoutBinder lb = new MockLayoutBinder(); 447 mExprModel = lb.getModel(); 448 IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); 449 IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); 450 final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class); 451 final TernaryExpr abTernary2 = parse(lb, "b > 3 ? b : a", TernaryExpr.class); 452 mExprModel.seal(); 453 Iterable<Expr> shouldRead = getShouldRead(); 454 assertExactMatch(shouldRead, a, b, abTernary.getPred(), abTernary2.getPred()); 455 assertTrue(mExprModel.markBitsRead()); 456 shouldRead = getShouldRead(); 457 assertExactMatch(shouldRead, abTernary, abTernary2); 458 } 459 460 @Test 461 public void testNoFlagsForNonBindingStatic() { 462 LayoutBinder lb = new MockLayoutBinder(); 463 mExprModel = lb.getModel(); 464 lb.addVariable("a", int.class.getCanonicalName()); 465 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 466 mExprModel.seal(); 467 // +1 for invalidate all flag 468 assertEquals(1, parsed.getRight().getInvalidFlags().cardinality()); 469 // +1 for invalidate all flag 470 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 471 // +1 for invalidate all flag 472 assertEquals(2, mExprModel.getInvalidateableFieldLimit()); 473 } 474 475 @Test 476 public void testFlagsForBindingStatic() { 477 LayoutBinder lb = new MockLayoutBinder(); 478 mExprModel = lb.getModel(); 479 lb.addVariable("a", int.class.getCanonicalName()); 480 final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class); 481 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 482 mExprModel.seal(); 483 assertTrue(staticParsed.isBindingExpression()); 484 // +1 for invalidate all flag 485 assertEquals(1, staticParsed.getInvalidFlags().cardinality()); 486 assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags()); 487 // +1 for invalidate all flag 488 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 489 // +1 for invalidate all flag 490 assertEquals(2, mExprModel.getInvalidateableFieldLimit()); 491 } 492 493 @Test 494 public void testFinalFieldOfAVariable() { 495 LayoutBinder lb = new MockLayoutBinder(); 496 mExprModel = lb.getModel(); 497 IdentifierExpr user = lb.addVariable("user", User.class.getCanonicalName()); 498 Expr fieldGet = parse(lb, "user.finalField", FieldAccessExpr.class); 499 mExprModel.seal(); 500 assertTrue(fieldGet.isDynamic()); 501 // read user 502 assertExactMatch(getShouldRead(), user, fieldGet); 503 mExprModel.markBitsRead(); 504 // no need to read user.finalField 505 assertEquals(0, Iterables.size(getShouldRead())); 506 } 507 508 @Test 509 public void testFinalFieldOfAField() { 510 LayoutBinder lb = new MockLayoutBinder(); 511 mExprModel = lb.getModel(); 512 lb.addVariable("user", User.class.getCanonicalName()); 513 Expr finalFieldGet = parse(lb, "user.subObj.finalField", FieldAccessExpr.class); 514 mExprModel.seal(); 515 assertTrue(finalFieldGet.isDynamic()); 516 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 517 // read user 518 Iterable<Expr> shouldRead = getShouldRead(); 519 assertEquals(3, Iterables.size(shouldRead)); 520 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 521 finalFieldGet); 522 mExprModel.markBitsRead(); 523 // no need to read user.subObj.finalField because it is final 524 assertEquals(0, Iterables.size(getShouldRead())); 525 } 526 527 @Test 528 public void testFinalFieldOfAMethod() { 529 LayoutBinder lb = new MockLayoutBinder(); 530 mExprModel = lb.getModel(); 531 lb.addVariable("user", User.class.getCanonicalName()); 532 Expr finalFieldGet = parse(lb, "user.anotherSubObj.finalField", FieldAccessExpr.class); 533 mExprModel.seal(); 534 assertTrue(finalFieldGet.isDynamic()); 535 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 536 // read user 537 Iterable<Expr> shouldRead = getShouldRead(); 538 assertEquals(3, Iterables.size(shouldRead)); 539 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 540 finalFieldGet); 541 mExprModel.markBitsRead(); 542 // no need to read user.subObj.finalField because it is final 543 assertEquals(0, Iterables.size(getShouldRead())); 544 } 545 546 @Test 547 public void testFinalOfAClass() { 548 LayoutBinder lb = new MockLayoutBinder(); 549 mExprModel = lb.getModel(); 550 mExprModel.addImport("View", "android.view.View"); 551 FieldAccessExpr fieldAccess = parse(lb, "View.VISIBLE", FieldAccessExpr.class); 552 assertFalse(fieldAccess.isDynamic()); 553 mExprModel.seal(); 554 assertEquals(0, Iterables.size(getShouldRead())); 555 } 556 557 @Test 558 public void testFinalOfStaticField() { 559 LayoutBinder lb = new MockLayoutBinder(); 560 mExprModel = lb.getModel(); 561 mExprModel.addImport("UX", User.class.getCanonicalName()); 562 FieldAccessExpr fieldAccess = parse(lb, "UX.innerStaticInstance.finalStaticField", FieldAccessExpr.class); 563 assertFalse(fieldAccess.isDynamic()); 564 mExprModel.seal(); 565 assertExactMatch(getShouldRead(), fieldAccess.getChild()); 566 } 567 568 @Test 569 public void testFinalOfFinalStaticField() { 570 LayoutBinder lb = new MockLayoutBinder(); 571 mExprModel = lb.getModel(); 572 mExprModel.addImport("User", User.class.getCanonicalName()); 573 FieldAccessExpr fieldAccess = parse(lb, "User.innerFinalStaticInstance.finalStaticField", FieldAccessExpr.class); 574 assertFalse(fieldAccess.isDynamic()); 575 mExprModel.seal(); 576 assertEquals(0, Iterables.size(getShouldRead())); 577 } 578 579// TODO uncomment when we have inner static access 580// @Test 581// public void testFinalOfInnerStaticClass() { 582// LayoutBinder lb = new MockLayoutBinder(); 583// mExprModel = lb.getModel(); 584// mExprModel.addImport("User", User.class.getCanonicalName()); 585// FieldAccessExpr fieldAccess = parse(lb, "User.InnerStaticClass.finalStaticField", FieldAccessExpr.class); 586// assertFalse(fieldAccess.isDynamic()); 587// mExprModel.seal(); 588// assertEquals(0, Iterables.size(getShouldRead())); 589// } 590 591 private void assertFlags(Expr a, int... flags) { 592 BitSet bitset = new BitSet(); 593 for (int flag : flags) { 594 bitset.set(flag); 595 } 596 assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags()); 597 } 598 599 private void assertFlags(Expr a, Expr... exprs) { 600 BitSet bitSet = a.getShouldReadFlags(); 601 for (Expr expr : exprs) { 602 BitSet clone = (BitSet) bitSet.clone(); 603 clone.and(expr.getInvalidFlags()); 604 assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr 605 .getUniqueKey(), expr.getInvalidFlags(), clone); 606 } 607 608 BitSet composite = new BitSet(); 609 for (Expr expr : exprs) { 610 composite.or(expr.getInvalidFlags()); 611 } 612 assertEquals("composite flags should match", composite, bitSet); 613 } 614 615 private void assertExactMatch(Iterable<Expr> iterable, Expr... exprs) { 616 int i = 0; 617 log("list", iterable); 618 for (Expr expr : exprs) { 619 assertTrue((i++) + ":must contain " + expr.getUniqueKey(), 620 Iterables.contains(iterable, expr)); 621 } 622 i = 0; 623 for (Expr expr : iterable) { 624 assertTrue((i++) + ":must be expected " + expr.getUniqueKey(), 625 ArrayUtils.contains(exprs, expr)); 626 } 627 } 628 629 private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) { 630 final Expr parsed = binder.parse(input); 631 assertTrue(klass.isAssignableFrom(parsed.getClass())); 632 return (T) parsed; 633 } 634 635 private void log(String s, Iterable<Expr> iterable) { 636 L.d(s); 637 for (Expr e : iterable) { 638 L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(), 639 e.getShouldReadFlagsWithConditionals(), e.getReadSoFar()); 640 } 641 L.d("end of %s", s); 642 } 643 644 private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead) { 645 return getReadFirst(shouldRead, null); 646 } 647 648 private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead, final Iterable<Expr> justRead) { 649 return Iterables.filter(shouldRead, new Predicate<Expr>() { 650 @Override 651 public boolean apply(Expr input) { 652 return input.shouldReadNow(justRead); 653 } 654 }); 655 } 656 657 private Iterable<Expr> getShouldRead() { 658 return mExprModel.filterShouldRead(mExprModel.getPendingExpressions()); 659 } 660 661 public static class User extends BaseObservable { 662 663 String name; 664 665 String lastName; 666 667 public final int finalField = 5; 668 public static InnerStaticClass innerStaticInstance = new InnerStaticClass(); 669 public static final InnerStaticClass innerFinalStaticInstance = new InnerStaticClass(); 670 public SubObj subObj = new SubObj(); 671 672 public String getName() { 673 return name; 674 } 675 676 public String getLastName() { 677 return lastName; 678 } 679 680 public boolean getCond(int i) { 681 return true; 682 } 683 684 public SubObj getAnotherSubObj() { 685 return new SubObj(); 686 } 687 688 public static class InnerStaticClass { 689 public static final int finalField = 3; 690 public static final int finalStaticField = 3; 691 } 692 } 693 694 public static class SubObj { 695 public final int finalField = 5; 696 } 697 698} 699