function testTree_JoinPredicates3() {
  var j1 = schema.getJob().as('j1');
  var j2 = schema.getJob().as('j2');

  var treeBefore =
      'select(join_pred(j1.maxSalary eq j2.minSalary))\n' +
      '-select(value_pred(j1.maxSalary lt 30000))\n' +
      '--cross_product\n' +
      '---table_access(Job as j1)\n' +
      '---table_access(Job as j2)\n';

  var treeAfter =
      'select(join_pred(j1.maxSalary eq j2.minSalary))\n' +
      '-cross_product\n' +
      '--select(value_pred(j1.maxSalary lt 30000))\n' +
      '---table_access(Job as j1)\n' +
      '--table_access(Job as j2)\n';

  var constructTree = function() {
    var queryContext = new lf.query.SelectContext(hr.db.getSchema());
    queryContext.from = [j1, j2];
    var predicate1 = j1.maxSalary.lt(30000);
    var predicate2 = j1.maxSalary.eq(j2.minSalary);
    queryContext.where = lf.op.and(predicate1, predicate2);

    var crossProductNode = new lf.proc.CrossProductNode();
    crossProductNode.addChild(new lf.proc.TableAccessNode(j1));
    crossProductNode.addChild(new lf.proc.TableAccessNode(j2));
    var selectNode1 = new lf.proc.SelectNode(predicate1);
    selectNode1.addChild(crossProductNode);
    var selectNode2 = new lf.proc.SelectNode(predicate2);
    selectNode2.addChild(selectNode1);

    return {queryContext: queryContext, root: selectNode2};
  };

  lf.testing.treeutil.assertTreeTransformation(
      constructTree(), treeBefore, treeAfter, pass);
}
Tests a tree that involves a self-table join and table aliases. The value
predicate that refers to only one of the two tables is expected to be pushed
below the cross product node, whereas the join predicate refers to both
tables and therefore should not be pushed further down.