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