@Test
public void testSuppressAfterExtractViolationIfExpectationSatisfiedAfterSlingInstall_multipleViolators()
        throws Exception {

    final String foo1 = "/foo1";
    final String foo2 = "/foo2";
    final PackageId container = PackageId.fromString("container");
    final PackageId embed1 = PackageId.fromString("embed1");
    SlingInstallable installable1 = mock(SlingInstallable.class);
    when(installable1.getParentId()).thenReturn(embed1);
    EmbeddedPackageInstallable epInstallable1 = new EmbeddedPackageInstallable(container, "", embed1);
    final PackageId embed2 = PackageId.fromString("embed2");
    SlingInstallable installable2 = mock(SlingInstallable.class);
    when(installable2.getParentId()).thenReturn(embed2);
    EmbeddedPackageInstallable epInstallable2 = new EmbeddedPackageInstallable(container, "", embed2);
    final PackageId embed3 = PackageId.fromString("embed3");
    SlingInstallable installable3 = mock(SlingInstallable.class);
    when(installable3.getParentId()).thenReturn(embed3);
    EmbeddedPackageInstallable epInstallable3 = new EmbeddedPackageInstallable(container, "", embed3);
    final ExpectPaths.Check check1 = checkFor(obj()
            .key("expectedPaths", arr(foo1, foo2))
            .key(ExpectPaths.keys().afterPackageIdRules(), arr()
                    .val(new Rule(RuleType.EXCLUDE, Pattern.compile(":container"))))
            .get());
    new OakpalPlan.Builder(null, null)
            .withRepoInits(Collections.singletonList(String.format("create path %s", foo1)))
            .build().toOakMachineBuilder(null, getClass().getClassLoader()).build()
            .adminInitAndInspect(session -> {
                check1.startedScan();
                check1.identifyPackage(container, null);
                check1.afterExtract(container, session);
                check1.beforeSlingInstall(container, epInstallable1, session);
                check1.identifyEmbeddedPackage(embed1, container, epInstallable1);
                check1.afterExtract(embed1, session);
                check1.beforeSlingInstall(container, epInstallable2, session);
                check1.identifyEmbeddedPackage(embed2, container, epInstallable2);
                check1.afterExtract(embed2, session);
                check1.beforeSlingInstall(container, epInstallable3, session);
                check1.identifyEmbeddedPackage(embed3, container, epInstallable3);
                check1.afterExtract(embed3, session);
                check1.beforeSlingInstall(container, installable1, session);
                check1.beforeSlingInstall(container, installable2, session);
                check1.beforeSlingInstall(container, installable3, session);
                check1.afterScanPackage(container, session);
                check1.finishedScan();
            });
    assertEquals("expect 1 expected violation", 1, check1.getReportedViolations().stream().count());
    final Violation exViolation1 = check1.getReportedViolations().stream().findFirst().get();
    assertTrue("starts with expected: " + exViolation1.getDescription(), exViolation1.getDescription().startsWith("expected"));
    assertTrue("ends with /foo2: " + exViolation1.getDescription(), exViolation1.getDescription().endsWith(foo2));
    assertEquals("expected violators",
            Stream.of(embed1, container).collect(Collectors.toSet()),
            new HashSet<>(exViolation1.getPackages()));
    new OakpalPlan.Builder(null, null)
            .withRepoInits(Collections.singletonList(String.format("create path %s", foo1)))
            .build().toOakMachineBuilder(null, getClass().getClassLoader()).build()
            .adminInitAndInspect(session -> {
                check1.startedScan();
                check1.identifyPackage(container, null);
                check1.afterExtract(container, session);
                check1.beforeSlingInstall(container, epInstallable1, session);
                check1.identifyEmbeddedPackage(embed1, container, epInstallable1);
                JcrUtils.getOrCreateByPath(foo2, "nt:folder", session);
                check1.afterExtract(embed1, session);
                check1.beforeSlingInstall(container, epInstallable2, session);
                check1.identifyEmbeddedPackage(embed2, container, epInstallable2);
                check1.afterExtract(embed2, session);
                check1.beforeSlingInstall(container, epInstallable3, session);
                check1.identifyEmbeddedPackage(embed3, container, epInstallable3);
                session.getNode(foo2).remove();
                check1.afterExtract(embed3, session);
                check1.beforeSlingInstall(container, installable1, session);
                check1.beforeSlingInstall(container, installable2, session);
                check1.beforeSlingInstall(container, installable3, session);
                check1.afterScanPackage(container, session);
                check1.finishedScan();
            });
    assertEquals("expect 1 expected violation", 1, check1.getReportedViolations().stream().count());
    final Violation exViolation2 = check1.getReportedViolations().stream().findFirst().get();
    assertTrue("starts with expected: " + exViolation2.getDescription(), exViolation2.getDescription().startsWith("expected"));
    assertTrue("ends with /foo2: " + exViolation2.getDescription(), exViolation2.getDescription().endsWith(foo2));
    assertEquals("expected violators",
            Stream.of(container, embed3).collect(Collectors.toSet()),
            new HashSet<>(exViolation2.getPackages()));
    final ExpectPaths.Check check2 = checkFor(obj()
            .key("notExpectedPaths", arr(foo1, foo2))
            //.key(ExpectPaths.keys().afterPackageIdRules(), arr()
            //        .val(new Rule(RuleType.EXCLUDE, Pattern.compile(":container"))))
            .get());
    new OakpalPlan.Builder(null, null)
            .withRepoInits(Collections.singletonList(String.format("create path %s", foo1)))
            .build().toOakMachineBuilder(null, getClass().getClassLoader()).build()
            .adminInitAndInspect(session -> {
                check2.startedScan();
                check2.identifyPackage(container, null);
                check2.afterExtract(container, session);
                check2.beforeSlingInstall(embed1, epInstallable1, session);
                check2.identifyEmbeddedPackage(embed1, container, epInstallable1);
                check2.afterExtract(embed1, session);
                check2.beforeSlingInstall(embed2, epInstallable2, session);
                check2.identifyEmbeddedPackage(embed2, container, epInstallable2);
                check2.afterExtract(embed2, session);
                check2.beforeSlingInstall(embed3, epInstallable3, session);
                check2.identifyEmbeddedPackage(embed3, container, epInstallable3);
                check2.afterExtract(embed3, session);
                check2.beforeSlingInstall(container, installable1, session);
                check2.beforeSlingInstall(container, installable2, session);
                check2.beforeSlingInstall(container, installable3, session);
                check2.afterScanPackage(container, session);
                check2.finishedScan();
            });
    assertEquals("expect 1 unexpected violation", 1, check2.getReportedViolations().stream().count());
    final Violation unViolation1 = check2.getReportedViolations().stream().findFirst().get();
    assertTrue("starts with unexpected: " + unViolation1.getDescription(), unViolation1.getDescription().startsWith("unexpected"));
    assertTrue("ends with /foo1: " + unViolation1.getDescription(), unViolation1.getDescription().endsWith(foo1));
    assertEquals("expected violators",
            Stream.of(container).collect(Collectors.toSet()),
            new HashSet<>(unViolation1.getPackages()));
    new OakpalPlan.Builder(null, null)
            .withRepoInits(Collections.singletonList(String.format("create path %s", foo1)))
            .build().toOakMachineBuilder(null, getClass().getClassLoader()).build()
            .adminInitAndInspect(session -> {
                check2.startedScan();
                check2.identifyPackage(container, null);
                session.getNode(foo1).remove();
                check2.afterExtract(container, session);
                check2.beforeSlingInstall(embed1, epInstallable1, session);
                check2.identifyEmbeddedPackage(embed1, container, epInstallable1);
                JcrUtils.getOrCreateByPath(foo1, "nt:folder", session);
                check2.afterExtract(embed1, session);
                check2.beforeSlingInstall(embed2, epInstallable2, session);
                check2.identifyEmbeddedPackage(embed2, container, epInstallable2);
                check2.afterExtract(embed2, session);
                check2.beforeSlingInstall(embed3, epInstallable3, session);
                check2.identifyEmbeddedPackage(embed3, container, epInstallable3);
                session.getNode(foo1).remove();
                check2.afterExtract(embed3, session);
                check2.beforeSlingInstall(container, installable1, session);
                check2.beforeSlingInstall(container, installable2, session);
                JcrUtils.getOrCreateByPath(foo2, "nt:folder", session);
                check2.appliedRepoInitScripts(container, Collections.emptyList(), installable2, session);
                check2.beforeSlingInstall(container, installable3, session);
                check2.afterScanPackage(container, session);
                check2.finishedScan();
            });
    assertEquals("expect 2 unexpected violations", 2, check2.getReportedViolations().size());
    final Collection<Violation> unViolations = check2.getReportedViolations();
    assertTrue("all start with unexpected " + unViolations, unViolations.stream()
            .allMatch(vio -> vio.getDescription().startsWith("unexpected")));
    assertEquals("one ends with /foo1: " + unViolations, 1, unViolations.stream()
            .filter(vio -> vio.getDescription().endsWith("/foo1")).count());
    assertEquals("one ends with /foo2: " + unViolations, 1, unViolations.stream()
            .filter(vio -> vio.getDescription().endsWith("/foo2")).count());
    assertEquals("expected violators of /foo1",
            Stream.of(embed1).collect(Collectors.toSet()), unViolations.stream()
                    .filter(vio -> vio.getDescription().endsWith("/foo1"))
                    .flatMap(Fun.compose1(Violation::getPackages, Collection::stream))
                    .collect(Collectors.toSet()));
    assertEquals("expected violators of /foo2",
            Stream.of(embed2, container).collect(Collectors.toSet()), unViolations.stream()
                    .filter(vio -> vio.getDescription().endsWith("/foo2"))
                    .flatMap(Fun.compose1(Violation::getPackages, Collection::stream))
                    .collect(Collectors.toSet()));
}