/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.cli.test.functional.general;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.deleteAndReplaceFeatureType;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.exitCode;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.geogigCLI;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.insert;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.insertAndAdd;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.insertAndAddFeatures;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.insertFeatures;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.platform;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.runAndParseCommand;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.runCommand;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.setUpDirectories;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.setupGeogig;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.stdOut;
import static org.locationtech.geogig.cli.test.functional.general.GlobalState.tempFolder;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.feature;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.idP1;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.lines1;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.lines2;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.lines3;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.points1;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.points1_modified;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.points2;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.points3;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.pointsName;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.pointsType;
import static org.locationtech.geogig.cli.test.functional.general.TestFeatures.setupFeatures;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import org.junit.rules.TemporaryFolder;
import org.locationtech.geogig.api.GeoGIG;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.RevFeatureTypeImpl;
import org.locationtech.geogig.api.plumbing.RefParse;
import org.locationtech.geogig.api.plumbing.UpdateRef;
import org.locationtech.geogig.api.plumbing.diff.AttributeDiff;
import org.locationtech.geogig.api.plumbing.diff.FeatureDiff;
import org.locationtech.geogig.api.plumbing.diff.GenericAttributeDiffImpl;
import org.locationtech.geogig.api.plumbing.diff.Patch;
import org.locationtech.geogig.api.plumbing.diff.PatchSerializer;
import org.locationtech.geogig.api.porcelain.MergeConflictsException;
import org.locationtech.geogig.api.porcelain.MergeOp;
import org.locationtech.geogig.api.porcelain.TagCreateOp;
import org.locationtech.geogig.cli.ArgumentTokenizer;
import org.locationtech.geogig.repository.Hints;
import org.locationtech.geogig.repository.WorkingTree;
import org.opengis.feature.Feature;
import org.opengis.feature.type.PropertyDescriptor;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.runtime.java.StepDefAnnotation;
/**
*
*/
@StepDefAnnotation
public class DefaultStepDefinitions {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
@cucumber.annotation.Before
public void before() throws Exception {
tempFolder = new TemporaryFolder();
tempFolder.create();
setupFeatures();
}
@cucumber.annotation.After
public void after() {
if (GlobalState.geogigCLI != null) {
GlobalState.geogigCLI.close();
GlobalState.geogigCLI = null;
}
if (GlobalState.consoleReader != null) {
GlobalState.consoleReader.shutdown();
GlobalState.consoleReader = null;
}
System.gc();
System.runFinalization();
tempFolder.delete();
// assertFalse(
// "this test is messing up with the config, make sure it uses CLITestInjectorBuilder",
// new File("/home/groldan/.geogigconfig").exists());
}
@Given("^I am in an empty directory$")
public void I_am_in_an_empty_directory() throws Throwable {
setUpDirectories();
assertEquals(0, platform.pwd().list().length);
setupGeogig();
}
@When("^I run the command \"(.*?)\"$")
public void I_run_the_command_X(String commandSpec) throws Throwable {
String[] args = ArgumentTokenizer.tokenize(commandSpec);
File pwd = platform.pwd();
for (int i = 0; i < args.length; i++) {
args[i] = args[i].replace("${currentdir}", pwd.getAbsolutePath());
args[i] = args[i].replace("\"", "");
}
runCommand(args);
}
@Then("^it should exit with non-zero exit code$")
public void it_should_exit_with_non_zero_exit_code() throws Throwable {
assertFalse("exited with exit code " + exitCode, exitCode == 0);
}
@Then("^it should exit with zero exit code$")
public void it_should_exit_with_zero_exit_code() throws Throwable {
assertEquals(0, exitCode);
}
@Then("^it should answer \"([^\"]*)\"$")
public void it_should_answer_exactly(String expected) throws Throwable {
File pwd = platform.pwd();
expected = expected.replace("${currentdir}", pwd.getAbsolutePath()).toLowerCase()
.replaceAll("\\\\", "/");
String actual = stdOut.toString().replaceAll(LINE_SEPARATOR, "").replaceAll("\\\\", "/")
.trim().toLowerCase();
assertEquals(expected, actual);
}
@Then("^the response should contain \"([^\"]*)\"$")
public void the_response_should_contain(String expected) throws Throwable {
String actual = stdOut.toString().replaceAll(LINE_SEPARATOR, "").replaceAll("\\\\", "/");
if (!actual.contains(expected))
System.err.println(actual);
assertTrue("'" + actual + "' does not contain '" + expected + "'",
actual.contains(expected));
}
@Then("^the response should not contain \"([^\"]*)\"$")
public void the_response_should_not_contain(String expected) throws Throwable {
String actual = stdOut.toString().replaceAll(LINE_SEPARATOR, "").replaceAll("\\\\", "/");
assertFalse(actual, actual.contains(expected));
}
@Then("^the response should contain ([^\"]*) lines$")
public void the_response_should_contain_x_lines(int lines) throws Throwable {
String output = stdOut.toString();
String[] lineStrings = output.split(LINE_SEPARATOR);
assertEquals(output, lines, lineStrings.length);
}
@Then("^the response should start with \"([^\"]*)\"$")
public void the_response_should_start_with(String expected) throws Throwable {
String actual = stdOut.toString().replaceAll(LINE_SEPARATOR, "");
assertTrue(actual, actual.startsWith(expected));
}
@Then("^the repository directory shall exist$")
public void the_repository_directory_shall_exist() throws Throwable {
List<String> output = runAndParseCommand(true, "rev-parse", "--resolve-geogig-dir");
assertEquals(output.toString(), 1, output.size());
String location = output.get(0);
assertNotNull(location);
File repoDir = new File(location);
assertTrue("Repository directory not found: " + repoDir.getAbsolutePath(), repoDir.exists());
}
@Given("^I have a remote ref called \"([^\"]*)\"$")
public void i_have_a_remote_ref_called(String expected) throws Throwable {
String ref = "refs/remotes/origin/" + expected;
geogigCLI.getGeogig(Hints.readWrite()).command(UpdateRef.class).setName(ref)
.setNewValue(ObjectId.NULL).call();
Optional<Ref> refValue = geogigCLI.getGeogig(Hints.readWrite()).command(RefParse.class)
.setName(ref).call();
assertTrue(refValue.isPresent());
assertEquals(refValue.get().getObjectId(), ObjectId.NULL);
}
@Given("^I have a remote tag called \"([^\"]*)\"$")
public void i_have_a_remote_tag_called(String expected) throws Throwable {
geogigCLI.getGeogig(Hints.readWrite()) //
.command(TagCreateOp.class) //
.setName(expected) //
.setMessage("Tagged " + expected) //
.setCommitId(ObjectId.NULL) //
.call();
}
@Given("^I have an unconfigured repository$")
public void I_have_an_unconfigured_repository() throws Throwable {
setUpDirectories();
setupGeogig();
List<String> output = runAndParseCommand(true, "init");
assertEquals(output.toString(), 1, output.size());
assertNotNull(output.get(0));
assertTrue(output.get(0), output.get(0).startsWith("Initialized"));
}
@Given("^I have a merge conflict state$")
public void I_have_a_merge_conflict_state() throws Throwable {
I_have_conflicting_branches();
Ref branch = geogigCLI.getGeogig(Hints.readOnly()).command(RefParse.class)
.setName("branch1").call().get();
try {
geogigCLI.getGeogig(Hints.readWrite()).command(MergeOp.class)
.addCommit(Suppliers.ofInstance(branch.getObjectId())).call();
fail();
} catch (MergeConflictsException e) {
}
}
@Given("^I have conflicting branches$")
public void I_have_conflicting_branches() throws Throwable {
// Create the following revision graph
// ............o
// ............|
// ............o - Points 1 added
// .........../|\
// branch2 - o | o - branch1 - Points 1 modifiedB and points 2 added
// ............|
// ............o - points 1 modified
// ............|
// ............o - master - HEAD - Lines 1 modified
// branch1 and master are conflicting
Feature points1ModifiedB = feature(pointsType, idP1, "StringProp1_3", new Integer(2000),
"POINT(1 1)");
Feature points1Modified = feature(pointsType, idP1, "StringProp1_2", new Integer(1000),
"POINT(1 1)");
insertAndAdd(points1);
runCommand(true, "commit -m Commit1");
runCommand(true, "branch branch1");
runCommand(true, "branch branch2");
insertAndAdd(points1Modified);
runCommand(true, "commit -m Commit2");
insertAndAdd(lines1);
runCommand(true, "commit -m Commit3");
runCommand(true, "checkout branch1");
insertAndAdd(points1ModifiedB);
insertAndAdd(points2);
runCommand(true, "commit -m Commit4");
runCommand(true, "checkout branch2");
insertAndAdd(points3);
runCommand(true, "commit -m Commit5");
runCommand(true, "checkout master");
}
@Given("^I set up a hook$")
public void I_set_up_a_hook() throws Throwable {
File hooksDir = new File(platform.pwd(), ".geogig/hooks");
File hook = new File(hooksDir, "pre_commit.js");
String script = "exception = Packages.org.locationtech.geogig.api.hooks.CannotRunGeogigOperationException;\n"
+ "msg = params.get(\"message\");\n"
+ "if (msg.length() < 5){\n"
+ "\tthrow new exception(\"Commit messages must have at least 5 letters\");\n"
+ "}\n" + "params.put(\"message\", msg.toLowerCase());";
Files.write(script, hook, Charset.forName("UTF-8"));
}
@Given("^there is a remote repository$")
public void there_is_a_remote_repository() throws Throwable {
createRemote("remoterepo");
}
@Given("^there is a remote repository with blank spaces$")
public void there_is_a_remote_repository_with_blank_spaces() throws Throwable {
createRemote("remote repo");
}
private void createRemote(String name) throws Throwable {
I_am_in_an_empty_directory();
final File currDir = platform.pwd();
List<String> output = runAndParseCommand(true, "init", name);
assertEquals(output.toString(), 1, output.size());
assertNotNull(output.get(0));
assertTrue(output.get(0), output.get(0).startsWith("Initialized"));
final File remoteRepo = new File(currDir, name);
GlobalState.remoteRepo = remoteRepo;
platform.setWorkingDir(remoteRepo);
setupGeogig();
runCommand(true, "config", "--global", "user.name", "John Doe");
runCommand(true, "config", "--global", "user.email", "JohnDoe@example.com");
insertAndAdd(points1);
runCommand(true, "commit -m Commit1");
runCommand(true, "branch -c branch1");
insertAndAdd(points2);
runCommand(true, "commit -m Commit2");
insertAndAdd(points3);
runCommand(true, "commit -m Commit3");
runCommand(true, "checkout master");
insertAndAdd(lines1);
runCommand(true, "commit -m Commit4");
insertAndAdd(lines2);
runCommand(true, "commit -m Commit5");
platform.setWorkingDir(currDir);
setupGeogig();
}
@Given("^there is a remote repository with a tag named \"([^\"]*)\"$")
public void there_is_a_remote_repository_with_a_tag_named(String tagName) throws Throwable {
I_am_in_an_empty_directory();
final File currDir = platform.pwd();
List<String> output = runAndParseCommand(true, "init", "remoterepo");
assertEquals(output.toString(), 1, output.size());
assertNotNull(output.get(0));
assertTrue(output.get(0), output.get(0).startsWith("Initialized"));
final File remoteRepo = new File(currDir, "remoterepo");
GlobalState.remoteRepo = remoteRepo;
platform.setWorkingDir(remoteRepo);
setupGeogig();
runCommand(true, "config", "--global", "user.name", "John Doe");
runCommand(true, "config", "--global", "user.email", "JohnDoe@example.com");
insertAndAdd(points1);
runCommand(true, "commit -m Commit1");
runCommand(true, "branch -c branch1");
insertAndAdd(points2);
runCommand(true, "commit -m Commit2");
insertAndAdd(points3);
runCommand(true, "commit -m Commit3");
runCommand(true, "checkout master");
insertAndAdd(lines1);
runCommand(true, "commit -m Commit4");
insertAndAdd(lines2);
runCommand(true, "commit -m Commit5");
runCommand(true, "tag " + tagName + " -m Created_" + tagName + "");
String actual = stdOut.toString().replaceAll(LINE_SEPARATOR, "").replaceAll("\\\\", "/")
.trim().toLowerCase();
assertTrue(actual, actual.startsWith("created tag " + tagName));
platform.setWorkingDir(currDir);
setupGeogig();
}
@Given("^I have a repository$")
public void I_have_a_repository() throws Throwable {
I_have_an_unconfigured_repository();
runCommand(true, "config", "--global", "user.name", "John Doe");
runCommand(true, "config", "--global", "user.email", "JohnDoe@example.com");
}
@Given("^I have a repository with a remote$")
public void I_have_a_repository_with_a_remote() throws Throwable {
there_is_a_remote_repository();
List<String> output = runAndParseCommand("init", "localrepo");
assertEquals(output.toString(), 1, output.size());
assertNotNull(output.get(0));
assertTrue(output.get(0), output.get(0).startsWith("Initialized"));
platform.setWorkingDir(new File(platform.pwd(), "localrepo"));
setupGeogig();
runCommand(true, "config", "--global", "user.name", "John Doe");
runCommand(true, "config", "--global", "user.email", "JohnDoe@example.com");
File remoteRepo = GlobalState.remoteRepo;
String remotePath = remoteRepo.getAbsolutePath();
runCommand(true, "remote", "add", "origin", remotePath);
}
@Given("^I have staged \"([^\"]*)\"$")
public void I_have_staged(String feature) throws Throwable {
if (feature.equals("points1")) {
insertAndAdd(points1);
} else if (feature.equals("points2")) {
insertAndAdd(points2);
} else if (feature.equals("points3")) {
insertAndAdd(points3);
} else if (feature.equals("points1_modified")) {
insertAndAdd(points1_modified);
} else if (feature.equals("lines1")) {
insertAndAdd(lines1);
} else if (feature.equals("lines2")) {
insertAndAdd(lines2);
} else if (feature.equals("lines3")) {
insertAndAdd(lines3);
} else {
throw new Exception("Unknown Feature");
}
}
@Given("^I have 6 unstaged features$")
public void I_have_6_unstaged_features() throws Throwable {
insertFeatures();
}
@Given("^I have unstaged \"([^\"]*)\"$")
public void I_have_unstaged(String feature) throws Throwable {
if (feature.equals("points1")) {
insert(points1);
} else if (feature.equals("points2")) {
insert(points2);
} else if (feature.equals("points3")) {
insert(points3);
} else if (feature.equals("points1_modified")) {
insert(points1_modified);
} else if (feature.equals("lines1")) {
insert(lines1);
} else if (feature.equals("lines2")) {
insert(lines2);
} else if (feature.equals("lines3")) {
insert(lines3);
} else {
throw new Exception("Unknown Feature");
}
}
@Given("^I have unstaged an empty feature type$")
public void I_have_unstaged_an_empty_feature_type() throws Throwable {
insert(points1);
GeoGIG geogig = geogigCLI.newGeoGIG();
final WorkingTree workTree = geogig.getRepository().workingTree();
workTree.delete(pointsName, idP1);
geogig.close();
}
@Given("^I stage 6 features$")
public void I_stage_6_features() throws Throwable {
insertAndAddFeatures();
}
@Given("^I have several commits$")
public void I_have_several_commits() throws Throwable {
insertAndAdd(points1, points2);
runCommand(true, "commit -m Commit1");
insertAndAdd(points3, lines1);
runCommand(true, "commit -m Commit2");
insertAndAdd(lines2, lines3);
runCommand(true, "commit -m Commit3");
insertAndAdd(points1_modified);
runCommand(true, "commit -m Commit4");
}
@Given("^I have several branches")
public void I_have_several_branches() throws Throwable {
insertAndAdd(points1);
runCommand(true, "commit -m Commit1");
runCommand(true, "branch -c branch1");
insertAndAdd(points2);
runCommand(true, "commit -m Commit2");
insertAndAdd(points3);
runCommand(true, "commit -m Commit3");
runCommand(true, "branch -c branch2");
insertAndAdd(lines1);
runCommand(true, "commit -m Commit4");
runCommand(true, "checkout master");
insertAndAdd(lines2);
runCommand(true, "commit -m Commit5");
}
@Given("I modify and add a feature")
public void I_modify_and_add_a_feature() throws Throwable {
insertAndAdd(points1_modified);
}
@Given("I modify a feature")
public void I_modify_a_feature() throws Throwable {
insert(points1_modified);
}
@Given("^I modify a feature type$")
public void I_modify_a_feature_type() throws Throwable {
deleteAndReplaceFeatureType();
}
@Then("^if I change to the respository subdirectory \"([^\"]*)\"$")
public void if_I_change_to_the_respository_subdirectory(String subdirSpec) throws Throwable {
String[] subdirs = subdirSpec.split("/");
File dir = platform.pwd();
for (String subdir : subdirs) {
dir = new File(dir, subdir);
}
assertTrue(dir.exists());
platform.setWorkingDir(dir);
setupGeogig();
}
@Given("^I have a patch file$")
public void I_have_a_patch_file() throws Throwable {
Patch patch = new Patch();
String path = NodeRef.appendChild(pointsName, points1.getIdentifier().getID());
Map<PropertyDescriptor, AttributeDiff> map = Maps.newHashMap();
Optional<?> oldValue = Optional.fromNullable(points1.getProperty("sp").getValue());
GenericAttributeDiffImpl diff = new GenericAttributeDiffImpl(oldValue, Optional.of("new"));
map.put(pointsType.getDescriptor("sp"), diff);
FeatureDiff feaureDiff = new FeatureDiff(path, map, RevFeatureTypeImpl.build(pointsType),
RevFeatureTypeImpl.build(pointsType));
patch.addModifiedFeature(feaureDiff);
File file = new File(platform.pwd(), "test.patch");
BufferedWriter writer = Files.newWriter(file, Charsets.UTF_8);
PatchSerializer.write(writer, patch);
writer.flush();
writer.close();
}
@Given("^I have an insert file$")
public void I_have_an_insert_file() throws Throwable {
File file = new File(platform.pwd(), "insert");
BufferedWriter writer = Files.newWriter(file, Charsets.UTF_8);
writer.write("Points/Points.1\n");
writer.write("sp\tNew_String\n");
writer.write("ip\t1001\n");
writer.write("pp\tPOINT(2 2)\n");
writer.flush();
writer.close();
}
@Given("^I am inside a repository subdirectory \"([^\"]*)\"$")
public void I_am_inside_a_repository_subdirectory(String subdirSpec) throws Throwable {
String[] subdirs = subdirSpec.split("/");
File dir = platform.pwd();
for (String subdir : subdirs) {
dir = new File(dir, subdir);
}
assertTrue(dir.mkdirs());
platform.setWorkingDir(dir);
}
}