content: /*******************************************************************************
   * Copyright (C) 2021, 1C-Soft LLC and others.
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   *
   * Contributors:
   *     1C-Soft LLC - initial API and implementation
   *******************************************************************************/
  package com.e1c.v8codestyle.bsl.check.itests;
  
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.List;
  import java.util.stream.Collectors;
  
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.resources.IncrementalProjectBuilder;
  import org.eclipse.core.resources.ResourcesPlugin;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.NullProgressMonitor;
  import org.eclipse.core.runtime.OperationCanceledException;
  import org.eclipse.core.runtime.Path;
  import org.eclipse.core.runtime.jobs.Job;
  import org.junit.Before;
  import org.junit.Ignore;
  
  import com._1c.g5.v8.bm.core.IBmObject;
  import com._1c.g5.v8.dt.bsl.model.Module;
  import com._1c.g5.v8.dt.core.lifecycle.ProjectStopType;
  import com._1c.g5.v8.dt.core.lifecycle.ServiceLifecycleJob;
  import com._1c.g5.v8.dt.core.lifecycle.WorkspaceProjectStopRequest;
  import com._1c.g5.v8.dt.core.platform.IDtProject;
  import com._1c.g5.v8.dt.metadata.mdclass.CommonModule;
  import com._1c.g5.v8.dt.testing.TestingProjectLifecycleSupport;
  import com._1c.g5.v8.dt.validation.marker.Marker;
  import com.e1c.g5.v8.dt.check.ICheck;
  import com.e1c.g5.v8.dt.check.settings.CheckUid;
  import com.e1c.g5.v8.dt.check.settings.ICheckSettings;
  import com.e1c.g5.v8.dt.testing.check.CheckTestBase;
  import com.e1c.v8codestyle.internal.bsl.BslPlugin;
  
  /**
   * Abstract test base which loads a project and updates its single module from a {@code test.bsl} resource if necessary.
   * Default project is {@code CommonModule} that allows to load rest resource to common module.
   *<br><br>
   * Clients may override the name of the project and path to the module, module id, and the way how to get the module
   * (for example manager module, object module, form module and etc).
   *
   * @author Dmitriy Marmyshev
   */
  @Ignore
  public class AbstractSingleModuleTestBase
      extends CheckTestBase
  {
  
      private static final String PROJECT_NAME = "CommonModule";
  
      private static final String FQN = "CommonModule.CommonModule";
  
      private static final String COMMON_MODULE_FILE_NAME = "/src/CommonModules/CommonModule/Module.bsl";
  
      protected static final String FOLDER_RESOURCE = "/resources/";
  
      private IDtProject dtProject;
  
      private Class<? extends ICheck> checkClass;
  
      private ICheck check;
  
      /**
       * Instantiates a new abstract single module test by the class of the check.
       *
       * @param checkClass the check class, cannot be {@code null}.
       */
      protected AbstractSingleModuleTestBase(Class<? extends ICheck> checkClass)
      {
          super();
          this.checkClass = checkClass;
      }
  
      @Override
      protected boolean enableCleanUp()
      {
          return false;
      }
  
      /**
       * Gets the DT project instance.
       *
       * @return the project, cannot return {@code null}.
       */
      protected IDtProject getProject()
      {
          return dtProject;
      }
  
      @Before
      public void setUp() throws CoreException
      {
  
          IProject project = testingWorkspace.getProject(getTestConfigurationName());
  
          if (!project.exists() || !project.isAccessible())
          {
              testingWorkspace.cleanUpWorkspace();
              dtProject = openProjectAndWaitForValidationFinish(getTestConfigurationName());
          }
          dtProject = dtProjectManager.getDtProject(project);
          setCheckEnable(true);
      }
  
      /**
       * Sets the check enable or disable.
       *
       * @param enable the new check enable
       */
      protected void setCheckEnable(boolean enable)
      {
          IProject project = getProject().getWorkspaceProject();
          CheckUid cuid = new CheckUid(getCheckId(), BslPlugin.PLUGIN_ID);
          ICheckSettings settings = checkRepository.getSettings(cuid, project);
          if (settings.isEnabled() != enable)
          {
              settings.setEnabled(enable);
              checkRepository.applyChanges(Collections.singleton(settings), project);
              waitForDD(getProject());
          }
      }
  
      /**
       * Gets the test configuration name, the project name.
       *
       * @return the test configuration name, cannot return {@code null}.
       */
      protected String getTestConfigurationName()
      {
          return PROJECT_NAME;
      }
  
      /**
       * Gets the module instance from the project.
       * <br><br>
       * Clients should override this method if they need to get module form different path
       * then {@code /ProjectName/src/CommonModules/CommonModule/Module.bsl}
       *
       * @return the module, cannot return {@code null}.
       */
      protected Module getModule()
      {
          IBmObject mdObject = getTopObjectByFqn(FQN, getProject());
          assertTrue(mdObject instanceof CommonModule);
          Module module = ((CommonModule)mdObject).getModule();
          assertNotNull(module);
  
          return module;
      }
  
      /**
       * Update project module file from bundle resource path, wait for validation and get the module.
       *
       * @param pathToResource the full path to resource in this bundle, cannot be {@code null}.
       * @return the module after validation, cannot return {@code null}.
       * @throws Exception the exception
       */
      protected Module updateAndGetModule(String pathToResource) throws Exception
      {
          updateModule(pathToResource);
  
          return getModule();
      }
  
      /**
       * Update project module file form bundle resource path and wait until validation finished.
       *
       * @param pathToResource the path to resource
       * @throws Exception the exception
       */
      protected void updateModule(String pathToResource) throws Exception
      {
          IProject project = getProject().getWorkspaceProject();
          IFile file = project.getFile(getModuleFileName());
          try (InputStream in = getClass().getResourceAsStream(pathToResource))
          {
              if (file.exists())
              {
                  file.setContents(in, true, true, new NullProgressMonitor());
              }
              else
              {
                  file.create(in, true, new NullProgressMonitor());
              }
          }
          ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null);
  
          // As well as AUTO_BUILD-family job is being scheduled synchronously
          // So all we need is to wait for auto-build job is being finished
          // And also a little protection from direct file changes (without Eclipse
          // resource subsystem)
          project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
          try
          {
              Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
              Job.getJobManager().join(ResourcesPlugin.FAMILY_MANUAL_BUILD, null);
          }
          catch (OperationCanceledException | InterruptedException e)
          {
              throw new IllegalStateException("Cannot update file:" + file.toString(), e); //$NON-NLS-1$
          }
          waitForDD(getProject());
      }
  
      /**
       * Gets the module first marker for this check.
       *
       * @return the module first marker, may return {@code null} if there is no markers with the check ID.
       */
      protected Marker getModuleFirstMarker()
      {
          return getFirstMarker(getCheckId(), getModuleId(), getProject());
      }
  
      /**
       * Gets the module project relative file path.
       *
       * @return the module file name, cannot return {@code null}.
       */
      protected String getModuleFileName()
      {
          return COMMON_MODULE_FILE_NAME;
      }
  
      /**
       * Gets the module id it is full project resource path.
       *
       * @return the module id, cannot return {@code null}.
       */
      protected String getModuleId()
      {
          return Path.ROOT.append(getTestConfigurationName()).append(getModuleFileName()).toString();
      }
  
      /**
       * Gets the module markers for this check.
       *
       * @return the module markers, cannot return {@code null}.
       */
      protected List<Marker> getModuleMarkers()
      {
          List<Marker> markers = List.of(markerManager.getMarkers(getProject().getWorkspaceProject(), getModuleId()));
  
          String chekcId = getCheckId();
          assertNotNull(chekcId);
  
          CheckUid id = new CheckUid(chekcId, BslPlugin.PLUGIN_ID);
  
          return markers.stream()
              .filter(
                  m -> id.equals(checkRepository.getUidForShortUid(m.getCheckId(), getProject().getWorkspaceProject())))
              .collect(Collectors.toList());
      }
  
      /**
       * Gets the check id gets from the check instance.
       *
       * @return the check id, cannot return {@code null}.
       */
      protected String getCheckId()
      {
          return getCheckInstance().getCheckId();
      }
  
      /**
       * Gets the check instance created in BSL bundle.
       *
       * @return the check instance, cannot return {@code null}.
       */
      protected ICheck getCheckInstance()
      {
          if (check == null)
          {
              check = BslPlugin.getDefault().getInjector().getInstance(checkClass);
          }
          return check;
      }
  
      /**
       * Delete project in the workspace.
       * @param project deleted project, cannot be <code>null</code>
       * @throws CoreException if workspace clean up error occurred
       */
      protected void cleanUpProject(IProject project) throws CoreException
      {
          // First of all we need to stop all DT projects to perform the most effective
          // cleanup, without stray dependent projects restarts and so on
          Collection<WorkspaceProjectStopRequest> stopRequests = new ArrayList<>();
  
          IDtProject dtProject = dtProjectManager.getDtProject(project);
          if (dtProject != null)
          {
              stopRequests.add(new WorkspaceProjectStopRequest(project, ProjectStopType.DELETE));
          }
  
          // Stopping projects in the right order
          orchestrator.stopWorkspaceProjects(stopRequests, new NullProgressMonitor());
  
          // Delete projects physically
  
          try
          {
              project.delete(false, true, null);
              TestingProjectLifecycleSupport.waitForProjectStop(project);
          }
          catch (Throwable e)
          {
              e.printStackTrace(System.err);
              //nop
          }
  
          // Checking if there are critical failures during the shutdown of the project
          try
          {
              // Force closing services that failed during the normal close
              if (dtProject != null && orchestrator.isStarted(dtProject))
              {
                  orchestrator.stopProject(dtProject, ProjectStopType.DELETE);
                  TestingProjectLifecycleSupport.waitForProjectStop(project);
              }
          }
          catch (Throwable e)
          {
              //nop
          }
          try
          {
              testingWorkspace.getWorkspaceRoot().refreshLocal(IResource.DEPTH_INFINITE, null);
          }
          catch (Throwable e)
          {
              e.printStackTrace(System.err);
          }
  
          try
          {
              Job.getJobManager().join(ServiceLifecycleJob.FAMILY, new NullProgressMonitor());
              Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
              Job.getJobManager().join(ResourcesPlugin.FAMILY_MANUAL_BUILD, null);
          }
          catch (OperationCanceledException | InterruptedException e)
          {
              // Nothing
          }
      }
  }
  