ExprModelTest.java revision 019c36b97c7c172ac03997f6bf170e65b2ed7fe4
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 testNoFlagsForNonBindingStatic() { 446 LayoutBinder lb = new MockLayoutBinder(); 447 mExprModel = lb.getModel(); 448 lb.addVariable("a", int.class.getCanonicalName()); 449 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 450 mExprModel.seal(); 451 // +1 for invalidate all flag 452 assertEquals(1, parsed.getRight().getInvalidFlags().cardinality()); 453 // +1 for invalidate all flag 454 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 455 // +1 for invalidate all flag 456 assertEquals(2, mExprModel.getInvalidateableFieldLimit()); 457 } 458 459 @Test 460 public void testFlagsForBindingStatic() { 461 LayoutBinder lb = new MockLayoutBinder(); 462 mExprModel = lb.getModel(); 463 lb.addVariable("a", int.class.getCanonicalName()); 464 final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class); 465 final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); 466 mExprModel.seal(); 467 assertTrue(staticParsed.isBindingExpression()); 468 // +1 for invalidate all flag 469 assertEquals(2, staticParsed.getInvalidFlags().cardinality()); 470 assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags()); 471 // +1 for invalidate all flag 472 assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality()); 473 // +1 for invalidate all flag 474 assertEquals(3, mExprModel.getInvalidateableFieldLimit()); 475 } 476 477 @Test 478 public void testFinalFieldOfAVariable() { 479 LayoutBinder lb = new MockLayoutBinder(); 480 mExprModel = lb.getModel(); 481 lb.addVariable("user", User.class.getCanonicalName()); 482 Expr fieldGet = parse(lb, "user.finalField", FieldAccessExpr.class); 483 mExprModel.seal(); 484 assertTrue(fieldGet.isDynamic()); 485 // read user 486 assertSame(fieldGet.getChildren().get(0), Iterables.getFirst(getShouldRead(), null)); 487 mExprModel.markBitsRead(); 488 // no need to read user.finalField 489 assertEquals(0, Iterables.size(getShouldRead())); 490 } 491 492 @Test 493 public void testFinalFieldOfAField() { 494 LayoutBinder lb = new MockLayoutBinder(); 495 mExprModel = lb.getModel(); 496 lb.addVariable("user", User.class.getCanonicalName()); 497 Expr finalFieldGet = parse(lb, "user.subObj.finalField", FieldAccessExpr.class); 498 mExprModel.seal(); 499 assertTrue(finalFieldGet.isDynamic()); 500 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 501 // read user 502 Iterable<Expr> shouldRead = getShouldRead(); 503 assertEquals(3, Iterables.size(shouldRead)); 504 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 505 finalFieldGet); 506 mExprModel.markBitsRead(); 507 // no need to read user.subObj.finalField because it is final 508 assertEquals(0, Iterables.size(getShouldRead())); 509 } 510 511 @Test 512 public void testFinalFieldOfAMethod() { 513 LayoutBinder lb = new MockLayoutBinder(); 514 mExprModel = lb.getModel(); 515 lb.addVariable("user", User.class.getCanonicalName()); 516 Expr finalFieldGet = parse(lb, "user.anotherSubObj.finalField", FieldAccessExpr.class); 517 mExprModel.seal(); 518 assertTrue(finalFieldGet.isDynamic()); 519 Expr userSubObjGet = finalFieldGet.getChildren().get(0); 520 // read user 521 Iterable<Expr> shouldRead = getShouldRead(); 522 assertEquals(3, Iterables.size(shouldRead)); 523 assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet, 524 finalFieldGet); 525 mExprModel.markBitsRead(); 526 // no need to read user.subObj.finalField because it is final 527 assertEquals(0, Iterables.size(getShouldRead())); 528 } 529 530 @Test 531 public void testFinalOfAClass() { 532 LayoutBinder lb = new MockLayoutBinder(); 533 mExprModel = lb.getModel(); 534 mExprModel.addImport("View", "android.view.View"); 535 FieldAccessExpr fieldAccess = parse(lb, "View.VISIBLE", FieldAccessExpr.class); 536 assertFalse(fieldAccess.isDynamic()); 537 mExprModel.seal(); 538 assertEquals(0, Iterables.size(getShouldRead())); 539 } 540 541 @Test 542 public void testFinalOfStaticField() { 543 LayoutBinder lb = new MockLayoutBinder(); 544 mExprModel = lb.getModel(); 545 mExprModel.addImport("UX", User.class.getCanonicalName()); 546 FieldAccessExpr fieldAccess = parse(lb, "UX.innerStaticInstance.finalStaticField", FieldAccessExpr.class); 547 assertFalse(fieldAccess.isDynamic()); 548 mExprModel.seal(); 549 assertExactMatch(getShouldRead(), fieldAccess.getChild()); 550 } 551 552 @Test 553 public void testFinalOfFinalStaticField() { 554 LayoutBinder lb = new MockLayoutBinder(); 555 mExprModel = lb.getModel(); 556 mExprModel.addImport("User", User.class.getCanonicalName()); 557 FieldAccessExpr fieldAccess = parse(lb, "User.innerFinalStaticInstance.finalStaticField", FieldAccessExpr.class); 558 assertFalse(fieldAccess.isDynamic()); 559 mExprModel.seal(); 560 assertEquals(0, Iterables.size(getShouldRead())); 561 } 562 563// TODO uncomment when we have inner static access 564// @Test 565// public void testFinalOfInnerStaticClass() { 566// LayoutBinder lb = new MockLayoutBinder(); 567// mExprModel = lb.getModel(); 568// mExprModel.addImport("User", User.class.getCanonicalName()); 569// FieldAccessExpr fieldAccess = parse(lb, "User.InnerStaticClass.finalStaticField", FieldAccessExpr.class); 570// assertFalse(fieldAccess.isDynamic()); 571// mExprModel.seal(); 572// assertEquals(0, Iterables.size(getShouldRead())); 573// } 574 575 private void assertFlags(Expr a, int... flags) { 576 BitSet bitset = new BitSet(); 577 for (int flag : flags) { 578 bitset.set(flag); 579 } 580 assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags()); 581 } 582 583 private void assertFlags(Expr a, Expr... exprs) { 584 BitSet bitSet = a.getShouldReadFlags(); 585 for (Expr expr : exprs) { 586 BitSet clone = (BitSet) bitSet.clone(); 587 clone.and(expr.getInvalidFlags()); 588 assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr 589 .getUniqueKey(), expr.getInvalidFlags(), clone); 590 } 591 592 BitSet composite = new BitSet(); 593 for (Expr expr : exprs) { 594 composite.or(expr.getInvalidFlags()); 595 } 596 assertEquals("composite flags should match", composite, bitSet); 597 } 598 599 private void assertExactMatch(Iterable<Expr> iterable, Expr... exprs) { 600 int i = 0; 601 log("list", iterable); 602 for (Expr expr : exprs) { 603 assertTrue((i++) + ":must contain " + expr.getUniqueKey(), 604 Iterables.contains(iterable, expr)); 605 } 606 i = 0; 607 for (Expr expr : iterable) { 608 assertTrue((i++) + ":must be expected " + expr.getUniqueKey(), 609 ArrayUtils.contains(exprs, expr)); 610 } 611 } 612 613 private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) { 614 final Expr parsed = binder.parse(input); 615 assertTrue(klass.isAssignableFrom(parsed.getClass())); 616 return (T) parsed; 617 } 618 619 private void log(String s, Iterable<Expr> iterable) { 620 L.d(s); 621 for (Expr e : iterable) { 622 L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(), 623 e.getShouldReadFlagsWithConditionals(), e.getReadSoFar()); 624 } 625 L.d("end of %s", s); 626 } 627 628 private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead) { 629 return getReadFirst(shouldRead, null); 630 } 631 632 private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead, final Iterable<Expr> justRead) { 633 return Iterables.filter(shouldRead, new Predicate<Expr>() { 634 @Override 635 public boolean apply(Expr input) { 636 return input.shouldReadNow(justRead); 637 } 638 }); 639 } 640 641 private Iterable<Expr> getShouldRead() { 642 return mExprModel.filterShouldRead(mExprModel.getPendingExpressions()); 643 } 644 645 public static class User extends BaseObservable { 646 647 String name; 648 649 String lastName; 650 651 public final int finalField = 5; 652 public static InnerStaticClass innerStaticInstance = new InnerStaticClass(); 653 public static final InnerStaticClass innerFinalStaticInstance = new InnerStaticClass(); 654 public SubObj subObj = new SubObj(); 655 656 public String getName() { 657 return name; 658 } 659 660 public String getLastName() { 661 return lastName; 662 } 663 664 public boolean getCond(int i) { 665 return true; 666 } 667 668 public SubObj getAnotherSubObj() { 669 return new SubObj(); 670 } 671 672 public static class InnerStaticClass { 673 public static final int finalField = 3; 674 public static final int finalStaticField = 3; 675 } 676 } 677 678 public static class SubObj { 679 public final int finalField = 5; 680 } 681 682} 683