1 /*******************************************************************************
2 * Copyright (c) 2010, 2014 SAP AG 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 * Mathias Kinzler (SAP AG) - initial implementation
10 *******************************************************************************/
11 package org
.eclipse
.egit
.ui
.test
;
13 import static org
.eclipse
.swtbot
.eclipse
.finder
.waits
.Conditions
.waitForView
;
14 import static org
.junit
.Assert
.assertEquals
;
15 import static org
.junit
.Assert
.assertNotNull
;
16 import static org
.junit
.Assert
.assertTrue
;
17 import static org
.junit
.Assert
.fail
;
20 import java
.io
.FileOutputStream
;
21 import java
.io
.IOException
;
22 import java
.io
.OutputStreamWriter
;
23 import java
.io
.Writer
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Arrays
;
26 import java
.util
.HashMap
;
27 import java
.util
.HashSet
;
28 import java
.util
.List
;
29 import java
.util
.Locale
;
30 import java
.util
.MissingResourceException
;
31 import java
.util
.ResourceBundle
;
33 import java
.util
.StringTokenizer
;
35 import org
.eclipse
.core
.net
.proxy
.IProxyService
;
36 import org
.eclipse
.core
.runtime
.jobs
.Job
;
37 import org
.eclipse
.egit
.ui
.Activator
;
38 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitHelper
;
39 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitHelper
.CommitInfo
;
40 import org
.eclipse
.jgit
.lib
.ConfigConstants
;
41 import org
.eclipse
.jgit
.lib
.Constants
;
42 import org
.eclipse
.jgit
.lib
.ObjectId
;
43 import org
.eclipse
.jgit
.lib
.Repository
;
44 import org
.eclipse
.jgit
.lib
.StoredConfig
;
45 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
46 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
47 import org
.eclipse
.jgit
.treewalk
.TreeWalk
;
48 import org
.eclipse
.jgit
.util
.StringUtils
;
49 import org
.eclipse
.osgi
.service
.localization
.BundleLocalization
;
50 import org
.eclipse
.swt
.widgets
.Display
;
51 import org
.eclipse
.swt
.widgets
.Shell
;
52 import org
.eclipse
.swtbot
.eclipse
.finder
.SWTWorkbenchBot
;
53 import org
.eclipse
.swtbot
.eclipse
.finder
.widgets
.SWTBotEditor
;
54 import org
.eclipse
.swtbot
.eclipse
.finder
.widgets
.SWTBotView
;
55 import org
.eclipse
.swtbot
.swt
.finder
.SWTBot
;
56 import org
.eclipse
.swtbot
.swt
.finder
.exceptions
.WidgetNotFoundException
;
57 import org
.eclipse
.swtbot
.swt
.finder
.waits
.ICondition
;
58 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotShell
;
59 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotTable
;
60 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotTree
;
61 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotTreeItem
;
62 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.TimeoutException
;
63 import org
.eclipse
.ui
.IViewReference
;
64 import org
.eclipse
.ui
.IWorkbenchPage
;
65 import org
.eclipse
.ui
.IWorkbenchWindow
;
66 import org
.eclipse
.ui
.PartInitException
;
67 import org
.eclipse
.ui
.PlatformUI
;
68 import org
.hamcrest
.BaseMatcher
;
69 import org
.hamcrest
.Description
;
70 import org
.hamcrest
.Matcher
;
71 import org
.hamcrest
.TypeSafeMatcher
;
72 import org
.osgi
.framework
.BundleContext
;
73 import org
.osgi
.framework
.ServiceReference
;
74 import org
.osgi
.util
.tracker
.ServiceTracker
;
77 * Utilities to be used by SWTBot tests
79 public class TestUtil
{
81 public final static String TESTAUTHOR
= "Test Author <test.author@test.com>";
83 public final static String TESTCOMMITTER
= "Test Committer <test.committer@test.com>";
85 public final static String TESTCOMMITTER_NAME
= "Test Committer";
87 public final static String TESTCOMMITTER_EMAIL
= "test.committer@test.com";
89 private final static char AMPERSAND
= '&';
91 private ResourceBundle myBundle
;
94 * Allows access to the localized values of the EGit UI Plug-in
96 * This will effectively read the plugin.properties. Ampersands (often used
97 * in menu items and field labels for keyboard shortcuts) will be filtered
98 * out (see also {@link #getPluginLocalizedValue(String, boolean)} in order
99 * to be able to reference these fields using SWTBot).
102 * the key, must not be null
103 * @return the localized value in the current default {@link Locale}, or
105 * @throws MissingResourceException
106 * if no value is found for the given key
108 public synchronized String
getPluginLocalizedValue(String key
)
109 throws MissingResourceException
{
110 return getPluginLocalizedValue(key
, false);
114 * Allows access to the localized values of the EGit UI Plug-in
118 * see {@link #getPluginLocalizedValue(String)}
119 * @param keepAmpersands
120 * if <code>true</code>, ampersands will be kept
121 * @return see {@link #getPluginLocalizedValue(String)}
122 * @throws MissingResourceException
123 * see {@link #getPluginLocalizedValue(String)}
125 public synchronized String
getPluginLocalizedValue(String key
,
126 boolean keepAmpersands
) throws MissingResourceException
{
127 if (myBundle
== null) {
129 BundleContext context
= Activator
.getDefault().getBundle()
132 ServiceTracker
<BundleLocalization
, BundleLocalization
> localizationTracker
=
133 new ServiceTracker
<BundleLocalization
, BundleLocalization
>(
134 context
, BundleLocalization
.class, null);
135 localizationTracker
.open();
137 BundleLocalization location
= localizationTracker
.getService();
138 if (location
!= null)
139 myBundle
= location
.getLocalization(Activator
.getDefault()
140 .getBundle(), Locale
.getDefault().toString());
142 if (myBundle
!= null) {
143 String raw
= myBundle
.getString(key
);
145 if (keepAmpersands
|| raw
.indexOf(AMPERSAND
) < 0)
148 StringBuilder sb
= new StringBuilder(raw
.length());
149 for (int i
= 0; i
< raw
.length(); i
++) {
150 char c
= raw
.charAt(i
);
154 return sb
.toString();
160 * Utility for waiting until the execution of jobs of a given
161 * family has finished.
163 * @throws InterruptedException
165 public static void joinJobs(Object family
) throws InterruptedException
{
166 Job
.getJobManager().join(family
, null);
170 * Process all queued UI events. If called from background thread, blocks
171 * until all pending events are processed in UI thread.
173 public static void processUIEvents() {
174 if (Display
.getCurrent() != null) {
175 while (Display
.getCurrent().readAndDispatch()) {
176 // process queued ui events
179 // synchronously refresh UI
180 PlatformUI
.getWorkbench().getDisplay().syncExec(new Runnable() {
189 * Appends content to given file.
194 * if true, then bytes will be written to the end of the file
195 * rather than the beginning
196 * @throws IOException
198 public static void appendFileContent(File file
, String content
, boolean append
)
202 fw
= new OutputStreamWriter(new FileOutputStream(file
, append
),
212 * Waits until the given tree has a node whose label contains text
217 * @throws TimeoutException
219 public static void waitUntilTreeHasNodeContainsText(SWTBot bot
,
220 final SWTBotTree tree
, final String text
, long timeout
)
221 throws TimeoutException
{
222 bot
.waitUntil(new ICondition() {
224 public boolean test() throws Exception
{
225 for (SWTBotTreeItem item
: tree
.getAllItems())
226 if (item
.getText().contains(text
))
231 public void init(SWTBot bot2
) {
235 public String
getFailureMessage() {
242 * Waits until the given tree item has a node whose label contains text
247 * @throws TimeoutException
249 public static void waitUntilTreeHasNodeContainsText(SWTBot bot
,
250 final SWTBotTreeItem treeItem
, final String text
, long timeout
)
251 throws TimeoutException
{
252 bot
.waitUntil(new ICondition() {
254 public boolean test() throws Exception
{
255 for (SWTBotTreeItem item
: treeItem
.getItems())
256 if (item
.getText().contains(text
))
261 public void init(SWTBot bot2
) {
265 public String
getFailureMessage() {
272 * Waits until the given tree item has a selected node with the given text
278 * @throws TimeoutException
280 public static void waitUntilTreeHasSelectedNodeWithText(SWTBot bot
,
281 final SWTBotTree tree
, final String text
, long timeout
)
282 throws TimeoutException
{
283 bot
.waitUntil(new ICondition() {
285 public boolean test() throws Exception
{
286 return tree
.selection().get(0, 0).equals(text
);
289 public void init(SWTBot bot2
) {
293 public String
getFailureMessage() {
300 * Waits until the given table has an item with the given text
305 * @throws TimeoutException
307 public static void waitUntilTableHasRowWithText(SWTBot bot
, final SWTBotTable table
,
308 final String text
, long timeout
) throws TimeoutException
{
309 bot
.waitUntil(new ICondition() {
311 public boolean test() throws Exception
{
312 if (table
.indexOf(text
)<0)
317 public void init(SWTBot bot2
) {
321 public String
getFailureMessage() {
327 public static void waitUntilEditorIsActive(SWTWorkbenchBot bot
,
328 final SWTBotEditor editor
, long timeout
) {
329 bot
.waitUntil(new ICondition() {
331 public boolean test() throws Exception
{
332 return editor
.isActive();
335 public void init(SWTBot bot2
) {
339 public String
getFailureMessage() {
346 * Disables usage of proxy servers
348 public static void disableProxy() {
349 BundleContext context
= Activator
.getDefault().getBundle().getBundleContext();
350 ServiceReference
<IProxyService
> serviceReference
= context
.getServiceReference(IProxyService
.class);
351 IProxyService proxyService
= context
.getService(serviceReference
);
352 proxyService
.setSystemProxiesEnabled(false);
353 proxyService
.setProxiesEnabled(false);
356 // TODO: this method is both needed by UI tests and Core tests
357 // provide a common base for UI tests and core tests
359 * verifies that repository contains exactly the given files.
364 public static void assertRepositoryContainsFiles(Repository repository
,
365 String
[] paths
) throws Exception
{
366 Set
<String
> expectedfiles
= new HashSet
<String
>();
367 for (String path
: paths
)
368 expectedfiles
.add(path
);
369 try (TreeWalk treeWalk
= new TreeWalk(repository
)) {
370 treeWalk
.addTree(repository
.resolve("HEAD^{tree}"));
371 treeWalk
.setRecursive(true);
372 while (treeWalk
.next()) {
373 String path
= treeWalk
.getPathString();
374 if (!expectedfiles
.contains(path
))
375 fail("Repository contains unexpected expected file "
377 expectedfiles
.remove(path
);
380 if (expectedfiles
.size() > 0) {
381 StringBuilder message
= new StringBuilder(
382 "Repository does not contain expected files: ");
383 for (String path
: expectedfiles
) {
384 message
.append(path
);
387 fail(message
.toString());
392 * verifies that repository contains exactly the given files with the given
393 * content. Usage example:<br>
396 * assertRepositoryContainsFiles(repository, "foo/a.txt", "content of A",
397 * "foo/b.txt", "content of B")
403 public static void assertRepositoryContainsFilesWithContent(Repository repository
,
404 String
... args
) throws Exception
{
405 HashMap
<String
, String
> expectedfiles
= mkmap(args
);
406 try (TreeWalk treeWalk
= new TreeWalk(repository
)) {
407 treeWalk
.addTree(repository
.resolve("HEAD^{tree}"));
408 treeWalk
.setRecursive(true);
409 while (treeWalk
.next()) {
410 String path
= treeWalk
.getPathString();
411 assertTrue(expectedfiles
.containsKey(path
));
412 ObjectId objectId
= treeWalk
.getObjectId(0);
413 byte[] expectedContent
= expectedfiles
.get(path
)
415 byte[] repoContent
= treeWalk
.getObjectReader().open(objectId
)
417 if (!Arrays
.equals(repoContent
, expectedContent
))
418 fail("File " + path
+ " has repository content "
419 + new String(repoContent
, "UTF-8")
420 + " instead of expected content "
421 + new String(expectedContent
, "UTF-8"));
422 expectedfiles
.remove(path
);
425 if (expectedfiles
.size() > 0) {
426 StringBuilder message
= new StringBuilder(
427 "Repository does not contain expected files: ");
428 for (String path
: expectedfiles
.keySet()) {
429 message
.append(path
);
432 fail(message
.toString());
436 private static HashMap
<String
, String
> mkmap(String
... args
) {
437 if ((args
.length
% 2) > 0)
438 throw new IllegalArgumentException("needs to be pairs");
439 HashMap
<String
, String
> map
= new HashMap
<String
, String
>();
440 for (int i
= 0; i
< args
.length
; i
+= 2)
441 map
.put(args
[i
], args
[i
+1]);
446 * @param projectExplorerTree
449 * @return the project item pertaining to the project
451 public SWTBotTreeItem
[] getProjectItems(SWTBotTree projectExplorerTree
,
452 String
... projects
) {
453 List
<SWTBotTreeItem
> items
= new ArrayList
<SWTBotTreeItem
>();
454 for (SWTBotTreeItem item
: projectExplorerTree
.getAllItems()) {
455 String itemText
= item
.getText();
456 StringTokenizer tok
= new StringTokenizer(itemText
, " ");
457 String name
= tok
.nextToken();
458 // may be a dirty marker
459 if (name
.equals(">"))
460 name
= tok
.nextToken();
461 for (String project
: projects
)
462 if (project
.equals(name
))
465 return items
.isEmpty() ?
null : items
.toArray(new SWTBotTreeItem
[items
.size()]);
470 * @param childNodeText
471 * @return child node containing childNodeText
472 * @see #getNode(SWTBotTreeItem[], String)
474 public static SWTBotTreeItem
getChildNode(SWTBotTreeItem node
,
475 String childNodeText
) {
476 return getNode(node
.getItems(), childNodeText
);
480 * Finds the node that contains the given text. Throws a nice message in
481 * case the item is not found or more than one matching node was found.
485 * @return node containing the text
487 public static SWTBotTreeItem
getNode(SWTBotTreeItem
[] nodes
, String searchText
) {
488 List
<String
> texts
= new ArrayList
<String
>();
489 List
<SWTBotTreeItem
> matchingItems
= new ArrayList
<SWTBotTreeItem
>();
491 for (SWTBotTreeItem item
: nodes
) {
492 String text
= item
.getText();
493 if (text
.contains(searchText
))
494 matchingItems
.add(item
);
498 if (matchingItems
.isEmpty())
499 throw new WidgetNotFoundException(
500 "Tree item element containg text \"" + searchText
501 + "\" was not found. Existing tree items:\n"
502 + StringUtils
.join(texts
, "\n"));
503 else if (matchingItems
.size() > 1)
504 throw new WidgetNotFoundException(
505 "Tree item element containg text \""
507 + "\" could not be uniquely identified. All tree items:\n"
508 + StringUtils
.join(texts
, "\n"));
510 return matchingItems
.get(0);
513 public static RevCommit
getHeadCommit(Repository repository
)
515 RevCommit headCommit
= null;
516 ObjectId parentId
= repository
.resolve(Constants
.HEAD
);
517 if (parentId
!= null) {
518 try (RevWalk rw
= new RevWalk(repository
)) {
519 headCommit
= rw
.parseCommit(parentId
);
525 public static void checkHeadCommit(Repository repository
, String author
,
526 String committer
, String message
) throws Exception
{
527 CommitInfo commitInfo
= CommitHelper
.getHeadCommitInfo(repository
);
528 assertEquals(author
, commitInfo
.getAuthor());
529 assertEquals(committer
, commitInfo
.getCommitter());
530 assertEquals(message
, commitInfo
.getCommitMessage());
533 public static void configureTestCommitterAsUser(Repository repository
) {
534 StoredConfig config
= repository
.getConfig();
535 config
.setString(ConfigConstants
.CONFIG_USER_SECTION
, null,
536 ConfigConstants
.CONFIG_KEY_NAME
, TestUtil
.TESTCOMMITTER_NAME
);
537 config
.setString(ConfigConstants
.CONFIG_USER_SECTION
, null,
538 ConfigConstants
.CONFIG_KEY_EMAIL
, TestUtil
.TESTCOMMITTER_EMAIL
);
541 public static void waitUntilViewWithGivenIdShows(final String viewId
) {
542 waitForView(new BaseMatcher
<IViewReference
>() {
543 public boolean matches(Object item
) {
544 if (item
instanceof IViewReference
)
545 return viewId
.equals(((IViewReference
) item
).getId());
549 public void describeTo(Description description
) {
550 description
.appendText("Wait for view with ID=" + viewId
);
555 public static void waitUntilViewWithGivenTitleShows(final String viewTitle
) {
556 waitForView(new BaseMatcher
<IViewReference
>() {
557 public boolean matches(Object item
) {
558 if (item
instanceof IViewReference
)
559 return viewTitle
.equals(((IViewReference
) item
).getTitle());
564 public void describeTo(Description description
) {
565 description
.appendText("Wait for view with title " + viewTitle
);
570 public static SWTBotShell
botForShellStartingWith(final String titlePrefix
) {
571 SWTWorkbenchBot bot
= new SWTWorkbenchBot();
573 Matcher
<Shell
> matcher
= new TypeSafeMatcher
<Shell
>() {
575 protected boolean matchesSafely(Shell item
) {
576 String title
= item
.getText();
577 return title
!= null && title
.startsWith(titlePrefix
);
580 public void describeTo(Description description
) {
581 description
.appendText("Shell with title starting with '"
582 + titlePrefix
+ "'");
586 Shell shell
= bot
.widget(matcher
);
587 return new SWTBotShell(shell
);
590 public static SWTBotView
showView(final String viewId
) {
591 Display
.getDefault().syncExec(new Runnable() {
593 IWorkbenchWindow workbenchWindow
= PlatformUI
.getWorkbench()
594 .getActiveWorkbenchWindow();
595 IWorkbenchPage workbenchPage
= workbenchWindow
.getActivePage();
597 workbenchPage
.showView(viewId
);
599 } catch (PartInitException e
) {
600 throw new RuntimeException("Showing view with ID " + viewId
606 SWTWorkbenchBot bot
= new SWTWorkbenchBot();
607 SWTBotView viewbot
= bot
.viewById(viewId
);
608 assertNotNull("View with ID " + viewId
+ " not found via SWTBot.",
613 public static void hideView(final String viewId
) {
614 Display
.getDefault().syncExec(new Runnable() {
616 IWorkbenchWindow workbenchWindow
= PlatformUI
.getWorkbench()
617 .getActiveWorkbenchWindow();
618 IWorkbenchPage workbenchPage
= workbenchWindow
.getActivePage();
619 IViewReference
[] views
= workbenchPage
.getViewReferences();
620 for (int i
= 0; i
< views
.length
; i
++) {
621 IViewReference view
= views
[i
];
622 if (viewId
.equals(view
.getId())) {
623 workbenchPage
.hideView(view
);
630 public static SWTBotView
showHistoryView() {
631 return showView("org.eclipse.team.ui.GenericHistoryView");
634 public static SWTBotView
showExplorerView() {
635 return showView("org.eclipse.jdt.ui.PackageExplorer");
638 public static SWTBotTree
getExplorerTree() {
639 SWTBotView view
= showExplorerView();
640 return view
.bot().tree();