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
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
;
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
;
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
) {
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.
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
)) {
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).
108 * The path of the archive containing the Git repository
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
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()) {
138 connectedProjects
.add(project
);
141 projects
= connectedProjects
.toArray(new IProject
[connectedProjects
.size()]);
145 * Connect a project to the Git repository.
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());
160 * Connect the Git repository to JGit.
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.
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$
186 } else if (child
.isDirectory()) {
187 File findGitDir
= findGitDir(child
);
188 if (findGitDir
!= null) {
198 * Import the project located in the given path into the test workspace.
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.
229 * The path to the archive (relative to the test class)
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
);
247 File dir
= new File(filePath
);
253 return extractedPaths
;
257 * Extract the given input stream to the given location.
260 * The zip input stream
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
];
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
) {
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
);