gt:     servletContext = mock(ServletContext.class);
context: /*
   * Copyright (c) 2012-2018 Red Hat, Inc.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *   Red Hat, Inc. - initial API and implementation
   */
  package org.eclipse.che.inject;
  
  import static java.io.File.pathSeparator;
  import static java.util.Collections.emptyEnumeration;
  import static org.eclipse.che.commons.test.SystemPropertiesHelper.overrideSystemProperties;
  import static org.eclipse.che.inject.CheBootstrap.CHE_LOCAL_CONF_DIR;
  import static org.eclipse.che.inject.CheBootstrap.PROPERTIES_ALIASES_CONFIG_FILE;
  import static org.mockito.ArgumentMatchers.eq;
  import static org.mockito.Mockito.atLeastOnce;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
  import static org.testng.Assert.assertEquals;
  
  import com.google.common.io.Files;
  import com.google.inject.Inject;
  import com.google.inject.Injector;
  import java.io.File;
  import java.io.IOException;
  import java.io.Writer;
  import java.net.URI;
  import java.net.URL;
  import java.nio.charset.Charset;
  import java.util.Map;
  import java.util.Properties;
  import javax.inject.Named;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletContextEvent;
  import org.eclipse.che.commons.annotation.Nullable;
  import org.eclipse.che.commons.lang.IoUtil;
  import org.eclipse.che.commons.lang.Pair;
  import org.eclipse.che.commons.test.SystemPropertiesHelper;
  import org.mockito.ArgumentCaptor;
  import org.testng.annotations.AfterMethod;
  import org.testng.annotations.BeforeMethod;
  import org.testng.annotations.Test;
  
  public class CheBootstrapTest {
    private CheBootstrap cheBootstrap;
    private ServletContext servletContext;
    private File che;
    private File userCongDir;
    private SystemPropertiesHelper systemPropertiesHelper;
  
    @BeforeMethod
    public void setUp() throws Exception {
      systemPropertiesHelper = overrideSystemProperties();
      cheBootstrap = new CheBootstrap();
      servletContext = mock(ServletContext.class);
  
      URL classesDirUrl = Thread.currentThread().getContextClassLoader().getResource(".");
      File classesDir = new File(classesDirUrl.toURI());
      che = new File(classesDir, "che");
      che.mkdir();
      userCongDir = new File(System.getenv(CHE_LOCAL_CONF_DIR));
      userCongDir.mkdirs();
  
      mockServletContext();
    }
  
    @AfterMethod
    public void tearDown() throws Exception {
      try {
        cheBootstrap.contextDestroyed(new ServletContextEvent(servletContext));
      } catch (Throwable ignored) {
      }
      systemPropertiesHelper.restoreFromBackup();
      IoUtil.deleteRecursive(che);
      IoUtil.deleteRecursive(userCongDir);
      File aliases = new File(che.getParent(), PROPERTIES_ALIASES_CONFIG_FILE);
      if (aliases.exists()) {
        aliases.delete();
      }
      ModuleScanner.modules.clear();
    }
  
    private Properties createTestProperties() {
      final Properties properties = new Properties();
      properties.put("test_int", "123");
      properties.put("test_bool", "true");
      properties.put("test_uri", "file:/a/b/c");
      properties.put("test_url", "http://localhost");
      properties.put("test_file", "/a/b/c");
      properties.put("test_strings", "a, b, c");
      properties.put("test_pair_of_strings", "a=b");
      properties.put("test_pair_of_strings2", "a");
      properties.put("test_pair_of_strings3", "a=");
      properties.put("test_pair_array", "a=b,c=d");
      properties.put("some.dir.in_tmp_dir", "${java.io.tmpdir}/some_dir");
      properties.put("suffixed.PATH", "${PATH}" + pathSeparator + "some_path");
      properties.put("nullable", "NULL");
      return properties;
    }
  
    @Test
    public void readsConfigurationPropertiesFromCheDirectory() throws Exception {
      writePropertiesFile(che, "che.properties", createTestProperties());
  
      ModuleScanner.modules.add(binder -> binder.bind(TestChePropertiesComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestChePropertiesComponent testComponent =
          injector.getInstance(TestChePropertiesComponent.class);
      assertEquals(testComponent.parameter_pair, Pair.of("a", "b"));
      assertEquals(testComponent.parameter_pair2, Pair.of("a", (String) null));
      assertEquals(testComponent.parameter_pair3, Pair.of("a", ""));
      assertEquals(
          testComponent.parameter_pair_array, new Pair[] {Pair.of("a", "b"), Pair.of("c", "d")});
      assertEquals(testComponent.nullable, null);
      assertEquals(testComponent.parameter_uri, new URI("file:/a/b/c"));
      assertEquals(testComponent.parameter_url, new URL("http://localhost"));
      assertEquals(testComponent.parameter_file, new File("/a/b/c"));
      assertEquals(testComponent.parameter_strings, new String[] {"a", "b", "c"});
      assertEquals(testComponent.parameter_int, 123);
      assertEquals(testComponent.parameter_long, 123);
      assertEquals(testComponent.parameter_bool, true);
      assertEquals(
          testComponent.someDir, new File(System.getProperty("java.io.tmpdir"), "/some_dir"));
      assertEquals(testComponent.suffixedPath, System.getenv("PATH") + pathSeparator + "some_path");
    }
  
    @Test
    public void readsConfigurationPropertiesFromSystemProperties() throws Exception {
      setSystemProperties(createTestProperties());
  
      ModuleScanner.modules.add(binder -> binder.bind(TestSystemPropertiesComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestSystemPropertiesComponent testComponent =
          injector.getInstance(TestSystemPropertiesComponent.class);
      assertEquals(testComponent.parameter_pair, Pair.of("a", "b"));
      assertEquals(testComponent.parameter_pair2, Pair.of("a", (String) null));
      assertEquals(testComponent.parameter_pair3, Pair.of("a", ""));
      assertEquals(
          testComponent.parameter_pair_array, new Pair[] {Pair.of("a", "b"), Pair.of("c", "d")});
      assertEquals(testComponent.nullable, null);
      assertEquals(testComponent.parameter_uri, new URI("file:/a/b/c"));
      assertEquals(testComponent.parameter_url, new URL("http://localhost"));
      assertEquals(testComponent.parameter_file, new File("/a/b/c"));
      assertEquals(testComponent.parameter_strings, new String[] {"a", "b", "c"});
      assertEquals(testComponent.parameter_int, 123);
      assertEquals(testComponent.parameter_long, 123);
      assertEquals(testComponent.parameter_bool, true);
      assertEquals(
          testComponent.someDir, new File(System.getProperty("java.io.tmpdir"), "/some_dir"));
      assertEquals(testComponent.suffixedPath, System.getenv("PATH") + pathSeparator + "some_path");
    }
  
    @Test
    public void readsConfigurationPropertiesFromEnvProperties() throws Exception {
      ModuleScanner.modules.add(binder -> binder.bind(TestEnvPropertiesComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestEnvPropertiesComponent testComponent =
          injector.getInstance(TestEnvPropertiesComponent.class);
      assertEquals(testComponent.string, System.getenv("PATH"));
    }
  
    @Test
    public void propertiesFromUserSpecifiedLocationOverrideCheProperties() throws Exception {
      systemPropertiesHelper.property(CHE_LOCAL_CONF_DIR, userCongDir.getAbsolutePath());
  
      Properties cheProperties = new Properties();
      cheProperties.put("che.some.name", "che_value");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties userProperties = new Properties();
      userProperties.put("che.some.name", "user_value");
      writePropertiesFile(userCongDir, "user.properties", userProperties);
  
      ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class);
      assertEquals(testComponent.string, "user_value");
    }
  
    @Test
    public void system_properties_prefixed_with_che_dot_override_user_specified_and_che_properties()
        throws Exception {
      Properties cheProperties = new Properties();
      cheProperties.put("che.some.name", "che_value");
      cheProperties.put("che.some.other.name", "NULL");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties userProperties = new Properties();
      userProperties.put("che.some.name", "user_value");
      writePropertiesFile(userCongDir, "user.properties", userProperties);
  
      systemPropertiesHelper.property("che.some.name", "che_dot_system_property_value");
  
      ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class);
      assertEquals(testComponent.string, "che_dot_system_property_value");
    }
  
    @Test
    public void
        environment_variables_prefixed_with_che_underscore_override_che_dot_prefixed_system_and_user_specified_and_che_properties()
            throws Exception {
      Properties cheProperties = new Properties();
      cheProperties.put("che.some.other.name", "che_value");
      cheProperties.put("che.some.name", "NULL");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties userProperties = new Properties();
      userProperties.put("che.some.other.name", "user_value");
      writePropertiesFile(userCongDir, "user.properties", userProperties);
  
      systemPropertiesHelper.property("che.some.other.name", "che_dot_system_property_value");
  
      ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class);
      assertEquals(testComponent.otherString, System.getenv("CHE_SOME_OTHER_NAME"));
    }
  
    @Test
    public void
        environment_variables_prefixed_with_che_underscore_convert_double_underscores_into_one_underscore_in_variable_name()
            throws Exception {
      Properties cheProperties = new Properties();
      cheProperties.put("che.some.other.name_with_underscores", "che_value");
      cheProperties.put("che.some.name", "NULL");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties userProperties = new Properties();
      userProperties.put("che.some.other.name_with_underscores", "user_value");
      writePropertiesFile(userCongDir, "user.properties", userProperties);
  
      systemPropertiesHelper.property(
          "che.some.other.name_with_underscores", "che_dot_system_property_value");
  
      ModuleScanner.modules.add(
          binder -> binder.bind(TestConfOverrideWithUnderscoresComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfOverrideWithUnderscoresComponent testComponent =
          injector.getInstance(TestConfOverrideWithUnderscoresComponent.class);
      assertEquals(
          testComponent.otherString, System.getenv("CHE_SOME_OTHER_NAME__WITH__UNDERSCORES"));
    }
  
    @Test
    public void processesPropertyAliases() throws Exception {
      Properties cheProperties = new Properties();
      cheProperties.put("very.new.some.name", "some_value");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties aliases = new Properties();
      aliases.put("very.new.some.name", "new.some.name, che.some.name");
      writePropertiesFile(che.getParentFile(), PROPERTIES_ALIASES_CONFIG_FILE, aliases);
  
      ModuleScanner.modules.add(binder -> binder.bind(TestConfAliasComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfAliasComponent testComponent = injector.getInstance(TestConfAliasComponent.class);
      assertEquals(testComponent.string, "some_value");
      assertEquals(testComponent.otherString, "some_value");
      assertEquals(testComponent.otherOtherString, "some_value");
    }
  
    @Test
    public void processesOld2NewPropertyAliases() throws Exception {
      Properties cheProperties = new Properties();
      cheProperties.put("che.some.name", "some_value");
      writePropertiesFile(che, "che.properties", cheProperties);
  
      Properties aliases = new Properties();
      aliases.put("very.new.some.name", "new.some.name, che.some.name");
      writePropertiesFile(che.getParentFile(), PROPERTIES_ALIASES_CONFIG_FILE, aliases);
  
      ModuleScanner.modules.add(binder -> binder.bind(TestConfAliasComponent.class));
  
      cheBootstrap.contextInitialized(new ServletContextEvent(servletContext));
  
      Injector injector = retrieveComponentFromServletContext(Injector.class);
  
      TestConfAliasComponent testComponent = injector.getInstance(TestConfAliasComponent.class);
      assertEquals(testComponent.string, "some_value");
      assertEquals(testComponent.otherString, "some_value");
      assertEquals(testComponent.otherOtherString, "some_value");
    }
  
    static class TestChePropertiesComponent {
      @Named("test_int")
      @Inject
      int parameter_int;
  
      @Named("test_int")
      @Inject
      int parameter_long;
  
      @Named("test_bool")
      @Inject
      boolean parameter_bool;
  
      @Named("test_uri")
      @Inject
      URI parameter_uri;
  
      @Named("test_url")
      @Inject
      URL parameter_url;
  
      @Named("test_file")
      @Inject
      File parameter_file;
  
      @Named("test_strings")
      @Inject
      String[] parameter_strings;
  
      @Named("test_pair_of_strings")
      @Inject
      Pair<String, String> parameter_pair;
  
      @Named("test_pair_of_strings2")
      @Inject
      Pair<String, String> parameter_pair2;
  
      @Named("test_pair_of_strings3")
      @Inject
      Pair<String, String> parameter_pair3;
  
      @Named("test_pair_array")
      @Inject
      Pair<String, String>[] parameter_pair_array;
  
      @Named("some.dir.in_tmp_dir")
      @Inject
      File someDir;
  
      @Named("suffixed.PATH")
      @Inject
      String suffixedPath;
  
      @Named("nullable")
      @Inject
      @Nullable
      String nullable;
    }
  
    static class TestSystemPropertiesComponent {
      @Named("sys.test_int")
      @Inject
      int parameter_int;
  
      @Named("sys.test_int")
      @Inject
      int parameter_long;
  
      @Named("sys.test_bool")
      @Inject
      boolean parameter_bool;
  
      @Named("sys.test_uri")
      @Inject
      URI parameter_uri;
  
      @Named("sys.test_url")
      @Inject
      URL parameter_url;
  
      @Named("sys.test_file")
      @Inject
      File parameter_file;
  
      @Named("sys.test_strings")
      @Inject
      String[] parameter_strings;
  
      @Named("sys.test_pair_of_strings")
      @Inject
      Pair<String, String> parameter_pair;
  
      @Named("sys.test_pair_of_strings2")
      @Inject
      Pair<String, String> parameter_pair2;
  
      @Named("sys.test_pair_of_strings3")
      @Inject
      Pair<String, String> parameter_pair3;
  
      @Named("sys.test_pair_array")
      @Inject
      Pair<String, String>[] parameter_pair_array;
  
      @Named("sys.some.dir.in_tmp_dir")
      @Inject
      File someDir;
  
      @Named("sys.suffixed.PATH")
      @Inject
      String suffixedPath;
  
      @Named("sys.nullable")
      @Inject
      @Nullable
      String nullable;
    }
  
    static class TestEnvPropertiesComponent {
      @Named("env.PATH")
      @Inject
      String string;
    }
  
    static class TestConfOverrideComponent {
      @Named("che.some.name")
      @Inject
      @Nullable
      String string;
  
      @Named("che.some.other.name")
      @Inject
      @Nullable
      String otherString;
    }
  
    static class TestConfOverrideWithUnderscoresComponent {
      @Named("che.some.name")
      @Inject
      @Nullable
      String string;
  
      @Named("che.some.other.name_with_underscores")
      @Inject
      @Nullable
      String otherString;
    }
  
    static class TestConfAliasComponent {
      @Named("che.some.name")
      @Inject
      String string;
  
      @Named("new.some.name")
      @Inject
      String otherString;
  
      @Named("very.new.some.name")
      @Inject
      String otherOtherString;
    }
  
    private void writePropertiesFile(File parent, String name, Properties properties)
        throws IOException {
      File propertiesFile = new File(parent, name);
      try (Writer writer = Files.newWriter(propertiesFile, Charset.forName("UTF-8"))) {
        properties.store(writer, null);
      }
    }
  
    private void setSystemProperties(Properties properties) throws IOException {
      for (Map.Entry<Object, Object> entry : properties.entrySet()) {
        systemPropertiesHelper.property((String) entry.getKey(), (String) entry.getValue());
      }
    }
  
    private void mockServletContext() {
  