From c2065dfd779c5aace139337ea7a1ef35564ba983 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sat, 29 Dec 2012 19:33:17 +0400 Subject: [PATCH] [git] cucumber tests: move from groovy to java, introduce virtual commits, understand commit details syntax Virtual commits are to be able to specify commit hashes in feature files. Since it's not possible to can't assign a hash to a commit, remember the actual hash after committing and link them together. Make it able to specify commit details in the feature file similar to the way they are written in the log, parse it using CommitDetails, and perform the commit given these specifications. --- plugins/git4idea/git4idea.iml | 2 +- .../git4idea/CherryPickeStepdefs.groovy | 88 -------- .../git4idea/GeneralStepdefs.groovy | 47 ----- .../git4idea/cherry-pick-auto-commit.feature | 45 +++-- .../test-stepdefs/git4idea/CommitDetails.java | 188 ++++++++++++++++++ .../git4idea/GeneralStepdefs.java | 80 ++++++++ .../git4idea/GitCherryPickStepdefs.java | 144 ++++++++++++++ .../git4idea/GitCucumberWorld.java | 3 + .../git4idea/GitTestVirtualCommitsHolder.java | 47 +++++ .../git4idea/test/GitExecutor.groovy | 8 +- .../git4idea/test/GitScenarios.groovy | 7 +- .../git4idea/test/GitTestImpl.groovy | 45 +++-- .../git4idea/test/GitTestInitUtil.groovy | 17 +- 13 files changed, 541 insertions(+), 180 deletions(-) delete mode 100644 plugins/git4idea/test-features/git4idea/CherryPickeStepdefs.groovy delete mode 100644 plugins/git4idea/test-features/git4idea/GeneralStepdefs.groovy create mode 100644 plugins/git4idea/test-stepdefs/git4idea/CommitDetails.java create mode 100644 plugins/git4idea/test-stepdefs/git4idea/GeneralStepdefs.java create mode 100644 plugins/git4idea/test-stepdefs/git4idea/GitCherryPickStepdefs.java create mode 100644 plugins/git4idea/test-stepdefs/git4idea/GitTestVirtualCommitsHolder.java diff --git a/plugins/git4idea/git4idea.iml b/plugins/git4idea/git4idea.iml index b0ecaa1607db..9871c3cff054 100644 --- a/plugins/git4idea/git4idea.iml +++ b/plugins/git4idea/git4idea.iml @@ -76,7 +76,7 @@ - + diff --git a/plugins/git4idea/test-features/git4idea/CherryPickeStepdefs.groovy b/plugins/git4idea/test-features/git4idea/CherryPickeStepdefs.groovy deleted file mode 100644 index 8cdca56d25b5..000000000000 --- a/plugins/git4idea/test-features/git4idea/CherryPickeStepdefs.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package git4idea -import com.intellij.dvcs.test.MockVirtualFile -import com.intellij.notification.Notification -import com.intellij.openapi.vcs.FilePathImpl -import com.intellij.openapi.vcs.changes.Change -import com.intellij.openapi.vcs.changes.ChangeListManager -import com.intellij.openapi.vcs.changes.LocalChangeList -import com.intellij.openapi.vcs.history.VcsRevisionNumber -import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile -import com.intellij.testFramework.vcs.MockChangeListManager -import com.intellij.testFramework.vcs.MockContentRevision -import git4idea.history.browser.GitCherryPicker -import git4idea.history.browser.GitCommit -import git4idea.history.browser.SHAHash -import git4idea.history.wholeTree.AbstractHash -import git4idea.test.TestNotificator - -import static com.intellij.dvcs.test.Executor.echo -import static cucumber.runtime.groovy.EN.* -import static git4idea.GitCucumberWorld.* -import static git4idea.test.GitExecutor.git -import static git4idea.test.GitScenarios.checkout -import static junit.framework.Assert.assertEquals -/** - * - * @author Kirill Likhodedov - */ - -String remCommit - -Given(~'^(enabled|disabled) auto-commit in the settings$') { String state -> - boolean enabled = state.equals("enabled") - myPlatformFacade.getSettings(myProject).setAutoCommitOnCherryPick(enabled); -} - -Given(~'^branch "([^"]*)" with commit "([^"]*)" by "([^"]*)" modifying (.+):$') { String branch, String commitMessage, String author, - String file, String content -> - checkout(myRepository, branch) - echo(file, content) - git("commit -am $commitMessage --author '$author <$author@example.com>'") - checkout(myRepository, "master") -} - -When(~'^I cherry-pick the commit "(.+)"$') { String msg -> - String hash = git("log --grep $msg --pretty=%H --all"); - remCommit = hash - new GitCherryPicker(myProject, myGit, myPlatformFacade, myPlatformFacade.getSettings(myProject).isAutoCommitOnCherryPick()) - .cherryPick(Collections.singletonMap(myRepository, Collections.singletonList(commit(hash, msg)))); -} - -private GitCommit commit(String hash, String message) { - AbstractHash ahash = AbstractHash.create(hash); - List changes = new ArrayList(); - changes.add(new Change(null, new MockContentRevision(new FilePathImpl(new MockVirtualFile("name")), VcsRevisionNumber.NULL))); - return new GitCommit(NullVirtualFile.INSTANCE, ahash, SHAHash.emulate(ahash), "John Smith", null, null, message, message, - null, null, null, null, null, null, null, changes, 0); -} - -Then(~'^the last commit is$') { String message -> - String actual = git("log -1 --pretty=%B"); - message = message.replace("#hash", remCommit) - assertEquals("Commit doesn't match", message, actual); -} - -And(~'^notification "([^"]*)" is shown$') { String title -> - assertEquals "Notification title is incorrect", title, lastNotification().title -} - -private Notification lastNotification() { - (myPlatformFacade.getNotificator(myProject) as TestNotificator).lastNotification -} - -And(~'^no new changelists are created$') {-> - assertOnlyDefaultChangelist() -} - -void assertOnlyDefaultChangelist() { - String DEFAULT = MockChangeListManager.DEFAULT_CHANGE_LIST_NAME; - assertChangeLists( [DEFAULT], DEFAULT) -} - -void assertChangeLists(Collection changeLists, String activeChangelist) { - ChangeListManager changeListManager = myPlatformFacade.getChangeListManager(myProject) - List lists = changeListManager.changeLists - Collection listNames = lists.collect { it.name } - assertEquals "Change lists are different", changeLists.toSet(), listNames.toSet() - assertEquals "Wrong active changelist", activeChangelist, changeListManager.defaultChangeList.name -} diff --git a/plugins/git4idea/test-features/git4idea/GeneralStepdefs.groovy b/plugins/git4idea/test-features/git4idea/GeneralStepdefs.groovy deleted file mode 100644 index 9ce5cd9b073a..000000000000 --- a/plugins/git4idea/test-features/git4idea/GeneralStepdefs.groovy +++ /dev/null @@ -1,47 +0,0 @@ -package git4idea -import com.intellij.dvcs.test.MockProject -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.util.io.FileUtil -import git4idea.test.GitTestImpl -import git4idea.test.GitTestPlatformFacade - -import static com.intellij.dvcs.test.Executor.cd -import static com.intellij.dvcs.test.Executor.mkdir -import static com.intellij.dvcs.test.Executor.touch -import static cucumber.runtime.groovy.EN.Given -import static cucumber.runtime.groovy.Hooks.After -import static cucumber.runtime.groovy.Hooks.Before -import static git4idea.GitCucumberWorld.* -import static git4idea.test.GitExecutor.git -import static git4idea.test.GitScenarios.checkout -import static git4idea.test.GitTestInitUtil.createRepository - -/** - * General step definitions and hooks used by all cucumber tests. - * - * @author Kirill Likhodedov - */ - -Before() { - myTestRoot = FileUtil.createTempDirectory("", "").getPath() - cd myTestRoot - myProjectRoot = mkdir ("project") - myProject = new MockProject(myProjectRoot) - myPlatformFacade = new GitTestPlatformFacade() - myGit = new GitTestImpl() - - cd(myProjectRoot) - myRepository = createRepository(myProjectRoot, myPlatformFacade, myProject) -} - -After() { - FileUtil.delete(new File(myTestRoot)) - Disposer.dispose(myProject) -} - -Given(~'^file (.*) on master:$') { String filename, String content -> - checkout(myRepository, "master") - touch(filename, content) - git("add $filename") - git("commit -m 'adding $filename'") -} \ No newline at end of file diff --git a/plugins/git4idea/test-features/git4idea/cherry-pick-auto-commit.feature b/plugins/git4idea/test-features/git4idea/cherry-pick-auto-commit.feature index 92ed5c7d7866..518891090552 100644 --- a/plugins/git4idea/test-features/git4idea/cherry-pick-auto-commit.feature +++ b/plugins/git4idea/test-features/git4idea/cherry-pick-auto-commit.feature @@ -1,28 +1,37 @@ Feature: Git Cherry-Pick When Auto-Commit is selected - Background: - Given enabled auto-commit in the settings - Given file file.txt on master: - """ - first line - - last line (space is to have possibility to avoid conflicts) - """ - Given branch "feature" with commit "fix_1" by "John Bro" modifying file.txt: - """ - first line - - last line on branch feature - """ +Background: + Given enabled auto-commit in the settings + Given file file.txt "initial" on master + And commit f5027a3 on branch feature + """ + fix #1 + ----- + Author: John Bro + Changes: + M file.txt "feature changes" + """ Scenario: Simple cherry-pick - When I cherry-pick the commit "fix_1" + When I cherry-pick the commit f5027a3 Then the last commit is """ - fix_1 - (cherry picked from commit #hash) + fix #1 + (cherry picked from commit f5027a3) """ - And notification "Cherry-pick successful" is shown + And there is notification 'Cherry-pick successful' And no new changelists are created + Scenario: Dirty tree, conflicting with the commit + Given file.txt is locally modified: + """ + master + """ + When I cherry-pick the commit f5027a3 + Then nothing is committed + And error notification 'Cherry-pick failed' is shown: + """ + f5027a3 fix #1 + error: Your local changes to the following files would be overwritten by merge: + """ diff --git a/plugins/git4idea/test-stepdefs/git4idea/CommitDetails.java b/plugins/git4idea/test-stepdefs/git4idea/CommitDetails.java new file mode 100644 index 000000000000..c2737d2788b5 --- /dev/null +++ b/plugins/git4idea/test-stepdefs/git4idea/CommitDetails.java @@ -0,0 +1,188 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.intellij.dvcs.test.Executor.echo; +import static com.intellij.dvcs.test.Executor.touch; +import static git4idea.GitCucumberWorld.virtualCommits; +import static git4idea.test.GitExecutor.git; + +/** + * + * @author Kirill Likhodedov + */ +public class CommitDetails { + + private String myHash; + private String myMessage; + private String myAuthor; + private Collection myChanges; + + private static class Change { + + public void apply() { + switch (myType) { + case MODIFIED: + echo(myFile, myContent); + break; + case ADDED: + touch(myFile, myContent); + break; + case DELETED: + throw new UnsupportedOperationException("Not implemented yet"); + case MOVED: + throw new UnsupportedOperationException("Not implemented yet"); + } + } + + enum Type { + MODIFIED, ADDED, DELETED, MOVED + } + + private final Type myType; + private final String myFile; + private final String myContent; + + Change(Type type, String filename, String content) { + myType = type; + myFile = filename; + myContent = content; + } + + } + + private enum ParsingStage { + MESSAGE, + DATA, + CHANGES + } + + /** + * Format: + *
+       commit subject
+
+       and optional description
+       -----
+       Author: John Bro
+       Changes:
+       M file.txt "feature changes"
+   * 
+ */ + public static CommitDetails parse(String hash, String details) { + CommitDetails commit = new CommitDetails(); + commit.myHash = hash; + + StringBuilder message = new StringBuilder(); + Collection changes = new ArrayList(); + ParsingStage stage = ParsingStage.MESSAGE; + for (String line : details.split("\n")) { + if (line.equals("-----")) { + stage = ParsingStage.DATA; + continue; + } + else if (line.equals("Changes:")) { + stage = ParsingStage.CHANGES; + continue; + } + + if (stage == ParsingStage.MESSAGE) { + message.append(line); + } + else if (stage == ParsingStage.CHANGES) { + changes.add(parseChange(line)); + } + else if (line.toLowerCase().startsWith("author: ")) { + commit.myAuthor = line.substring("author: ".length()); + } + } + + commit.myMessage = message.toString(); + commit.myChanges = changes; + return commit; + } + + private static Change parseChange(String change) { + int firstSpace = change.indexOf(' '); + int secondSpace = change.indexOf(' ', firstSpace + 1); + return new Change(parseType(change.substring(0, firstSpace)), + change.substring(firstSpace + 1, secondSpace), + change.substring(secondSpace + 1)); + } + + private static Change.Type parseType(String type) { + if (type.equals("M")) { + return Change.Type.MODIFIED; + } + else if (type.equals("A")) { + return Change.Type.ADDED; + } + else if (type.equals("D")) { + return Change.Type.DELETED; + } + else if (type.equals("R")) { + return Change.Type.MOVED; + } + return null; + } + + /** + * @return real commit details. + */ + public CommitDetails apply() { + for (Change change : myChanges) { + change.apply(); + } + + String commitOutput = git(String.format("commit -am '%1$s' --author '%2$s <%2$s@example.com>'", myMessage, myAuthor)); + CommitDetails realCommit = parseHashFromCommitOutput(commitOutput); + virtualCommits.register(myHash, realCommit); + return realCommit; + } + + CommitDetails parseHashFromCommitOutput(String commitOutput) { + String line = commitOutput.split("\n")[0]; + Pattern reg = Pattern.compile("^\\s*\\[.+ ([a-fA-F0-9]+)\\] (.+)$"); + Matcher matcher = reg.matcher(line); + boolean matches = matcher.matches(); + assert matches; + return new CommitDetails().hash(matcher.group(1)).message(matcher.group(2)); + } + + private CommitDetails hash(String hash) { + myHash = hash; + return this; + } + + private CommitDetails message(String message) { + myMessage = message; + return this; + } + + public String getHash() { + return myHash; + } + + public String getMessage() { + return myMessage; + } + +} diff --git a/plugins/git4idea/test-stepdefs/git4idea/GeneralStepdefs.java b/plugins/git4idea/test-stepdefs/git4idea/GeneralStepdefs.java new file mode 100644 index 000000000000..51137075adbc --- /dev/null +++ b/plugins/git4idea/test-stepdefs/git4idea/GeneralStepdefs.java @@ -0,0 +1,80 @@ +package git4idea;/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.intellij.dvcs.test.MockProject; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import cucumber.annotation.After; +import cucumber.annotation.Before; +import cucumber.annotation.en.Given; +import git4idea.test.GitTestImpl; +import git4idea.test.GitTestPlatformFacade; + +import java.io.File; +import java.io.IOException; + +import static com.intellij.dvcs.test.Executor.cd; +import static com.intellij.dvcs.test.Executor.mkdir; +import static git4idea.GitCucumberWorld.*; +import static git4idea.test.GitExecutor.git; +import static git4idea.test.GitExecutor.touch; +import static git4idea.test.GitScenarios.checkout; +import static git4idea.test.GitTestInitUtil.createRepository; + +/** + * @author Kirill Likhodedov + */ +public class GeneralStepdefs { + + @Before + public void setUpProject() throws IOException { + myTestRoot = FileUtil.createTempDirectory("", "").getPath(); + cd(myTestRoot); + myProjectRoot = mkdir("project"); + myProject = new MockProject(myProjectRoot); + myPlatformFacade = new GitTestPlatformFacade(); + myGit = new GitTestImpl(); + mySettings = myPlatformFacade.getSettings(myProject); + + cd(myProjectRoot); + myRepository = createRepository(myProjectRoot, myPlatformFacade, myProject); + + virtualCommits = new GitTestVirtualCommitsHolder(); + } + + @After + public void cleanup() { + FileUtil.delete(new File(myTestRoot)); + Disposer.dispose(myProject); + } + + @Given("^file (.*) \"(.*)\" on master$") + public void file_file_txt_on_master(String filename, String content) throws Throwable { + checkout(myRepository, "master"); + touch(filename, content); + git("add %s", filename); + git("commit -m 'adding %s'", filename); + } + + @Given("^commit (.+) on branch (.+)$") + public void commit_on_branch_feature(String hash, String branch, String commitDetails) throws Throwable { + CommitDetails commit = CommitDetails.parse(hash, commitDetails); + checkout(branch); + commit.apply(); + checkout("master"); + } + +} diff --git a/plugins/git4idea/test-stepdefs/git4idea/GitCherryPickStepdefs.java b/plugins/git4idea/test-stepdefs/git4idea/GitCherryPickStepdefs.java new file mode 100644 index 000000000000..cfcaa0f05810 --- /dev/null +++ b/plugins/git4idea/test-stepdefs/git4idea/GitCherryPickStepdefs.java @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.intellij.dvcs.test.MockVirtualFile; +import com.intellij.notification.Notification; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile; +import com.intellij.testFramework.vcs.MockChangeListManager; +import com.intellij.testFramework.vcs.MockContentRevision; +import cucumber.annotation.en.And; +import cucumber.annotation.en.Given; +import cucumber.annotation.en.Then; +import cucumber.annotation.en.When; +import git4idea.history.browser.GitCherryPicker; +import git4idea.history.browser.GitCommit; +import git4idea.history.browser.SHAHash; +import git4idea.history.wholeTree.AbstractHash; +import git4idea.test.TestNotificator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; + +import static com.intellij.dvcs.test.Executor.echo; +import static git4idea.GitCucumberWorld.*; +import static git4idea.test.GitExecutor.git; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Kirill Likhodedov + */ + +public class GitCherryPickStepdefs { + + @Given("^(enabled|disabled) auto-commit in the settings$") + public void auto_commit_in_the_settings(String state) { + boolean enabled = state.equals("enabled"); + myPlatformFacade.getSettings(myProject).setAutoCommitOnCherryPick(enabled); + } + + @When("^I cherry-pick the commit (.+)$") + public void I_cherry_pick_the_commit(String hash) { + CommitDetails realCommit = virtualCommits.getRealCommit(hash); + new GitCherryPicker(myProject, myGit, myPlatformFacade, mySettings.isAutoCommitOnCherryPick()) + .cherryPick(Collections.singletonMap(myRepository, Collections.singletonList(mockCommit(realCommit.getHash(), realCommit.getMessage())))); + } + + private static GitCommit mockCommit(String hash, String message) { + AbstractHash ahash = AbstractHash.create(hash); + List changes = new ArrayList(); + changes.add(new Change(null, new MockContentRevision(new FilePathImpl(new MockVirtualFile("name")), VcsRevisionNumber.NULL))); + return new GitCommit(NullVirtualFile.INSTANCE, ahash, SHAHash.emulate(ahash), "John Smith", null, null, message, message, + null, null, null, null, null, null, null, changes, 0); + } + + @Then("^the last commit is$") + public void the_last_commit_is(String message) { + String actual = git("log -1 --pretty=%B"); + message = virtualCommits.replaceVirtualHashes(message); + assertEquals("Commit doesn't match", message, trimHash(actual)); + } + + @And("^there is notification '(.*)'$") + public void there_is_notification(String title) { + assertEquals("Notification title is incorrect", title, lastNotification().getTitle()); + } + + private static Notification lastNotification() { + return ((TestNotificator)myPlatformFacade.getNotificator(myProject)).getLastNotification(); + } + + @And("^no new changelists are created$") + public void no_new_changelists_are_created() { + assertOnlyDefaultChangelist(); + } + + void assertOnlyDefaultChangelist() { + String DEFAULT = MockChangeListManager.DEFAULT_CHANGE_LIST_NAME; + assertChangeLists(Collections.singleton(DEFAULT), DEFAULT); + } + + void assertChangeLists(Collection changeLists, String activeChangelist) { + ChangeListManager changeListManager = myPlatformFacade.getChangeListManager(myProject); + List lists = changeListManager.getChangeLists(); + Collection listNames = Collections2.transform(lists, new Function() { + @Override + public String apply(LocalChangeList input) { + return input.getName(); + } + }); + assertEquals("Change lists are different", new ArrayList(changeLists), new ArrayList(listNames)); + assertEquals("Wrong active changelist", activeChangelist, changeListManager.getDefaultChangeList().getName()); + } + + @Given("^(.+) is locally modified:$") + public void is_locally_modified(String filename, String content) { + echo(filename, content); + } + + String trimHash(String commitMessage) { + int hashStart = commitMessage.lastIndexOf(' ') + 1; + String hash = commitMessage.substring(hashStart); + return commitMessage.replace(hash, hash.substring(0, 7)) + ")"; + } + + @Then("^nothing is committed$") + public void nothing_is_committed() { + assertFalse("Working tree is unexpectedly clean", git("diff").trim().isEmpty()); + } + + @And("^error notification '(.+)' is shown:$") + public void error_notification_is_shown(String title, String content) { + assertEquals("Notification title is incorrect", title, lastNotification().getTitle()); + assertEquals("Notification content is incorrect", virtualCommits.replaceVirtualHashes(content), + convertNotificationHtml(lastNotification().getContent())); + } + + private static String convertNotificationHtml(String content) { + return content.replaceAll("
", Matcher.quoteReplacement("\n")); + } +} \ No newline at end of file diff --git a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java index 375c000f9ce5..b92dbc55d816 100644 --- a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java +++ b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java @@ -2,6 +2,7 @@ package git4idea; import com.intellij.dvcs.test.MockProject; import git4idea.commands.Git; +import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepository; /** @@ -18,5 +19,7 @@ public class GitCucumberWorld { public static GitPlatformFacade myPlatformFacade; public static Git myGit; public static GitRepository myRepository; + public static GitVcsSettings mySettings; + public static GitTestVirtualCommitsHolder virtualCommits; } diff --git a/plugins/git4idea/test-stepdefs/git4idea/GitTestVirtualCommitsHolder.java b/plugins/git4idea/test-stepdefs/git4idea/GitTestVirtualCommitsHolder.java new file mode 100644 index 000000000000..57d6c01c88c9 --- /dev/null +++ b/plugins/git4idea/test-stepdefs/git4idea/GitTestVirtualCommitsHolder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea; + +import java.util.HashMap; +import java.util.Map; + +/** + * So called Virtual Commits are used in Cucumber feature files, so that the writer of a test could refer to commits in the natural way: + * via hashes. We can't define a hash to the commit when committing to Git, therefore to match these two sets we store them here. + * + * @author Kirill Likhodedov + */ +public class GitTestVirtualCommitsHolder { + + // virtual hash -> commit details + private Map commits = new HashMap(); + + void register(String virtualHash, CommitDetails CommitInfo) { + commits.put(virtualHash, CommitInfo); + } + + CommitDetails getRealCommit(String virtualHash) { + return commits.get(virtualHash); + } + + String replaceVirtualHashes(String message) { + for (Map.Entry entry : commits.entrySet()) { + message = message.replace(entry.getKey(), entry.getValue().getHash()); + } + return message; + } + +} diff --git a/plugins/git4idea/testFramework/git4idea/test/GitExecutor.groovy b/plugins/git4idea/testFramework/git4idea/test/GitExecutor.groovy index 3b3c3b4e5672..263aa9dd1317 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitExecutor.groovy +++ b/plugins/git4idea/testFramework/git4idea/test/GitExecutor.groovy @@ -95,10 +95,16 @@ class GitExecutor extends Executor { } public static String git(GitRepository repository, String command) { - cd repository.root.path + if (repository != null) { + cd repository.root.path + } git command } + public static String git(String formatString, String... args) { + return git(String.format(formatString, args)) + } + public static void cd(GitRepository repository) { cd repository.root.path } diff --git a/plugins/git4idea/testFramework/git4idea/test/GitScenarios.groovy b/plugins/git4idea/testFramework/git4idea/test/GitScenarios.groovy index 02d8f66c90a8..9953bce904d1 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitScenarios.groovy +++ b/plugins/git4idea/testFramework/git4idea/test/GitScenarios.groovy @@ -154,16 +154,17 @@ class GitScenarios { git("commit -m just_a_commit") } - public static boolean branchExists(GitRepository repo, String branch) { + public static boolean branchExists(GitRepository repo = null, String branch) { git(repo, "branch").contains(branch) } - public static void checkout(GitRepository repository, String branch) { - if (branchExists(repository, branch)) { + public static void checkout(GitRepository repository = null, String branch) { + if (branch.equals("master") || branchExists(repository, branch)) { git("checkout $branch") } else { git("checkout -b $branch") } } + } diff --git a/plugins/git4idea/testFramework/git4idea/test/GitTestImpl.groovy b/plugins/git4idea/testFramework/git4idea/test/GitTestImpl.groovy index fddc63797e4c..83121783cb66 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitTestImpl.groovy +++ b/plugins/git4idea/testFramework/git4idea/test/GitTestImpl.groovy @@ -32,10 +32,12 @@ import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable import java.lang.reflect.Method +import static com.intellij.dvcs.test.Executor.cd; +import static GitExecutor.git; + /** * @author Kirill Likhodedov */ -@Mixin(GitExecutor) public class GitTestImpl implements Git { @NotNull @@ -197,13 +199,13 @@ public class GitTestImpl implements Git { @NotNull String hash, boolean autoCommit, @NotNull GitLineHandlerListener... listeners) { - execute(repository, "cherry-pick -x ${autoCommit ? "" : "-n"} $hash") + return execute(repository, "cherry-pick -x ${autoCommit ? "" : "-n"} $hash", listeners); } @NotNull @Override public GitCommandResult getUnmergedFiles(@NotNull GitRepository repository) { - execute(repository, "ls-files --unmerged") + return execute(repository, "ls-files --unmerged"); } @NotNull @@ -216,28 +218,41 @@ public class GitTestImpl implements Git { } private static GitCommandResult commandResult(String output) { - boolean success = !output.split("\n").collect { isError(it) }.contains(true) - return new GitCommandResult(success, 0, Collections.emptyList(), Arrays.asList(StringUtil.splitByLines(output))) + Collection err = new ArrayList<>(); + Collection out = new ArrayList<>(); + for (String line : output.split("\n")) { + if (isError(line)) { + err.add(line); + } + else { + out.add(line); + } + } + boolean success = err.isEmpty(); + return new GitCommandResult(success, 0, err, out); } private static boolean isError(String s) { // we don't want to make that method public, since it is reused only in the test. - Method m = GitImpl.class.getDeclaredMethod("isError", String.class) - m.setAccessible(true) - return m.invoke(null, s) as boolean + Method m = GitImpl.class.getDeclaredMethod("isError", String.class); + m.setAccessible(true); + return (boolean) m.invoke(null, s); } - static def feedOutput(String output, GitLineHandlerListener... listeners) { + private static void feedOutput(String output, GitLineHandlerListener... listeners) { listeners.each { GitLineHandlerListener listener -> - output.split("\n").each { listener.onLineAvailable(it, ProcessOutputTypes.STDERR) } + String split = output.split("\n") + for (String line : split) { + listener.onLineAvailable(line, ProcessOutputTypes.STDERR); + } } } - def execute(GitRepository repository, String operation, GitLineHandlerListener... listeners) { - cd repository.root.path - def out = git(operation) - feedOutput(out, listeners) - commandResult(out) + private static GitCommandResult execute(GitRepository repository, String operation, GitLineHandlerListener... listeners) { + cd(repository.getRoot().getPath()); + String out = git(operation); + feedOutput(out, listeners); + return commandResult(out); } } diff --git a/plugins/git4idea/testFramework/git4idea/test/GitTestInitUtil.groovy b/plugins/git4idea/testFramework/git4idea/test/GitTestInitUtil.groovy index 850323ee78dd..4674d988b5f3 100644 --- a/plugins/git4idea/testFramework/git4idea/test/GitTestInitUtil.groovy +++ b/plugins/git4idea/testFramework/git4idea/test/GitTestInitUtil.groovy @@ -6,6 +6,9 @@ import git4idea.GitPlatformFacade import git4idea.repo.GitRepository import git4idea.repo.GitRepositoryImpl +import static com.intellij.dvcs.test.Executor.* +import static git4idea.test.GitExecutor.* + /** * * @author Kirill Likhodedov @@ -20,17 +23,17 @@ class GitTestInitUtil { * @param repoRoot */ public static void initRepo(String repoRoot) { - com.intellij.dvcs.test.Executor.cd repoRoot - git4idea.test.GitExecutor.git("init") + cd repoRoot + git("init") setupUsername(); - com.intellij.dvcs.test.Executor.touch("initial.txt") - git4idea.test.GitExecutor.git("add initial.txt") - git4idea.test.GitExecutor.git("commit -m initial") + touch("initial.txt") + git("add initial.txt") + git("commit -m initial") } public static void setupUsername() { - git4idea.test.GitExecutor.git("config user.name $USER_NAME") - git4idea.test.GitExecutor.git("config user.email $USER_EMAIL") + git("config user.name $USER_NAME") + git("config user.email $USER_EMAIL") } public static GitRepository createRepository(String rootDir, GitPlatformFacade platformFacade, Project project) {