content: /**
   * Copyright Alex Objelean
   */
  package ro.isdc.wro.maven.plugin;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import static org.mockito.Mockito.spy;
  import static org.mockito.Mockito.when;
  
  import java.io.ByteArrayInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.PrintStream;
  import java.io.Reader;
  import java.io.Writer;
  import java.net.URISyntaxException;
  import java.net.URL;
  import java.util.Collection;
  import java.util.Date;
  import java.util.Properties;
  import java.util.UUID;
  import java.util.concurrent.Callable;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import org.apache.commons.io.FileUtils;
  import org.apache.maven.model.Build;
  import org.apache.maven.model.Model;
  import org.apache.maven.plugin.MojoExecutionException;
  import org.apache.maven.project.MavenProject;
  import org.junit.After;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Ignore;
  import org.junit.Test;
  import org.mockito.Mock;
  import org.mockito.Mockito;
  import org.mockito.MockitoAnnotations;
  import org.mockito.invocation.InvocationOnMock;
  import org.mockito.stubbing.Answer;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.sonatype.plexus.build.incremental.BuildContext;
  
  import ro.isdc.wro.config.Context;
  import ro.isdc.wro.manager.WroManager.Builder;
  import ro.isdc.wro.manager.factory.WroManagerFactory;
  import ro.isdc.wro.manager.factory.WroManagerFactoryDecorator;
  import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
  import ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory;
  import ro.isdc.wro.model.WroModel;
  import ro.isdc.wro.model.group.Group;
  import ro.isdc.wro.model.resource.Resource;
  import ro.isdc.wro.model.resource.locator.UriLocator;
  import ro.isdc.wro.model.resource.locator.factory.UriLocatorFactory;
  import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
  import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
  import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
  import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
  import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
  import ro.isdc.wro.model.resource.support.hash.HashStrategy;
  import ro.isdc.wro.model.resource.support.naming.ConfigurableNamingStrategy;
  import ro.isdc.wro.model.resource.support.naming.DefaultHashEncoderNamingStrategy;
  import ro.isdc.wro.model.resource.support.naming.FolderHashEncoderNamingStrategy;
  import ro.isdc.wro.model.resource.support.naming.NamingStrategy;
  import ro.isdc.wro.util.WroTestUtils;
  import ro.isdc.wro.util.WroUtil;
  import ro.isdc.wro.util.concurrent.TaskExecutor;
  
  
  /**
   * Test class for {@link Wro4jMojo}
   *
   * @author Alex Objelean
   */
  public class TestWro4jMojo {
    private static final Logger LOG = LoggerFactory.getLogger(TestWro4jMojo.class);
    @Mock
    private BuildContext mockBuildContext;
    @Mock
    private HashStrategy mockHashStrategy;
    private UriLocatorFactory mockLocatorFactory;
    @Mock
    private UriLocator mockLocator;
    private File cssDestinationFolder;
    private File jsDestinationFolder;
    private File destinationFolder;
    private File extraConfigFile;
    private Wro4jMojo victim;
  
    @Before
    public void setUp()
        throws Exception {
      MockitoAnnotations.initMocks(this);
      mockLocatorFactory = new UriLocatorFactory() {
        public InputStream locate(final String uri)
            throws IOException {
          return mockLocator.locate(uri);
        }
  
        public UriLocator getInstance(final String uri) {
          return mockLocator;
        }
      };
      Context.set(Context.standaloneContext());
      victim = new Wro4jMojo();
      setUpMojo(victim);
    }
  
    /**
     * Perform basic initialization with valid values of the provided mojo.
     */
    private void setUpMojo(final Wro4jMojo mojo)
        throws Exception {
      mojo.setIgnoreMissingResources(false);
      mojo.setParallelProcessing(false);
      mojo.setMinimize(true);
      setWroWithValidResources();
      destinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-" + new Date().getTime());
      destinationFolder.mkdir();
      cssDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-css-" + new Date().getTime());
      destinationFolder.mkdir();
      jsDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-js-" + new Date().getTime());
      destinationFolder.mkdir();
      extraConfigFile = new File(FileUtils.getTempDirectory(), "extraConfig-" + new Date().getTime());
      extraConfigFile.createNewFile();
      mojo.setBuildDirectory(destinationFolder);
      mojo.setExtraConfigFile(extraConfigFile);
      mojo.setDestinationFolder(destinationFolder);
      
      MavenProject mockMavenProject = Mockito.mock(MavenProject.class);
      Model mockMavenModel = Mockito.mock(Model.class);
      Build mockBuild = Mockito.mock(Build.class);
      Mockito.when(mockMavenProject.getModel()).thenReturn(mockMavenModel);
      Mockito.when(mockMavenModel.getBuild()).thenReturn(mockBuild);
      Mockito.when(mockBuild.getDirectory()).thenReturn(FileUtils.getTempDirectoryPath());
      
      mojo.setMavenProject(mockMavenProject);
      mojo.setBuildContext(mockBuildContext);
    }
  
    private void setWroFile(final String classpathResourceName)
        throws URISyntaxException {
      final URL url = getClass().getClassLoader().getResource(classpathResourceName);
      final File wroFile = new File(url.toURI());
      victim.setWroFile(wroFile);
      victim.setContextFolder(wroFile.getParentFile().getPath());
    }
  
    private void setWroWithValidResources()
        throws Exception {
      setWroFile("wro.xml");
    }
  
    private void setWroWithInvalidResources()
        throws Exception {
      setWroFile("wroWithInvalidResources.xml");
    }
  
    @Test
    public void testMojoWithPropertiesSetAndOneTargetGroup()
        throws Exception {
      victim.setTargetGroups("g1");
      victim.setIgnoreMissingResources(true);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void shouldFailWhenInvalidResourcesAreUsed()
        throws Exception {
      victim.setIgnoreMissingResources(false);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testNoDestinationFolderSet()
        throws Exception {
      victim.setDestinationFolder(null);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testOnlyCssDestinationFolderSet()
        throws Exception {
      victim.setCssDestinationFolder(cssDestinationFolder);
      victim.setDestinationFolder(null);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testOnlyJsDestinationFolderSet()
        throws Exception {
      victim.setJsDestinationFolder(jsDestinationFolder);
      victim.setDestinationFolder(null);
      victim.execute();
    }
  
    @Test
    public void testJsAndCssDestinationFolderSet()
        throws Exception {
      victim.setIgnoreMissingResources(true);
      victim.setJsDestinationFolder(jsDestinationFolder);
      victim.setCssDestinationFolder(cssDestinationFolder);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void cannotExecuteWhenInvalidResourcesPresentAndDoNotIgnoreMissingResources()
        throws Exception {
      setWroWithInvalidResources();
      victim.setIgnoreMissingResources(false);
      victim.execute();
    }
  
    @Test
    public void testWroXmlWithInvalidResourcesAndIgnoreMissingResourcesTrue()
        throws Exception {
      setWroWithInvalidResources();
      victim.setIgnoreMissingResources(true);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testMojoWithWroManagerFactorySet()
        throws Exception {
      victim.setWroManagerFactory(ExceptionThrowingWroManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testInvalidMojoWithWroManagerFactorySet()
        throws Exception {
      victim.setWroManagerFactory("INVALID_CLASS_NAME");
      victim.execute();
    }
  
    @Test
    public void executeWithNullTargetGroupsProperty()
        throws Exception {
      victim.setIgnoreMissingResources(true);
      victim.setTargetGroups(null);
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testMojoWithCustomManagerFactoryWithInvalidResourceAndNotIgnoreMissingResources()
        throws Exception {
      setWroWithInvalidResources();
      victim.setIgnoreMissingResources(false);
      victim.setWroManagerFactory(CustomManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test
    public void testMojoWithCustomManagerFactoryWithInvalidResourceAndIgnoreMissingResources()
        throws Exception {
      setWroWithInvalidResources();
      victim.setIgnoreMissingResources(true);
      victim.setWroManagerFactory(CustomManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testMojoWithConfigurableWroManagerFactory()
        throws Exception {
      setWroWithValidResources();
      victim.setIgnoreMissingResources(true);
      // by default a valid file is used, set null explicitly
      victim.setExtraConfigFile(null);
      victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test
    public void testMojoWithConfigurableWroManagerFactoryWithValidAndEmptyConfigFileSet()
        throws Exception {
      setWroWithValidResources();
      victim.setIgnoreMissingResources(true);
      victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test
    public void testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet()
        throws Exception {
      setWroWithValidResources();
      final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=cssMin";
      FileUtils.write(extraConfigFile, preProcessors);
  
      victim.setIgnoreMissingResources(true);
      victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
      victim.execute();
    }
  
    /**
     * Ignoring this test, since it is not reliable.
     */
    @Ignore
    @Test
    public void shouldBeFasterWhenRunningProcessingInParallel()
        throws Exception {
      //warmup
      testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
      //start actual test
      final long begin = System.currentTimeMillis();
      victim.setParallelProcessing(false);
      testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
      final long endSerial = System.currentTimeMillis();
      victim.setParallelProcessing(true);
      testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
      final long endParallel = System.currentTimeMillis();
  
      final long serial = endSerial - begin;
      final long parallel = endParallel - endSerial;
      LOG.info("serial took: {}ms", serial);
      LOG.info("parallel took: {}ms", parallel);
      assertTrue(String.format("Serial (%s) > Parallel (%s)", serial, parallel), serial > parallel);
    }
  
    @Test
    public void shouldUseTaskExecutorWhenRunningInParallel()
        throws Exception {
      final AtomicBoolean invoked = new AtomicBoolean();
      final TaskExecutor<Void> taskExecutor = new TaskExecutor<Void>() {
        @Override
        public void submit(final Collection<Callable<Void>> callables)
            throws Exception {
          invoked.set(true);
          super.submit(callables);
        }
      };
      victim.setTaskExecutor(taskExecutor);
      victim.setIgnoreMissingResources(true);
  
      victim.setParallelProcessing(false);
      victim.execute();
      assertFalse(invoked.get());
  
      victim.setParallelProcessing(true);
      victim.execute();
      assertTrue(invoked.get());
    }
  
    @Test
    public void shouldComputedAggregatedFolderWhenContextPathIsSet()
        throws Exception {
      setWroWithValidResources();
      victim.setWroManagerFactory(CssUrlRewriterWroManagerFactory.class.getName());
      victim.setIgnoreMissingResources(true);
      final File cssDestinationFolder = new File(this.destinationFolder, "subfolder");
      cssDestinationFolder.mkdir();
      victim.setCssDestinationFolder(cssDestinationFolder);
      victim.execute();
      assertEquals("/subfolder", Context.get().getAggregatedFolderPath());
  
      victim.setContextPath("app");
      victim.execute();
      assertEquals("/app", Context.get().getRequest().getContextPath());
  
      victim.setContextPath("/app/");
      victim.execute();
      assertEquals("/app", Context.get().getRequest().getContextPath());
  
      victim.setContextPath("/");
      victim.execute();
      assertEquals("/", Context.get().getRequest().getContextPath());
    }
  
    @Test(expected = MojoExecutionException.class)
    public void testMojoWithConfigurableWroManagerFactoryWithInvalidPreProcessor()
        throws Exception {
      setWroWithValidResources();
      final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=INVALID";
      FileUtils.write(extraConfigFile, preProcessors);
  
      victim.setIgnoreMissingResources(true);
      victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
      victim.execute();
    }
  
    @Test
    public void shouldGenerateGroupMappingUsingNoOpNamingStrategy()
        throws Exception {
      setWroWithValidResources();
  
      final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());
  
      victim.setGroupNameMappingFile(groupNameMappingFile);
      victim.setIgnoreMissingResources(true);
      victim.execute();
  
      // verify
      final Properties groupNames = new Properties();
      groupNames.load(new FileInputStream(groupNameMappingFile));
      LOG.debug("groupNames: {}", groupNames);
      assertEquals("g1.js", groupNames.get("g1.js"));
  
      FileUtils.deleteQuietly(groupNameMappingFile);
    }
  
    @Test
    public void shouldGenerateGroupMappingUsingCustomNamingStrategy()
        throws Exception {
      setWroWithValidResources();
  
      final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());
  
      victim.setWroManagerFactory(CustomNamingStrategyWroManagerFactory.class.getName());
      victim.setGroupNameMappingFile(groupNameMappingFile);
      victim.setIgnoreMissingResources(true);
      victim.execute();
  
      // verify
      final Properties groupNames = new Properties();
      groupNames.load(new FileInputStream(groupNameMappingFile));
      LOG.debug("groupNames: {}", groupNames);
      Assert.assertEquals(CustomNamingStrategyWroManagerFactory.PREFIX + "g1.js", groupNames.get("g1.js"));
  
      FileUtils.deleteQuietly(groupNameMappingFile);
    }
  
    /**
     * Uses a not existing folder to store groupNameMappingFile and proves that it is getting created instead of failing.
     */
    @Test
    public void shouldCreateMissingFolderForGroupNameMappingFile()
        throws Exception {
      final File parentFolder = new File(FileUtils.getTempDirectory(), "wro4j-" + UUID.randomUUID());
      try {
        setWroWithValidResources();
        final File groupNameMappingFile = new File(parentFolder, "groupMapping-" + new Date().getTime());
  
        victim.setWroManagerFactory(CustomNamingStrategyWroManagerFactory.class.getName());
        victim.setGroupNameMappingFile(groupNameMappingFile);
        victim.setIgnoreMissingResources(true);
        victim.execute();
      } finally {
        FileUtils.deleteQuietly(parentFolder);
      }
    }
  
    @Test
    public void shouldSkipSecondProcessingWhenIncrementalBuildEnabled()
        throws Exception {
      victim.setBuildContext(null);
      victim.setIncrementalBuildEnabled(true);
      testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
      testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    }
  
    @Test
    public void shouldUseConfiguredNamingStrategy()
        throws Exception {
      setWroWithValidResources();
  
      final File extraConfigFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());
  
      final Properties props = new Properties();
      // TODO create a properties builder
      props.setProperty(ConfigurableNamingStrategy.KEY, FolderHashEncoderNamingStrategy.ALIAS);
      props.list(new PrintStream(extraConfigFile));
  
      victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
      victim.setExtraConfigFile(extraConfigFile);
      victim.setIgnoreMissingResources(true);
      victim.execute();
  
      FileUtils.deleteQuietly(extraConfigFile);
    }
  
    public static final class ExceptionThrowingWroManagerFactory
        extends DefaultStandaloneContextAwareManagerFactory {
      @Override
      protected ProcessorsFactory newProcessorsFactory() {
        final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
        final ResourcePostProcessor postProcessor = Mockito.mock(ResourcePostProcessor.class);
        try {
          Mockito.doThrow(new RuntimeException()).when(postProcessor).process(Mockito.any(Reader.class),
              Mockito.any(Writer.class));
        } catch (final IOException e) {
          Assert.fail("never happen");
        }
        factory.addPostProcessor(postProcessor);
        return factory;
      }
    }
  
    public static class CustomManagerFactory
        extends DefaultStandaloneContextAwareManagerFactory {
    }
  
    public static final class CustomNamingStrategyWroManagerFactory
        extends DefaultStandaloneContextAwareManagerFactory {
      public static final String PREFIX = "renamed";
      {
        setNamingStrategy(new NamingStrategy() {
          public String rename(final String originalName, final InputStream inputStream)
              throws IOException {
            return PREFIX + originalName;
          }
        });
      }
    }
  
    public static final class CssUrlRewriterWroManagerFactory
        extends DefaultStandaloneContextAwareManagerFactory {
      @Override
      protected ProcessorsFactory newProcessorsFactory() {
        final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
        factory.addPreProcessor(new CssUrlRewritingProcessor());
        return factory;
      }
    }
  
    @Test
    public void shouldDetectIncrementalChange()
        throws Exception {
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory getManagerFactory() {
          return new WroManagerFactoryDecorator(super.getManagerFactory()) {
            @Override
            protected void onBeforeBuild(final Builder builder) {
              builder.setHashStrategy(mockHashStrategy);
            }
          };
        }
      };
      setUpMojo(victim);
      final String hashValue = "SomeHashValue";
      when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(hashValue);
      when(mockBuildContext.isIncremental()).thenReturn(true);
      when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(hashValue);
      victim.setIgnoreMissingResources(true);
      // incremental build detects no change
      assertTrue(victim.getTargetGroupsAsList().isEmpty());
  
      // incremental change detects change for all resources
      when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn("TotallyDifferentValue");
      assertFalse(victim.getTargetGroupsAsList().isEmpty());
    }
  
    @Test
    public void shouldDetectIncrementalChangeOfImportedCss()
        throws Exception {
      final String importResource = "imported.css";
  
      configureMojoForModelWithImportedCssResource(importResource);
      // incremental build detects no change
      assertTrue(victim.getTargetGroupsAsList().isEmpty());
  
      when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
      assertFalse(victim.getTargetGroupsAsList().isEmpty());
    }
  
    private void configureMojoForModelWithImportedCssResource(final String importResource)
        throws Exception {
      final String parentResource = "parent.css";
  
      final WroModel model = new WroModel();
      model.addGroup(new Group("g1").addResource(Resource.create(parentResource)));
      when(mockLocator.locate(Mockito.anyString())).thenAnswer(answerWithContent(""));
      final String parentContent = String.format("@import url(%s)", importResource);
      when(mockLocator.locate(Mockito.eq(parentResource))).thenAnswer(answerWithContent(parentContent));
  
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory newWroManagerFactory()
            throws MojoExecutionException {
          final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
          managerFactory.setUriLocatorFactory(mockLocatorFactory);
          managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
          return managerFactory;
        }
      };
      final HashStrategy hashStrategy = victim.getManagerFactory().create().getHashStrategy();
      setUpMojo(victim);
  
      final String importedInitialContent = "initial";
  
      when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent(importedInitialContent));
      when(mockBuildContext.isIncremental()).thenReturn(true);
      when(mockBuildContext.getValue(Mockito.eq(parentResource))).thenReturn(
          hashStrategy.getHash(new ByteArrayInputStream(parentContent.getBytes())));
      when(mockBuildContext.getValue(Mockito.eq(importResource))).thenReturn(
          hashStrategy.getHash(new ByteArrayInputStream(importedInitialContent.getBytes())));
      victim.setIgnoreMissingResources(true);
    }
  
    @Test
    public void shouldIgnoreChangesOfGroupsWhichAreNotPartOfTargetGroups()
        throws Exception {
      final String importResource = "imported.css";
  
      configureMojoForModelWithImportedCssResource(importResource);
      victim.setTargetGroups("g2");
  
      // incremental build detects no change
      assertTrue(victim.getTargetGroupsAsList().isEmpty());
  
      when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
      assertTrue(victim.getTargetGroupsAsList().isEmpty());
    }
  
    @Test
    public void shouldReuseGroupNameMappingFileWithIncrementalBuild()
        throws Exception {
      final File groupNameMappingFile = WroUtil.createTempFile();
  
      final Resource g1Resource = spy(Resource.create("1.js"));
      try {
        final WroModel model = new WroModel();
        model.addGroup(new Group("g1").addResource(g1Resource));
        model.addGroup(new Group("g2").addResource(Resource.create("2.js")));
        victim = new Wro4jMojo() {
          @Override
          protected WroManagerFactory newWroManagerFactory()
              throws MojoExecutionException {
            final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
            managerFactory.setUriLocatorFactory(WroTestUtils.createResourceMockingLocatorFactory());
            managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
            managerFactory.setNamingStrategy(new DefaultHashEncoderNamingStrategy());
            return managerFactory;
          }
        };
        setUpMojo(victim);
  
        victim.setGroupNameMappingFile(groupNameMappingFile);
  
        assertEquals(2, victim.getTargetGroupsAsList().size());
        victim.execute();
  
        // Now mark it as incremental
        when(mockBuildContext.isIncremental()).thenReturn(true);
  
        final Properties groupNames = new Properties();
        groupNames.load(new FileInputStream(groupNameMappingFile));
        assertEquals(4, groupNames.entrySet().size());
  
        // change the uri of the resource from group a (equivalent to changing its content).
        when(g1Resource.getUri()).thenReturn("1a.js");
  
        assertEquals(1, victim.getTargetGroupsAsList().size());
        victim.execute();
  
        groupNames.load(new FileInputStream(groupNameMappingFile));
        // The number of persisted groupNames should still be unchanged, even though only a single group has been changed
        // after incremental build.
        assertEquals(4, groupNames.entrySet().size());
      } finally {
        FileUtils.deleteQuietly(groupNameMappingFile);
      }
    }
  
    /**
     * Test for the following scenario: when incremental build is performed, only changed resources are processed. Given
     * that there are two target groups, and a resource from only one group is changed incremental build should process
     * only that one group. However, if the targetFolder does not exist, all target groups must be processed.
     */
    @Test
    public void shouldProcessTargetGroupsWhenDestinationFolderDoesNotExist()
        throws Exception {
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory getManagerFactory() {
          return new WroManagerFactoryDecorator(super.getManagerFactory()) {
            @Override
            protected void onBeforeBuild(final Builder builder) {
              builder.setHashStrategy(mockHashStrategy);
            }
          };
        }
      };
      final String constantHash = "hash";
      when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(constantHash);
      setUpMojo(victim);
      victim.setIgnoreMissingResources(true);
  
      final int totalGroups = 10;
  
      assertEquals(totalGroups, victim.getTargetGroupsAsList().size());
  
      when(mockBuildContext.isIncremental()).thenReturn(true);
      when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(constantHash);
  
      assertEquals(0, victim.getTargetGroupsAsList().size());
  
      // delete target folder
      destinationFolder.delete();
  
      assertEquals(totalGroups, victim.getTargetGroupsAsList().size());
  
      victim.doExecute();
    }
  
    private Answer<InputStream> answerWithContent(final String content) {
      return new Answer<InputStream>() {
        public InputStream answer(final InvocationOnMock invocation)
            throws Throwable {
          return new ByteArrayInputStream(content.getBytes());
        }
      };
    }
  
    /**
     * Verify that the plugin execution does not fail when one of the context folder is invalid.
     */
    @Test
    public void shouldUseMultipleContextFolders()
        throws Exception {
      final String defaultContextFolder = victim.getContextFoldersAsCSV();
      victim.setTargetGroups("contextRelative");
  
      victim.setContextFolder("invalid, " + defaultContextFolder);
      victim.doExecute();
  
      // reversed order should work the same
      victim.setContextFolder(defaultContextFolder + ", invalid");
      victim.doExecute();
    }
  
    @Test
    public void shouldSkipExecutionWhenSkipIsEnabled()
        throws Exception {
      victim.setSkip(false);
      try {
        victim.execute();
        fail("should have failed");
      } catch (final MojoExecutionException e) {
      }
      victim.setSkip(true);
      victim.execute();
    }
  
    @After
    public void tearDown()
        throws Exception {
      victim.clean();
      FileUtils.deleteDirectory(destinationFolder);
      FileUtils.deleteDirectory(cssDestinationFolder);
      FileUtils.deleteDirectory(jsDestinationFolder);
      FileUtils.deleteQuietly(extraConfigFile);
    }
  }
  