/* * Copyright (c) 2015 Google Inc. * * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse * Public License v1.0 which accompanies this distribution, and is available at * * http://www.eclipse.org/legal/epl-v10.html */ package com.google.eclipse.protobuf.ui.commands.semicolon; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import org.eclipse.swt.custom.StyledTextContent; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; import com.google.eclipse.protobuf.junit.core.XtextRule; import com.google.eclipse.protobuf.protobuf.Group; import com.google.eclipse.protobuf.protobuf.Literal; import com.google.eclipse.protobuf.protobuf.MessageField; import com.google.eclipse.protobuf.ui.plugin.ProtobufEditorPlugIn; import com.google.inject.Inject; public class SmartSemicolonHandlerTest { @Rule public XtextRule xtext = XtextRule.createWith(ProtobufEditorPlugIn.injector()); @Inject private SmartSemicolonHandler handler; // // ignore errors // syntax = "proto2"; // // message Message { // optional bool incomplete // } @Test public void shouldDetermineFirstIndexToBe1() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(1L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool in_message = 2; // optional group outer_group = 4 { // optional bool in_outer_group = 5; // optional group inner_group = 3 { // optional bool in_inner_group = 1; // optional bool incomplete // } // } // } @Test public void shouldDetermineCorrectIndexInsideOfGroups() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(6L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool in_message = 2; // optional group outer_group = 4 { // optional bool in_outer_group = 5; // optional group inner_group = 3 { // optional bool in_inner_group = 1; // } // } // optional bool incomplete // } @Test public void shouldDetermineCorrectIndexOutsideOfGroups() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(6L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool in_message = 2; // message InnerMessage { // optional bool in_inner_message = 4; // optional bool incomplete // } // } @Test public void shouldDetermineCorrectIndexInsideOfNestedMessage() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(5L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool in_message = 2; // message InnerMessage { // optional bool in_inner_message = 4; // } // optional bool incomplete // } @Test public void shouldDetermineCorrectIndexOutsideOfNestedMessage() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(3L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool foo = 1; // reserved 3; // optional bool incomplete // } @Test public void shouldDetermineCorrectIndexWithSingleReserved() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(4L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool foo = 1; // reserved 3, 5 to 7; // optional bool incomplete // } @Test public void shouldDetermineCorrectIndexWithReservedRange() { MessageField incomplete = xtext.find("incomplete", MessageField.class); assertThat(handler.determineNewIndex(incomplete), is(8L)); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool incomplete // } @Test public void shouldComplete() { String incompleteFieldName = "incomplete"; MessageField incomplete = xtext.find(incompleteFieldName, MessageField.class); ICompositeNode node = NodeModelUtils.getNode(incomplete); ReplaceEdit indexEdit = handler.completeWithIndex(node, 1); assertThat(indexEdit.getOffset(), is(xtext.text().indexOf(incompleteFieldName) + incompleteFieldName.length())); assertThat(indexEdit.getText(), is(" = 1;")); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool incomplete // = // } @Test public void shouldCompleteAfterExistingEquals() { MessageField incomplete = xtext.find("incomplete", MessageField.class); ICompositeNode node = NodeModelUtils.getNode(incomplete); ReplaceEdit indexEdit = handler.completeWithIndex(node, 1); String equalsAtStartOfLine = " ="; assertThat(indexEdit.getOffset(), is(xtext.text().indexOf(equalsAtStartOfLine) + equalsAtStartOfLine.length())); assertThat(indexEdit.getText(), is(" 1;")); } // // ignore errors // syntax = "proto2"; // // message Message { // optional bool incomplete [ default = true; ]; // } @Test public void shouldCompleteWithoutSemicolonBeforeOptionBracket() { String incompleteFieldName = "incomplete"; MessageField incomplete = xtext.find(incompleteFieldName, MessageField.class); ICompositeNode node = NodeModelUtils.getNode(incomplete); ReplaceEdit indexEdit = handler.completeWithIndex(node, 1); assertThat(indexEdit.getOffset(), is(xtext.text().indexOf(incompleteFieldName) + incompleteFieldName.length())); assertThat(indexEdit.getText(), is(" = 1 ")); } // // ignore errors // syntax = "proto2"; // // message Message { // optional group incomplete { // } // } @Test public void shouldCompleteWithoutSemicolonBeforeGroupBrace() { String incompleteGroupName = "incomplete"; Group incomplete = xtext.find(incompleteGroupName, Group.class); ICompositeNode node = NodeModelUtils.getNode(incomplete); ReplaceEdit indexEdit = handler.completeWithIndex(node, 1); assertThat(indexEdit.getOffset(), is(xtext.text().indexOf(incompleteGroupName) + incompleteGroupName.length())); assertThat(indexEdit.getText(), is(" = 1 ")); } @Test public void shouldDeleteTrailingWhitespace() { String trailingWhitespace = " "; String lineContent = " optional bool foo" + trailingWhitespace; int lineNumber = 10; int lineStartOffset = 100; int insertionOffset = lineStartOffset + lineContent.lastIndexOf(trailingWhitespace); StyledTextContent content = Mockito.mock(StyledTextContent.class); Mockito.when(content.getLineAtOffset(insertionOffset)).thenReturn(lineNumber); Mockito.when(content.getOffsetAtLine(lineNumber)).thenReturn(lineStartOffset); Mockito.when(content.getLine(lineNumber)).thenReturn(lineContent); TextEdit trailingWhitespaceEdit = handler.deleteTrailingWhitespace(content, insertionOffset); assertThat(trailingWhitespaceEdit.getOffset(), is(insertionOffset)); assertThat(trailingWhitespaceEdit.getLength(), is(trailingWhitespace.length())); } // // ignore errors // syntax = "proto2"; // // // Next Id: 2 // message Message { // optional bool field = 1; // optional bool incomplete // } @Test public void shouldUpdateNextIndexComment() { MessageField incomplete = xtext.find("incomplete", MessageField.class); ReplaceEdit commentEdit = handler.updateNextIndexComment(incomplete, 3); String pattern = "Next Id: "; assertThat(commentEdit.getOffset(), is(xtext.text().indexOf(pattern) + pattern.length())); assertThat(commentEdit.getText(), is("3")); } // // ignore errors // syntax = "proto2"; // // /* // * Next Id: 2 // */ // message Message { // optional bool field = 1; // optional bool incomplete // } @Test public void shouldUpdateMultilineComment() { MessageField incomplete = xtext.find("incomplete", MessageField.class); ReplaceEdit commentEdit = handler.updateNextIndexComment(incomplete, 3); String pattern = "Next Id: "; assertThat(commentEdit.getOffset(), is(xtext.text().indexOf(pattern) + pattern.length())); assertThat(commentEdit.getText(), is("3")); } // // ignore errors // syntax = "proto2"; // // enum Enum { // ONE = 1; // TWO = 2; // THREE = 3; // FOUR // // Next Id: 4 // } @Test public void shouldUpdateNextIndexCommentAtEndOfEnum() { Literal incomplete = xtext.find("FOUR", Literal.class); ReplaceEdit commentEdit = handler.updateNextIndexComment(incomplete, 5); String pattern = "Next Id: "; assertThat(commentEdit.getOffset(), is(xtext.text().indexOf(pattern) + pattern.length())); assertThat(commentEdit.getText(), is("5")); } // // ignore errors // syntax = "proto2"; // // // My Favorite Number: 200 // message Message { // optional bool field = 1; // optional bool incomplete // } @Test public void shouldNotUpdateOtherComment() { MessageField incomplete = xtext.find("incomplete", MessageField.class); ReplaceEdit commentEdit = handler.updateNextIndexComment(incomplete, 3); assertThat(commentEdit, is((ReplaceEdit) null)); } }