blob: e34372024f49aa6a86b88073ce9ed2f0dbd659a8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012 Steffen Pingel and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Steffen Pingel - initial API and implementation
* Benjamin Muskalla - bug 386920
*******************************************************************************/
package org.eclipse.mylyn.trac.tests.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import junit.framework.TestCase;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.mylyn.commons.net.AuthenticationCredentials;
import org.eclipse.mylyn.commons.net.AuthenticationType;
import org.eclipse.mylyn.internal.tasks.core.TaskTask;
import org.eclipse.mylyn.internal.tasks.core.data.TextTaskAttachmentSource;
import org.eclipse.mylyn.internal.tasks.core.sync.SynchronizationSession;
import org.eclipse.mylyn.internal.trac.core.TracAttribute;
import org.eclipse.mylyn.internal.trac.core.TracAttributeMapper;
import org.eclipse.mylyn.internal.trac.core.TracCorePlugin;
import org.eclipse.mylyn.internal.trac.core.TracRepositoryConnector;
import org.eclipse.mylyn.internal.trac.core.TracTaskDataHandler;
import org.eclipse.mylyn.internal.trac.core.TracTaskMapper;
import org.eclipse.mylyn.internal.trac.core.client.ITracClient;
import org.eclipse.mylyn.internal.trac.core.model.TracTicket;
import org.eclipse.mylyn.internal.trac.core.model.TracTicket.Key;
import org.eclipse.mylyn.internal.trac.core.util.TracUtil;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskAttachment;
import org.eclipse.mylyn.tasks.core.RepositoryStatus;
import org.eclipse.mylyn.tasks.core.TaskMapping;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentHandler;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskMapper;
import org.eclipse.mylyn.tasks.core.data.TaskOperation;
import org.eclipse.mylyn.tasks.core.data.TaskRelation;
import org.eclipse.mylyn.trac.tests.support.TracFixture;
import org.eclipse.mylyn.trac.tests.support.TracHarness;
import org.eclipse.mylyn.trac.tests.support.TracTestUtil;
/**
* @author Steffen Pingel
* @author Benjamin Muskalla
*/
public class TracTaskDataHandlerXmlRpcTest extends TestCase {
private TracRepositoryConnector connector;
private TaskRepository repository;
private TracTaskDataHandler taskDataHandler;
private ITracClient client;
private TracHarness harness;
public TracTaskDataHandlerXmlRpcTest() {
}
@Override
protected void setUp() throws Exception {
harness = TracFixture.current().createHarness();
connector = harness.connector();
taskDataHandler = connector.getTaskDataHandler();
repository = harness.repository();
client = connector.getClientManager().getTracClient(repository);
}
@Override
protected void tearDown() throws Exception {
harness.dispose();
}
private SynchronizationSession createSession(ITask... tasks) {
SynchronizationSession session = new SynchronizationSession();
session.setNeedsPerformQueries(true);
session.setTaskRepository(repository);
session.setFullSynchronization(true);
session.setTasks(new HashSet<ITask>(Arrays.asList(tasks)));
return session;
}
public void testMarkStaleTasks() throws Exception {
SynchronizationSession session;
// sleep for one second to ensure that the created ticket has a unique time stamp
Thread.sleep(1000);
TracTicket ticket = harness.createTicket("markStaleTasks");
ITask task = harness.getTask(ticket);
long lastModified = TracUtil.toTracTime(task.getModificationDate());
// an empty set should not cause contact to the repository
repository.setSynchronizationTimeStamp(null);
session = createSession(task);
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertNull(repository.getSynchronizationTimeStamp());
repository.setSynchronizationTimeStamp(null);
session = createSession(task);
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
// always returns the ticket because time comparison mode is >=
repository.setSynchronizationTimeStamp(lastModified + "");
session = createSession(task);
connector.preSynchronization(session, null);
// false since query that check for changed tasks only returns a single task
assertFalse(session.needsPerformQueries());
assertEquals(Collections.emptySet(), session.getStaleTasks());
// nothing has changed, should not detect a change
repository.setSynchronizationTimeStamp((lastModified + 1) + "");
session = createSession(task);
connector.preSynchronization(session, null);
assertFalse(session.needsPerformQueries());
assertEquals(Collections.emptySet(), session.getStaleTasks());
long mostRecentlyModified = 0;
// try changing ticket 3x to make sure it gets a new change time
for (int i = 0; i < 3; i++) {
ticket = client.getTicket(ticket.getId(), null);
ticket.putBuiltinValue(Key.DESCRIPTION, lastModified + "");
client.updateTicket(ticket, "comment", null);
mostRecentlyModified = TracUtil.toTracTime(ticket.getLastChanged());
// needs to be at least one second ahead of repository time stamp
if (mostRecentlyModified >= lastModified + 1) {
break;
} else if (i == 2) {
fail("Failed to update ticket modification time: ticket id=" + ticket.getId() + ", lastModified="
+ lastModified + ", mostRectentlyModified=" + mostRecentlyModified);
}
Thread.sleep(1500);
}
// should now detect a change
repository.setSynchronizationTimeStamp((lastModified + 1) + "");
session = createSession(task);
connector.preSynchronization(session, null);
assertTrue("Expected change: ticket id=" + ticket.getId() + ", lastModified=" + lastModified
+ ", mostRectentlyModified=" + mostRecentlyModified, session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
}
public void testMarkStaleTasksNoTimeStamp() throws Exception {
ITask task = harness.createTask("MarkStaleTasksNoTimeStamp");
SynchronizationSession session = createSession(task);
repository.setSynchronizationTimeStamp(null);
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
session = createSession(task);
repository.setSynchronizationTimeStamp("");
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
session = createSession(task);
repository.setSynchronizationTimeStamp("0");
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
session = createSession(task);
repository.setSynchronizationTimeStamp("abc");
connector.preSynchronization(session, null);
assertTrue(session.needsPerformQueries());
assertEquals(Collections.singleton(task), session.getStaleTasks());
}
public void testNonNumericTaskId() {
try {
connector.getTaskData(repository, "abc", null);
fail("Expected CoreException");
} catch (CoreException e) {
}
}
public void testAttachmentChangesLastModifiedDate() throws Exception {
AbstractTaskAttachmentHandler attachmentHandler = connector.getTaskAttachmentHandler();
ITask task = harness.createTask("attachmentChangesLastModifiedDate");
Date lastModified = task.getModificationDate();
harness.getFixture().waitToGuaranteeTaskUpdate();
// XXX the test case fails when comment == null
attachmentHandler.postContent(repository, task, new TextTaskAttachmentSource("abc"), "comment", null, null);
task = harness.getTask(task.getTaskId());
Date newLastModified = task.getModificationDate();
assertTrue("Expected " + newLastModified + " to be more recent than " + lastModified,
newLastModified.after(lastModified));
}
public void testAttachmentUrlEncoding() throws Exception {
AbstractTaskAttachmentHandler attachmentHandler = connector.getTaskAttachmentHandler();
TracTicket ticket = harness.createTicket("attachment url test");
ITask task = harness.getTask(ticket);
attachmentHandler.postContent(repository, task, new TextTaskAttachmentSource("abc") {
@Override
public String getName() {
return "https%3A%2F%2Fbugs.eclipse.org%2Fbugs.xml.zip";
}
}, "comment", null, null);
task = harness.getTask(ticket);
List<ITaskAttachment> attachments = TracTestUtil.getTaskAttachments(task);
assertEquals(1, attachments.size());
assertEquals(repository.getUrl() + "/attachment/ticket/" + ticket.getId()
+ "/https%253A%252F%252Fbugs.eclipse.org%252Fbugs.xml.zip", attachments.get(0).getUrl());
}
public void testPostTaskDataInvalidCredentials() throws Exception {
TaskData taskData = harness.createTaskData("postTaskDataInvalidCredentials");
taskData.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).setValue("new comment");
repository.setCredentials(AuthenticationType.REPOSITORY, new AuthenticationCredentials("foo", "bar"), false);
try {
taskDataHandler.postTaskData(repository, taskData, null, null);
} catch (CoreException expected) {
assertEquals(RepositoryStatus.ERROR_REPOSITORY_LOGIN, expected.getStatus().getCode());
}
assertEquals("new comment", taskData.getRoot().getMappedAttribute(TaskAttribute.COMMENT_NEW).getValue());
}
public void testCanInitializeTaskData() throws Exception {
ITask task = new TaskTask(TracCorePlugin.CONNECTOR_KIND, "", "");
assertFalse(taskDataHandler.canInitializeSubTaskData(repository, task));
task.setAttribute(TracRepositoryConnector.TASK_KEY_SUPPORTS_SUBTASKS, Boolean.TRUE.toString());
assertTrue(taskDataHandler.canInitializeSubTaskData(repository, task));
}
public void testCanInitializeTaskDataRepositoryTask() throws Exception {
ITask task = harness.createTask("canInitializeTaskDataRepositoryTask");
TaskData taskData = taskDataHandler.getTaskData(repository, task.getTaskId(), null);
assertFalse(taskDataHandler.canInitializeSubTaskData(repository, task));
taskData.getRoot().createAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKED_BY);
connector.updateTaskFromTaskData(repository, task, taskData);
assertTrue(taskDataHandler.canInitializeSubTaskData(repository, task));
task.setAttribute(TracRepositoryConnector.TASK_KEY_SUPPORTS_SUBTASKS, Boolean.FALSE.toString());
connector.updateTaskFromTaskData(repository, task, taskData);
assertTrue(taskDataHandler.canInitializeSubTaskData(repository, task));
}
public void testInitializeSubTaskDataInvalidParent() throws Exception {
TaskData parentTaskData = harness.createTaskData("initializeSubTaskDataInvalidParent");
try {
taskDataHandler.initializeSubTaskData(repository, parentTaskData, parentTaskData, null);
fail("expected CoreException");
} catch (CoreException expected) {
}
}
public void testInitializeSubTaskData() throws Exception {
TaskData parentTaskData = harness.createTaskData("initializeSubTaskData");
TaskMapper parentTaskMapper = new TracTaskMapper(parentTaskData, null);
parentTaskMapper.setSummary("abc");
parentTaskMapper.setDescription("def");
String component = parentTaskData.getRoot()
.getMappedAttribute(TracAttribute.COMPONENT.getTracKey())
.getOptions()
.get(0);
parentTaskMapper.setComponent(component);
parentTaskData.getRoot().createAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKED_BY);
TaskData subTaskData = new TaskData(parentTaskData.getAttributeMapper(), TracCorePlugin.CONNECTOR_KIND, "", "");
subTaskData.getRoot().createAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKING);
taskDataHandler.initializeSubTaskData(repository, subTaskData, parentTaskData, new NullProgressMonitor());
TaskMapper subTaskMapper = new TracTaskMapper(subTaskData, null);
assertEquals("", subTaskMapper.getSummary());
assertEquals("", subTaskMapper.getDescription());
assertEquals(component, subTaskMapper.getComponent());
TaskAttribute attribute = subTaskData.getRoot().getMappedAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKING);
assertEquals(parentTaskData.getTaskId(), attribute.getValue());
attribute = parentTaskData.getRoot().getMappedAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKED_BY);
assertEquals("", attribute.getValue());
}
public void testGetSubTaskIds() throws Exception {
TaskData taskData = new TaskData(new TracAttributeMapper(new TaskRepository("", ""), client),
TracCorePlugin.CONNECTOR_KIND, "", "");
TaskAttribute blockedBy = taskData.getRoot().createAttribute(TracTaskDataHandler.ATTRIBUTE_BLOCKED_BY);
Collection<String> subTaskIds;
blockedBy.setValue("123 456");
subTaskIds = getSubTaskIds(taskData);
assertEquals(2, subTaskIds.size());
assertTrue(subTaskIds.contains("123"));
assertTrue(subTaskIds.contains("456"));
blockedBy.setValue("7,8");
subTaskIds = getSubTaskIds(taskData);
assertEquals(2, subTaskIds.size());
assertTrue(subTaskIds.contains("7"));
assertTrue(subTaskIds.contains("8"));
blockedBy.setValue(" 7 , 8, ");
subTaskIds = getSubTaskIds(taskData);
assertEquals(2, subTaskIds.size());
assertTrue(subTaskIds.contains("7"));
assertTrue(subTaskIds.contains("8"));
blockedBy.setValue("7");
subTaskIds = getSubTaskIds(taskData);
assertEquals(1, subTaskIds.size());
assertTrue(subTaskIds.contains("7"));
blockedBy.setValue("");
subTaskIds = getSubTaskIds(taskData);
assertEquals(0, subTaskIds.size());
blockedBy.setValue(" ");
subTaskIds = getSubTaskIds(taskData);
assertEquals(0, subTaskIds.size());
}
private Collection<String> getSubTaskIds(TaskData taskData) {
List<String> subTaskIds = new ArrayList<String>();
Collection<TaskRelation> relations = connector.getTaskRelations(taskData);
for (TaskRelation taskRelation : relations) {
subTaskIds.add(taskRelation.getTaskId());
}
return subTaskIds;
}
public void testInitializeTaskData() throws Exception {
TaskData taskData = new TaskData(taskDataHandler.getAttributeMapper(repository), TracCorePlugin.CONNECTOR_KIND,
"", "");
TaskMapping mapping = new TaskMapping() {
@Override
public String getDescription() {
return "description";
}
@Override
public String getSummary() {
return "summary";
}
};
taskDataHandler.initializeTaskData(repository, taskData, mapping, new NullProgressMonitor());
// initializeTaskData() should ignore the initialization data
TaskMapper mapper = new TracTaskMapper(taskData, null);
assertEquals(null, mapper.getResolution());
assertEquals("", mapper.getSummary());
assertEquals("", mapper.getDescription());
// check for default values
assertEquals("Defect", mapper.getTaskKind());
assertEquals("major", mapper.getPriority());
// empty attributes should not exist
assertNull(taskData.getRoot().getAttribute(TracAttribute.SEVERITY.getTracKey()));
}
public void testInitializeTaskDataNoMonitor() throws Exception {
connector.getClientManager().repositoryRemoved(repository);
TaskData taskData = new TaskData(new TaskAttributeMapper(repository), TracCorePlugin.CONNECTOR_KIND, "", "");
boolean initialized = taskDataHandler.initializeTaskData(repository, taskData, new TaskMapping(), null);
assertTrue(initialized);
}
public void testOperations() throws Exception {
boolean hasReassign = TracFixture.current().getVersion().compareTo("0.11") >= 0;
TaskData taskData = taskDataHandler.getTaskData(repository, "1", new NullProgressMonitor());
List<TaskAttribute> operations = taskData.getAttributeMapper().getAttributesByType(taskData,
TaskAttribute.TYPE_OPERATION);
assertEquals("Unexpected operations: " + operations, (hasReassign ? 5 : 4), operations.size());
TaskOperation operation = taskData.getAttributeMapper().getTaskOperation(operations.get(0));
assertEquals(TaskAttribute.OPERATION, operation.getTaskAttribute().getId());
operation = taskData.getAttributeMapper().getTaskOperation(operations.get(1));
assertEquals("leave", operation.getOperationId());
assertNotNull(operation.getLabel());
operation = taskData.getAttributeMapper().getTaskOperation(operations.get(2));
assertEquals("resolve", operation.getOperationId());
assertNotNull(operation.getLabel());
String associatedId = operation.getTaskAttribute()
.getMetaData()
.getValue(TaskAttribute.META_ASSOCIATED_ATTRIBUTE_ID);
assertNotNull(associatedId);
if (hasReassign) {
operation = taskData.getAttributeMapper().getTaskOperation(operations.get(3));
assertEquals("reassign", operation.getOperationId());
assertNotNull(operation.getLabel());
operation = taskData.getAttributeMapper().getTaskOperation(operations.get(4));
assertEquals("accept", operation.getOperationId());
assertNotNull(operation.getLabel());
} else {
operation = taskData.getAttributeMapper().getTaskOperation(operations.get(3));
assertEquals("accept", operation.getOperationId());
assertNotNull(operation.getLabel());
}
}
public void testPostTaskDataUnsetResolution() throws Exception {
TracTicket ticket = harness.createTicket("postTaskDataUnsetResolution");
TaskData taskData = taskDataHandler.getTaskData(repository, ticket.getId() + "", new NullProgressMonitor());
TaskAttribute attribute = taskData.getRoot().getMappedAttribute(TaskAttribute.RESOLUTION);
attribute.setValue("fixed");
taskDataHandler.postTaskData(repository, taskData, null, new NullProgressMonitor());
// should not set resolution unless resolve operation is selected
taskData = taskDataHandler.getTaskData(repository, ticket.getId() + "", new NullProgressMonitor());
attribute = taskData.getRoot().getMappedAttribute(TaskAttribute.RESOLUTION);
assertEquals("", attribute.getValue());
}
public void testPostTaskDataMidAirCollision() throws Exception {
TracTicket ticket = harness.createTicket("midAirCollision");
if (ticket.getValue(Key.TOKEN) == null) {
// repository does not have mid-air collision support
System.err.println("Skipping TracTaskDataHandler.testPostTaskDataMidAirCollision() due to lack of mid-air collision support on "
+ repository.getRepositoryUrl());
return;
}
TaskData taskData = taskDataHandler.getTaskData(repository, ticket.getId() + "", new NullProgressMonitor());
TaskAttribute attribute = taskData.getRoot().getMappedAttribute(TaskAttribute.PRIORITY);
attribute.setValue("blocker");
// change ticket in repository
ticket.putBuiltinValue(Key.PRIORITY, "trivial");
client.updateTicket(ticket, "changing priority", null);
harness.getFixture().waitToGuaranteeTaskUpdate();
// submit conflicting change
try {
taskDataHandler.postTaskData(repository, taskData, null, new NullProgressMonitor());
fail("Expected CoreException due to mid-air collision");
} catch (CoreException e) {
assertEquals(RepositoryStatus.createCollisionError(repository.getRepositoryUrl(), TracCorePlugin.ID_PLUGIN)
.getMessage(), e.getStatus().getMessage());
}
}
}