Make sure folders are created when extracting input files
[EMFCompare2.git] / plugins / org.eclipse.emf.compare.ide.ui.tests.git.framework / src / org / eclipse / emf / compare / ide / ui / tests / git / framework / internal / statements / InternalGitTestSupport.java
blob690ce1d6c31a5e5bb3fdbd741efcb59e3755f4f3
1 /*******************************************************************************
2 * Copyright (c) 2016 Obeo and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Obeo - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.emf.compare.ide.ui.tests.git.framework.internal.statements;
13 import java.io.BufferedOutputStream;
14 import java.io.File;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.lang.reflect.InvocationTargetException;
19 import java.nio.file.Files;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Comparator;
24 import java.util.List;
25 import java.util.stream.Stream;
26 import java.util.zip.ZipEntry;
27 import java.util.zip.ZipInputStream;
29 import org.eclipse.core.resources.IProject;
30 import org.eclipse.core.resources.IProjectDescription;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.resources.IWorkspaceRoot;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IPath;
36 import org.eclipse.core.runtime.NullProgressMonitor;
37 import org.eclipse.core.runtime.Path;
38 import org.eclipse.egit.core.Activator;
39 import org.eclipse.egit.core.op.ConnectProviderOperation;
40 import org.eclipse.egit.core.op.DisconnectProviderOperation;
41 import org.eclipse.egit.core.project.RepositoryFinder;
42 import org.eclipse.egit.core.project.RepositoryMapping;
43 import org.eclipse.jgit.lib.Repository;
44 import org.eclipse.ui.dialogs.IOverwriteQuery;
45 import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
46 import org.eclipse.ui.wizards.datatransfer.ImportOperation;
48 /**
49 * This class contains utility methods to perform git tests. Those methods are internal and are not intended
50 * to be used by clients.
52 * @author <a href="mailto:mathieu.cartaud@obeo.fr">Mathieu Cartaud</a>
54 @SuppressWarnings({"restriction" })
55 public class InternalGitTestSupport {
57 /** The prefix of Git branches. */
58 private static final String GIT_BRANCH_PREFIX = "refs/heads/"; //$NON-NLS-1$
60 /** Name of the Eclipse metadata folder. */
61 private static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$
63 /** Name of the git metadata folder. */
64 private static final String GIT_FOLDER = ".git"; //$NON-NLS-1$
66 /** Size of the buffer to read/write data. */
67 private static final int BUFFER_SIZE = 4096;
69 /** The JGit repository. */
70 protected Repository repository;
72 /** The list of projects in the repository. */
73 protected IProject[] projects;
75 /** The list of JGit disposers. */
76 protected ArrayList<Runnable> disposers;
78 /** Query used to specify if the projects import must override existing projects. */
79 private IOverwriteQuery overwriteQuery = new IOverwriteQuery() {
80 public String queryOverwrite(String file) {
81 return ALL;
85 /**
86 * Adapt the given <code>branch</code> name to be in a correct format for Git or return it directly if it
87 * is already the case.
89 * @param branch
90 * The branch name we want to normalize
91 * @return the branch in Git format
93 protected static String normalizeBranch(String branch) {
94 if (branch.startsWith(GIT_BRANCH_PREFIX)) {
95 return branch;
96 } else {
97 return GIT_BRANCH_PREFIX + branch;
102 * Perform all loading actions (unzip the archive, connect the git repository to JGit, import the
103 * contained projects in the workspace and connect them through JGit).
105 * @param clazz
106 * The test class
107 * @param path
108 * The path of the archive containing the Git repository
109 * @throws Exception
110 * If something goes wrong during unzip or import steps
112 protected void createRepositoryFromPath(Class<?> clazz, String path) throws Exception {
113 final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
114 // Delete all projects that can remain in the workspace : prevent errors dues to wrong cleanup of
115 // other tests
116 root.delete(true, new NullProgressMonitor());
117 IPath location = root.getLocation();
118 List<String> extractedPaths = extractArchive(clazz, path, root);
119 for (String extractedPath : extractedPaths) {
120 File extractedFile = new File(extractedPath);
121 if (!extractedFile.getPath().contains(File.separatorChar + METADATA_FOLDER + File.separatorChar)
122 && !extractedFile.getPath()
123 .contains(File.separatorChar + GIT_FOLDER + File.separatorChar)) {
124 if (".project".equals(extractedFile.getName())) { //$NON-NLS-1$
125 importProject(extractedFile);
129 connectRepository(new File(location.toString()));
130 List<IProject> existingProjects = Arrays.asList(root.getProjects());
131 List<IProject> connectedProjects = new ArrayList<>();
132 for (IProject project : existingProjects) {
133 RepositoryFinder finder = new RepositoryFinder(project);
134 finder.setFindInChildren(false);
135 Collection<RepositoryMapping> repos = finder.find(new NullProgressMonitor());
136 if (!repos.isEmpty()) {
137 connect(project);
138 connectedProjects.add(project);
141 projects = connectedProjects.toArray(new IProject[connectedProjects.size()]);
145 * Connect a project to the Git repository.
147 * @param project
148 * The project to connect
149 * @throws CoreException
150 * Thrown if the project cannot be connected to the workspace
151 * @throws InterruptedException
152 * Thrown if the operation is interrupted
154 private void connect(IProject project) throws CoreException, InterruptedException {
155 ConnectProviderOperation op = new ConnectProviderOperation(project, repository.getDirectory());
156 op.execute(null);
160 * Connect the Git repository to JGit.
162 * @param file
163 * The path to the root of the repository
164 * @throws IOException
165 * If their is a problem during the connection to JGit
167 private void connectRepository(File file) throws IOException {
168 File gitDir = findGitDir(file);
169 this.repository = Activator.getDefault().getRepositoryCache().lookupRepository(gitDir);
170 this.disposers = new ArrayList<Runnable>();
174 * Find the ".git" folder in the repository.
176 * @param file
177 * The path of the root of the unziped files
178 * @return The path to the .git folder
180 private File findGitDir(File file) {
181 File[] listFiles = file.listFiles();
182 if (listFiles != null) {
183 for (File child : listFiles) {
184 if (child.isDirectory() && child.getName().equals(".git")) { //$NON-NLS-1$
185 return child;
186 } else if (child.isDirectory()) {
187 File findGitDir = findGitDir(child);
188 if (findGitDir != null) {
189 return findGitDir;
194 return null;
198 * Import the project located in the given path into the test workspace.
200 * @param file
201 * The path to the .project file
202 * @throws InvocationTargetException
203 * Thrown if an error happen during the import of the project
204 * @throws InterruptedException
205 * Thrown if the import operation is interrupted
206 * @throws CoreException
207 * Thrown if the project cannot be created in the workspace
209 private void importProject(File file)
210 throws InvocationTargetException, InterruptedException, CoreException {
211 IProjectDescription description = ResourcesPlugin.getWorkspace()
212 .loadProjectDescription(new Path(file.getAbsolutePath()));
213 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(description.getName());
214 project.create(description, new NullProgressMonitor());
215 project.open(new NullProgressMonitor());
217 ImportOperation importOperation = new ImportOperation(project.getFullPath(), file.getParentFile(),
218 FileSystemStructureProvider.INSTANCE, overwriteQuery);
219 importOperation.setCreateContainerStructure(false);
220 importOperation.run(new NullProgressMonitor());
224 * Extract the zip file into the given workspace.
226 * @param clazz
227 * The test class
228 * @param path
229 * The path to the archive (relative to the test class)
230 * @param root
231 * The root of the test workspace
232 * @return The list of files extracted from this archive. Does not include folders.
233 * @throws IOException
234 * Thrown if the zip extraction goes wrong
236 private List<String> extractArchive(Class<?> clazz, String path, IWorkspaceRoot root) throws IOException {
237 List<String> extractedPaths = new ArrayList<>();
238 try (InputStream resourceAsStream = clazz.getResourceAsStream(path);
239 ZipInputStream zipIn = new ZipInputStream(resourceAsStream);) {
240 ZipEntry entry = null;
241 while ((entry = zipIn.getNextEntry()) != null) {
242 String filePath = root.getLocation() + File.separator + entry.getName();
243 if (!entry.isDirectory()) {
244 extractFile(zipIn, filePath);
245 extractedPaths.add(filePath);
246 } else {
247 File dir = new File(filePath);
248 dir.mkdir();
250 zipIn.closeEntry();
253 return extractedPaths;
257 * Extract the given input stream to the given location.
259 * @param zipIn
260 * The zip input stream
261 * @param filePath
262 * The destination path for the extraction
263 * @throws IOException
264 * Thrown if something happen during the extraction
266 private void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
267 File file = new File(filePath);
268 file.getParentFile().mkdirs();
269 try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));) {
270 byte[] bytesIn = new byte[BUFFER_SIZE];
271 int read = 0;
272 while ((read = zipIn.read(bytesIn)) != -1) {
273 bos.write(bytesIn, 0, read);
279 * Clean all possibly remaining elements to start the test in a clean state.
281 * @throws CoreException
282 * Thrown if a project cannot be deleted
283 * @throws IOException
284 * Thrown if a file cannot be deleted
286 protected void setup() throws CoreException, IOException {
287 final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
288 IProject[] unknownProjects = workspaceRoot.getProjects();
289 if (unknownProjects != null && unknownProjects.length > 0) {
290 for (IProject iProject : unknownProjects) {
291 iProject.delete(true, new NullProgressMonitor());
294 Activator.getDefault().getRepositoryCache().clear();
296 File file = new File(workspaceRoot.getLocation().toOSString());
297 File[] listFiles = file.listFiles();
298 if (listFiles != null) {
299 for (File child : listFiles) {
300 if (!child.getName().equals(METADATA_FOLDER)) {
301 try (Stream<java.nio.file.Path> walk = Files.walk(child.toPath())) {
302 walk.sorted(Comparator.reverseOrder()).map(java.nio.file.Path::toFile)
303 .forEach(File::delete);
311 * Clear workspace and repository for next tests.
313 * @throws CoreException
314 * Thrown if a project cannot be deleted
315 * @throws IOException
316 * Thrown if a file cannot be deleted
318 protected void tearDown() throws CoreException, IOException {
319 if (disposers != null) {
320 for (Runnable disposer : disposers) {
321 disposer.run();
323 disposers.clear();
326 Activator.getDefault().getRepositoryCache().clear();
328 if (projects != null) {
329 List<IProject> disconnectMe = new ArrayList<>();
330 for (IProject iProject : projects) {
331 disconnectMe.add(iProject);
333 new DisconnectProviderOperation(disconnectMe).execute(null);
335 // Delete sub-projects first
336 List<IProject> allProjects = new ArrayList<>(
337 Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()));
338 allProjects.sort((p1, p2) -> p2.getLocation().toString().compareTo(p1.getLocation().toString()));
340 for (IProject iProject : allProjects) {
341 if (iProject.isAccessible()) {
342 iProject.delete(true, new NullProgressMonitor());
345 ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE,
346 new NullProgressMonitor());
349 File file = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
350 File[] listFiles = file.listFiles();
351 if (listFiles != null) {
352 for (File child : listFiles) {
353 if (!child.getName().equals(METADATA_FOLDER)) {
354 try (Stream<java.nio.file.Path> walk = Files.walk(child.toPath())) {
355 walk.sorted(Comparator.reverseOrder()).map(java.nio.file.Path::toFile)
356 .forEach(File::delete);