@Test
public void applyStructification() {

  // A placeholder translation unit that has one (incomplete) declaration.
  // We want to check that the generated structs get put into this translation unit, before
  // the existing declaration.
  TranslationUnit tu = new TranslationUnit(
      Optional.empty(),
      Collections.singletonList(new VariablesDeclaration(BasicType.FLOAT, new ArrayList<>())));
  // float v = 3.0;
  DeclarationStmt declStmt =
      new DeclarationStmt(
          new VariablesDeclaration(
              BasicType.FLOAT,
              new VariableDeclInfo(
                  "v",
                  null,
                  new Initializer(
                      new FloatConstantExpr("3.0")))));
  // float v = 3.0;
  // v = v + v;
  // {
  //   float v = 2.0;
  //   v = v + v;
  // }
  ExprStmt assignment = new ExprStmt(
      new BinaryExpr(
          new VariableIdentifierExpr("v"),
          new BinaryExpr(
              new VariableIdentifierExpr("v"),
              new VariableIdentifierExpr("v"),
              BinOp.ADD
          ),
          BinOp.ASSIGN
      )
  );
  BlockStmt block =
      new BlockStmt(
          Arrays.asList(
              declStmt.clone(),
              assignment.clone(),
              new BlockStmt(Arrays.asList(declStmt.clone(), assignment.clone()), true)
          ), true);
  CannedRandom generator = new CannedRandom(
      // To generate the struct
      2 /* (2 + 1) fields */,
      true /* first field is struct */,
      1 /* with (1 + 1) fields */,
      false /* first of which is not a struct */,
      0 /* instead it is FLOAT */,
      false /* second of which is not a struct */,
      1 /* instead it is VEC2 */,
      false /* second top-level field is not a struct */,
      0 /* instead it is FLOAT */,
      true /* third top-level field is a struct */,
      0 /* with (0 + 1) field */,
      false /* that is not a struct */,
      0 /* instead it is FLOAT */,
      // To insert variable into the struct
      true, /* choose to insert into struct field */
      0, /* choose to insert into the first of the struct fields */
      2 /* choose to insert at the end of this struct */
  );
  new StructificationMutation(
      (DeclarationStmt) block.getStmt(0),
      block,
      tu,
      new IdGenerator(),
      generator,
      GenerationParams.normal(ShaderKind.FRAGMENT, true))
      .apply();
  // The generator should have used up all its values by now.
  assertTrue(generator.isExhausted());
  // Check that the right structs have been added
  assertEquals(4, tu.getTopLevelDeclarations().size()); // The dummy decl, plus 3 more structs
  // Check there are the same number of statements in the block
  assertEquals(3, block.getNumStmts());
  assertEquals(2, ((BlockStmt) block.getStmt(2)).getNumStmts());
  // Check that the inner declaration is still called v
  assertEquals("v",
      ((DeclarationStmt) ((BlockStmt) block.getStmt(2)).getStmt(0)).getVariablesDeclaration()
          .getDeclInfo(0).getName());
  assertEquals(BasicType.FLOAT,
      ((DeclarationStmt) ((BlockStmt) block.getStmt(2)).getStmt(0)).getVariablesDeclaration()
          .getBaseType());
  assertEquals("v",
      ((VariableIdentifierExpr) ((BinaryExpr) ((ExprStmt) ((BlockStmt) block.getStmt(2))
          .getStmt(1)).getExpr()).getLhs()).getName());
  String newName =
      ((DeclarationStmt) block.getStmt(0)).getVariablesDeclaration().getDeclInfo(0).getName();
  assertTrue(newName.startsWith(Constants.GLF_STRUCT_REPLACEMENT));
  BinaryExpr newAssignment = (BinaryExpr) ((ExprStmt) block.getStmt(1)).getExpr();
  assertTrue(newAssignment.getLhs() instanceof MemberLookupExpr);
  assertEquals("v", ((MemberLookupExpr) newAssignment.getLhs()).getMember());
  assertTrue(((MemberLookupExpr) newAssignment.getLhs()).getStructure()
      instanceof MemberLookupExpr);
  assertEquals("_f0",
      ((MemberLookupExpr) ((MemberLookupExpr) newAssignment.getLhs()).getStructure())
      .getMember());
  assertTrue(((MemberLookupExpr) ((MemberLookupExpr) newAssignment.getLhs()).getStructure())
      .getStructure() instanceof VariableIdentifierExpr);
  assertEquals(newName,
      ((VariableIdentifierExpr)
          ((MemberLookupExpr) ((MemberLookupExpr) newAssignment.getLhs()).getStructure())
              .getStructure()).getName()
  );
}