Skip to content

Commit f231fc1

Browse files
authored
CHE-4536: Prevent removing breakpoint annotations (eclipse-che#6588)
1 parent 59abab9 commit f231fc1

13 files changed

Lines changed: 221 additions & 65 deletions

File tree

ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/debug/BreakpointManagerImpl.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,19 +296,22 @@ public void onLineChange(
296296
final List<Breakpoint> toAdd = new ArrayList<>();
297297

298298
for (final Breakpoint breakpoint : fileBreakpoints) {
299-
final int lineNumber = breakpoint.getLocation().getLineNumber();
299+
final int lineNumber = breakpoint.getLocation().getLineNumber() - 1;
300300

301-
// before any change
302-
if (firstLine > lineNumber) {
301+
// line removed
302+
if (firstLine <= lineNumber && lineNumber < firstLine + linesRemoved) {
303+
toRemove.add(breakpoint);
303304
continue;
304305
}
305306

306-
// in the middle
307-
if (firstLine + Math.abs(delta) > lineNumber && lineNumber >= firstLine) {
307+
// line modified
308+
if (firstLine <= lineNumber && lineNumber < firstLine + linesAdded) {
308309
toRemove.add(breakpoint);
309-
toAdd.add(breakpoint);
310+
continue;
311+
}
310312

311-
} else {
313+
// line shifted
314+
if (lineNumber >= firstLine + Math.abs(delta)) {
312315
Location currentLocation = breakpoint.getLocation();
313316
Location newLocation =
314317
new LocationImpl(

ide/che-core-orion-editor/src/main/java/org/eclipse/che/ide/editor/orion/client/OrionBreakpointRuler.java

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public class OrionBreakpointRuler implements Gutter {
3535
private final OrionEditorOverlay editorOverlay;
3636
private final OrionAnnotationModelOverlay annotationModel;
3737

38-
private OrionTextModelOverlay.EventHandler<ModelChangedEventOverlay> modelChangingEventHandler;
38+
private OrionTextModelOverlay.EventHandler<ModelChangedEventOverlay> afterChangedEventHandler;
39+
private OrionTextModelOverlay.EventHandler<ModelChangedEventOverlay> beforeChangedEventHandler;
3940

4041
public OrionBreakpointRuler(OrionExtRulerOverlay rulerOverlay, OrionEditorOverlay editorOverlay) {
4142
this.orionExtRulerOverlay = rulerOverlay;
@@ -64,24 +65,38 @@ public void addGutterItem(
6465
}
6566

6667
addGutterItem(line, gutterId, element);
67-
if (modelChangingEventHandler == null) {
68-
modelChangingEventHandler =
69-
new OrionTextModelOverlay.EventHandler<ModelChangedEventOverlay>() {
70-
@Override
71-
public void onEvent(ModelChangedEventOverlay parameter) {
72-
int linesAdded = parameter.addedLineCount();
73-
int linesRemoved = parameter.removedLineCount();
74-
int fromLine = editorOverlay.getModel().getLineAtOffset(parameter.start());
75-
String line = editorOverlay.getModel().getLine(fromLine);
76-
77-
if (linesAdded > 0 || linesRemoved > 0 || line.trim().isEmpty()) {
78-
removeAnnotations(getAnnotationsFrom(fromLine));
79-
lineCallback.onLineNumberingChange(fromLine, linesRemoved, linesAdded);
68+
if (beforeChangedEventHandler == null) {
69+
beforeChangedEventHandler =
70+
parameter -> {
71+
int linesAdded = parameter.addedLineCount();
72+
int linesRemoved = parameter.removedLineCount();
73+
int fromLine = editorOverlay.getModel().getLineAtOffset(parameter.start());
74+
75+
if (linesAdded > 0 || linesRemoved > 0) {
76+
for (int i = fromLine; i < fromLine + Math.abs(linesAdded - linesRemoved); i++) {
77+
removeAnnotations(getAnnotations(i));
8078
}
8179
}
8280
};
8381

84-
this.editorOverlay.getModel().addEventListener("Changed", modelChangingEventHandler, false);
82+
this.editorOverlay.getModel().addEventListener("Changing", beforeChangedEventHandler, false);
83+
}
84+
85+
if (afterChangedEventHandler == null) {
86+
afterChangedEventHandler =
87+
parameter -> {
88+
int linesAdded = parameter.addedLineCount();
89+
int linesRemoved = parameter.removedLineCount();
90+
int fromLine = editorOverlay.getModel().getLineAtOffset(parameter.start());
91+
92+
if (linesAdded > 0
93+
|| linesRemoved > 0
94+
|| editorOverlay.getModel().getLine(fromLine).isEmpty()) {
95+
lineCallback.onLineNumberingChange(fromLine, linesRemoved, linesAdded);
96+
}
97+
};
98+
99+
this.editorOverlay.getModel().addEventListener("Changed", afterChangedEventHandler, false);
85100
}
86101
}
87102

@@ -147,8 +162,6 @@ private void removeAnnotations(OrionAnnotationOverlay[] annotations) {
147162

148163
private OrionAnnotationOverlay toAnnotation(Element element, int line) {
149164
int lineStart = editorOverlay.getModel().getLineStart(line);
150-
int lineEnd = editorOverlay.getModel().getLineEnd(line);
151-
152165
OrionAnnotationOverlay annotation = OrionAnnotationOverlay.create();
153166

154167
OrionStyleOverlay styleOverlay = OrionStyleOverlay.create();
@@ -157,20 +170,14 @@ private OrionAnnotationOverlay toAnnotation(Element element, int line) {
157170
annotation.setStyle(styleOverlay);
158171
annotation.setType(CHE_BREAKPOINT);
159172
annotation.setStart(lineStart);
160-
annotation.setEnd(lineEnd);
173+
annotation.setEnd(lineStart);
161174

162175
return annotation;
163176
}
164177

165178
private OrionAnnotationOverlay[] getAnnotations(int line) {
166179
int lineStart = editorOverlay.getModel().getLineStart(line);
167-
int lineEnd = editorOverlay.getModel().getLineEnd(line);
168-
return doGetAnnotations(lineStart, lineEnd);
169-
}
170-
171-
private OrionAnnotationOverlay[] getAnnotationsFrom(int fromLine) {
172-
int lineStart = editorOverlay.getModel().getLineStart(fromLine);
173-
return doGetAnnotations(lineStart, Integer.MAX_VALUE);
180+
return doGetAnnotations(lineStart, lineStart);
174181
}
175182

176183
private OrionAnnotationOverlay[] getAllAnnotations() {

selenium/che-selenium-test/src/main/java/org/eclipse/che/selenium/pageobject/CodenvyEditor.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOf;
2626
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfAllElementsLocatedBy;
2727
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated;
28+
import static org.testng.Assert.fail;
2829

2930
import com.google.inject.Inject;
3031
import com.google.inject.Singleton;
@@ -1160,7 +1161,7 @@ public void setBreakPointAndWaitActiveState(int position) {
11601161
seleniumWebDriver
11611162
.findElement(By.xpath(String.format(Locators.DEBUGGER_PREFIX_XPATH, position)))
11621163
.click();
1163-
waitAcitveBreakpoint(position);
1164+
waitActiveBreakpoint(position);
11641165
}
11651166

11661167
public void setBreakpoint(int position) {
@@ -1176,12 +1177,32 @@ public void setBreakpoint(int position) {
11761177
*
11771178
* @param position the position in the codenvy - editor
11781179
*/
1179-
public void waitAcitveBreakpoint(int position) {
1180+
public void waitActiveBreakpoint(int position) {
11801181
redrawDriverWait.until(
11811182
visibilityOfElementLocated(
11821183
By.xpath(String.format(Locators.DEBUGGER_BREAK_POINT_ACTIVE, position))));
11831184
}
11841185

1186+
public void waitInactiveBreakpoint(int position) {
1187+
redrawDriverWait.until(
1188+
visibilityOfElementLocated(
1189+
By.xpath(String.format(Locators.DEBUGGER_BREAK_POINT_INACTIVE, position))));
1190+
}
1191+
1192+
public void waitBreakpointRemoved(int position) {
1193+
try {
1194+
waitInactiveBreakpoint(position);
1195+
fail("Breakpoint should be removed at " + position);
1196+
} catch (Exception e) {
1197+
}
1198+
1199+
try {
1200+
waitActiveBreakpoint(position);
1201+
fail("Breakpoint should be removed at " + position);
1202+
} catch (Exception e) {
1203+
}
1204+
}
1205+
11851206
/** wait while editor will be empty */
11861207
public void waitEditorIsEmpty() {
11871208
elemDriverWait.until((WebDriver driver) -> getVisibleTextFromEditor().isEmpty());
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2012-2017 Red Hat, Inc.
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
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
*/
11+
package org.eclipse.che.selenium.debugger;
12+
13+
import com.google.inject.Inject;
14+
import java.nio.file.Paths;
15+
import org.eclipse.che.commons.lang.NameGenerator;
16+
import org.eclipse.che.selenium.core.SeleniumWebDriver;
17+
import org.eclipse.che.selenium.core.action.ActionsFactory;
18+
import org.eclipse.che.selenium.core.client.TestProjectServiceClient;
19+
import org.eclipse.che.selenium.core.project.ProjectTemplates;
20+
import org.eclipse.che.selenium.core.workspace.TestWorkspace;
21+
import org.eclipse.che.selenium.pageobject.CodenvyEditor;
22+
import org.eclipse.che.selenium.pageobject.Ide;
23+
import org.eclipse.che.selenium.pageobject.ProjectExplorer;
24+
import org.eclipse.che.selenium.pageobject.debug.DebugPanel;
25+
import org.openqa.selenium.Keys;
26+
import org.testng.annotations.BeforeClass;
27+
import org.testng.annotations.Test;
28+
29+
/** @author Musienko Maxim */
30+
public class BreakpointReorderingTest {
31+
private static final String PROJECT_NAME = NameGenerator.generate("project", 2);
32+
33+
@Inject private TestWorkspace ws;
34+
@Inject private Ide ide;
35+
36+
@Inject private ProjectExplorer projectExplorer;
37+
@Inject private CodenvyEditor editor;
38+
@Inject private DebugPanel debugPanel;
39+
@Inject private TestProjectServiceClient testProjectServiceClient;
40+
@Inject private SeleniumWebDriver seleniumWebDriver;
41+
@Inject private ActionsFactory actionsFactory;
42+
43+
@BeforeClass
44+
public void setUp() throws Exception {
45+
testProjectServiceClient.importProject(
46+
ws.getId(),
47+
Paths.get(getClass().getResource("/projects/debug-spring-project").toURI()),
48+
PROJECT_NAME,
49+
ProjectTemplates.MAVEN_SPRING);
50+
51+
ide.open(ws);
52+
projectExplorer.waitItem(PROJECT_NAME);
53+
projectExplorer.quickExpandWithJavaScript();
54+
debugPanel.openDebugPanel();
55+
56+
projectExplorer.openItemByPath(
57+
PROJECT_NAME + "/src/main/java/org/eclipse/qa/examples/AppController.java");
58+
59+
editor.setCursorToLine(26);
60+
editor.setInactiveBreakpoint(26);
61+
editor.setInactiveBreakpoint(29);
62+
editor.setInactiveBreakpoint(31);
63+
editor.setCursorToLine(38);
64+
editor.setInactiveBreakpoint(38);
65+
}
66+
67+
@Test
68+
public void shouldNotRemoveBreakpointWhenFirstCharacterRemoved() throws Exception {
69+
editor.setCursorToDefinedLineAndChar(26, 1);
70+
actionsFactory.createAction(seleniumWebDriver).sendKeys(Keys.DELETE).build().perform();
71+
72+
editor.waitInactiveBreakpoint(26);
73+
}
74+
75+
@Test(priority = 1)
76+
public void shouldNotRemoveBreakpointWhenLastCharacterRemoved() throws Exception {
77+
editor.setCursorToDefinedLineAndChar(26, 65);
78+
actionsFactory.createAction(seleniumWebDriver).sendKeys(Keys.BACK_SPACE).build().perform();
79+
80+
editor.waitInactiveBreakpoint(26);
81+
}
82+
83+
@Test(priority = 2)
84+
public void shouldReorderBreakpointsWhenLineRemoved() throws Exception {
85+
editor.deleteCurrentLine();
86+
87+
editor.waitInactiveBreakpoint(28);
88+
editor.waitInactiveBreakpoint(30);
89+
editor.waitInactiveBreakpoint(37);
90+
91+
actionsFactory
92+
.createAction(seleniumWebDriver)
93+
.keyDown(Keys.CONTROL)
94+
.sendKeys("z")
95+
.keyUp(Keys.CONTROL)
96+
.perform();
97+
98+
editor.waitInactiveBreakpoint(29);
99+
editor.waitInactiveBreakpoint(31);
100+
editor.waitInactiveBreakpoint(38);
101+
}
102+
103+
@Test(priority = 3)
104+
public void shouldReorderBreakpointsWhenLineAdded() throws Exception {
105+
editor.setCursorToDefinedLineAndChar(26, 1);
106+
actionsFactory.createAction(seleniumWebDriver).sendKeys(Keys.ENTER).build().perform();
107+
108+
editor.waitInactiveBreakpoint(30);
109+
editor.waitInactiveBreakpoint(32);
110+
editor.waitInactiveBreakpoint(39);
111+
112+
actionsFactory
113+
.createAction(seleniumWebDriver)
114+
.keyDown(Keys.CONTROL)
115+
.sendKeys("z")
116+
.keyUp(Keys.CONTROL)
117+
.perform();
118+
119+
editor.waitInactiveBreakpoint(29);
120+
editor.waitInactiveBreakpoint(31);
121+
editor.waitInactiveBreakpoint(38);
122+
}
123+
}

selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/debugger/ChangeVariableWithEvaluatingTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public void changeVariableTest() throws Exception {
131131
+ workspaceServiceClient.getServerAddressByPort(ws.getId(), 8080)
132132
+ "/spring/guess";
133133
String requestMess = "11";
134-
editor.waitAcitveBreakpoint(34);
134+
editor.waitActiveBreakpoint(34);
135135
CompletableFuture<String> instToRequestThread =
136136
debuggerUtils.gotoDebugAppAndSendRequest(appUrl, requestMess);
137137
debugPanel.openDebugPanel();

selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/debugger/CheckBreakPointStateTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.net.URL;
1515
import java.nio.file.Paths;
1616
import org.eclipse.che.commons.lang.NameGenerator;
17+
import org.eclipse.che.selenium.core.SeleniumWebDriver;
1718
import org.eclipse.che.selenium.core.client.TestProjectServiceClient;
1819
import org.eclipse.che.selenium.core.constant.TestMenuCommandsConstants;
1920
import org.eclipse.che.selenium.core.project.ProjectTemplates;
@@ -30,10 +31,8 @@
3031

3132
/** @author Musienko Maxim */
3233
public class CheckBreakPointStateTest {
33-
private static final String PROJECT_NAME =
34-
NameGenerator.generate(CheckBreakPointStateTest.class.getSimpleName(), 3);
35-
private static final String PROJECT_NAME_2 =
36-
NameGenerator.generate(CheckBreakPointStateTest.class.getSimpleName(), 2);
34+
private static final String PROJECT_NAME = NameGenerator.generate("project", 3);
35+
private static final String PROJECT_NAME_2 = NameGenerator.generate("project", 3);
3736
private static final String PATH_PREFFIX = "/src/main/java/org/eclipse/qa/examples/";
3837
private static final String PATH_TO_PROJECT_WITH_ONE_CLASS = PROJECT_NAME + PATH_PREFFIX;
3938
private static final String PATH_TO_PROJECT_WITH_TWO_CLASSES = PROJECT_NAME_2 + PATH_PREFFIX;
@@ -48,6 +47,7 @@ public class CheckBreakPointStateTest {
4847
@Inject private Loader loader;
4948
@Inject private AskDialog askDialog;
5049
@Inject private TestProjectServiceClient testProjectServiceClient;
50+
@Inject private SeleniumWebDriver seleniumWebDriver;
5151

5252
@BeforeClass
5353
public void setUp() throws Exception {
@@ -61,6 +61,9 @@ public void setUp() throws Exception {
6161
ws.getId(), Paths.get(resource.toURI()), PROJECT_NAME_2, ProjectTemplates.MAVEN_SPRING);
6262

6363
ide.open(ws);
64+
projectExplorer.waitItem(PROJECT_NAME);
65+
projectExplorer.waitItem(PROJECT_NAME_2);
66+
projectExplorer.quickExpandWithJavaScript();
6467
}
6568

6669
@Test
@@ -71,8 +74,6 @@ public void checkStateAfterDeletionFileAndFolder() throws Exception {
7174
String expectedBreakpointsForGreetingClass =
7275
"AppController.java:29\n" + "AppController.java:30\n" + "AppController.java:31";
7376

74-
projectExplorer.waitItem(PROJECT_NAME_2);
75-
projectExplorer.quickExpandWithJavaScript();
7677
projectExplorer.openItemByPath(PATH_TO_PROJECT_WITH_TWO_CLASSES + "AdditonalClass.java");
7778
editor.waitActiveEditor();
7879
editor.setInactiveBreakpoint(7);
@@ -119,6 +120,6 @@ public void checkStateAfterDeletionProject() {
119120
askDialog.confirmAndWaitClosed();
120121
projectExplorer.waitDisappearItemByPath(PROJECT_NAME);
121122
debugPanel.waitBreakPointsPanelIsEmpty();
122-
ide.driver().navigate().refresh();
123+
seleniumWebDriver.navigate().refresh();
123124
}
124125
}

selenium/che-selenium-test/src/test/java/org/eclipse/che/selenium/debugger/DebugExternalClassTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public void shouldDebugJreClass() {
129129
debugConfig.getXpathToІRunDebugCommand(PROJECT));
130130

131131
notifications.waitExpectedMessageOnProgressPanelAndClosed("Remote debugger connected");
132-
editor.waitAcitveBreakpoint(19);
132+
editor.waitActiveBreakpoint(19);
133133
debugPanel.clickOnButton(DebugPanel.DebuggerButtonsPanel.STEP_INTO);
134134

135135
// then
@@ -156,7 +156,7 @@ public void shouldDebugMavenArtifactClassWithSources() {
156156
debugConfig.getXpathToІRunDebugCommand(PROJECT));
157157

158158
notifications.waitExpectedMessageOnProgressPanelAndClosed("Remote debugger connected");
159-
editor.waitAcitveBreakpoint(23);
159+
editor.waitActiveBreakpoint(23);
160160
debugPanel.clickOnButton(DebugPanel.DebuggerButtonsPanel.STEP_INTO);
161161

162162
// then
@@ -195,7 +195,7 @@ public void shouldHandleDebugOfMavenArtifactWithoutSources() {
195195
debugConfig.getXpathToІRunDebugCommand(PROJECT));
196196

197197
notifications.waitExpectedMessageOnProgressPanelAndClosed("Remote debugger connected");
198-
editor.waitAcitveBreakpoint(27);
198+
editor.waitActiveBreakpoint(27);
199199
debugPanel.clickOnButton(DebugPanel.DebuggerButtonsPanel.STEP_INTO);
200200

201201
// then

0 commit comments

Comments
 (0)