Bug 550987 - [tests] Increase test coverage of TextPresentation

and disposal of some, previous ignored, resources.

Change-Id: I227b81264752e90f4557eee74e696d67fd639ba5
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/TextPresentationTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/TextPresentationTest.java
index c67d4c3..41289bb 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/TextPresentationTest.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/TextPresentationTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -13,19 +13,27 @@
  *******************************************************************************/
 package org.eclipse.jface.text.tests;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GlyphMetrics;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
 
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.Region;
 import org.eclipse.jface.text.TextPresentation;
 
@@ -33,14 +41,19 @@
 
 	private static final int NORMAL= SWT.NORMAL;
 	private static final int BOLD= SWT.BOLD;
-//	private static final int ITALIC= SWT.ITALIC;
 
 	private TextPresentation fTextPresentation;
 	private StyleRange[] fAllRanges;
 	private StyleRange[] fNonDefaultRanges;
 
+	// collect colors for disposal
+	private ArrayList<Color> fColors= new ArrayList<>();
+
+	private Display fDisplay;
+
 	@Before
 	public void setUp() {
+		fDisplay= Display.getDefault();
 		setUpStyleRanges();
 		setUpTextPresentation();
 	}
@@ -66,11 +79,22 @@
 	private void setUpTextPresentation() {
 		fTextPresentation= new TextPresentation();
 		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 140, NORMAL));
-		for (StyleRange fAllRange : fAllRanges) {
-			fTextPresentation.addStyleRange(fAllRange);
+		for (StyleRange range : fAllRanges) {
+			fTextPresentation.addStyleRange(range);
 		}
 	}
 
+	@After
+	public void tearDown() {
+		for (Color c : fColors) {
+			if (c != null && !c.isDisposed()) {
+				c.dispose();
+			}
+		}
+		fColors.clear();
+		fDisplay.dispose();
+	}
+
 	private StyleRange createStyleRange(int start, int end, int style) {
 		return createStyleRange(start, end, style, null, null);
 	}
@@ -83,8 +107,6 @@
 		return createStyleRange(start, end, style, createColor(foreground, foreground, foreground), createColor(background, background, background));
 	}
 
-	private Display fDisplay= Display.getDefault();
-
 	/**
 	 * Creates a new color.
 	 *
@@ -96,23 +118,83 @@
 	private Color createColor(int red, int green, int blue) {
 		if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255)
 			return null;
-		return new Color(fDisplay, red, green, blue);
+		Color c= new Color(fDisplay, red, green, blue);
+		fColors.add(c);
+		return c;
 	}
 
-	private void checkRegions(StyleRange[] expectedAllRanges, StyleRange[] expectedNonDefaultRanges) {
-		Iterator<StyleRange> e= fTextPresentation.getAllStyleRangeIterator();
-		for (StyleRange expectedAllRange : expectedAllRanges) {
-			assertTrue(e.hasNext());
-			assertEquals(expectedAllRange, e.next());
-		}
-		assertTrue(!e.hasNext());
+	/**
+	 * Check that the presentation contains the expected style ranges and other presentation methods
+	 * return compatible values.
+	 *
+	 * @param expectedAllRanges all styles expected for the whole presentation
+	 * @param expectedNonDefaultRanges all styles which are not the default for the whole
+	 *            presentation
+	 */
+	protected void checkRegions(StyleRange[] expectedAllRanges, StyleRange[] expectedNonDefaultRanges) {
+		checkRegions(expectedAllRanges, fTextPresentation.getAllStyleRangeIterator(), true);
+		checkRegions(expectedNonDefaultRanges, fTextPresentation.getNonDefaultStyleRangeIterator(), false);
+		// now test the same but remove the default style
+		StyleRange defaultRange= fTextPresentation.getDefaultStyleRange();
+		fTextPresentation.setDefaultStyleRange(null);
+		checkRegions(expectedAllRanges, fTextPresentation.getAllStyleRangeIterator(), true);
+		checkRegions(expectedAllRanges, fTextPresentation.getNonDefaultStyleRangeIterator(), true);
+		fTextPresentation.setDefaultStyleRange(defaultRange);
+	}
 
-		e= fTextPresentation.getNonDefaultStyleRangeIterator();
-		for (StyleRange expectedNonDefaultRange : expectedNonDefaultRanges) {
-			assertTrue(e.hasNext());
-			assertEquals(expectedNonDefaultRange, e.next());
+	/**
+	 * Check expected regions against existing regions from presentation. Also validates that
+	 * iterator returns ordered and disjoint styles and most other presentation methods.
+	 *
+	 * @param expectedRanges the expected regions
+	 * @param rangeIterator iterator from presentation to get the actual regions
+	 * @param withDefaults if <code>true</code> the expected ranges also contains the ranges which
+	 *            are equal to default style
+	 */
+	private void checkRegions(StyleRange[] expectedRanges, Iterator<StyleRange> rangeIterator, boolean withDefaults) {
+		StyleRange defaultRange= fTextPresentation.getDefaultStyleRange();
+		int start= -1, end= -1;
+		int lastEnd= defaultRange != null ? defaultRange.start : Integer.MIN_VALUE;
+		for (int i= 0; i < expectedRanges.length; i++) {
+			StyleRange expectedRange= expectedRanges[i];
+			assertTrue("Presentation has less ranges than expected.", rangeIterator.hasNext());
+			StyleRange actualRange= rangeIterator.next();
+			assertEquals(expectedRange, actualRange);
+			assertTrue("Unexpected default style.", withDefaults || !actualRange.similarTo(defaultRange));
+			assertTrue("Overlapping or wrong ordered style.", lastEnd <= actualRange.start);
+			lastEnd= actualRange.start + actualRange.length;
+
+			// test first and last range methods
+			if (i == 0) {
+				start= actualRange.start;
+				StyleRange first= fTextPresentation.getFirstStyleRange();
+				if (withDefaults) {
+					assertEquals("getFirstStyleRange() failed", expectedRange, first);
+				} else {
+					assertTrue("getFirstStyleRange() failed", first.equals(expectedRange) || first.similarTo(defaultRange));
+				}
+			} else if (i == expectedRanges.length - 1) {
+				end= actualRange.start + actualRange.length;
+				StyleRange last= fTextPresentation.getLastStyleRange();
+				if (withDefaults) {
+					assertEquals("getLastStyleRange() failed", expectedRange, last);
+				} else {
+					assertTrue("getLastStyleRange() failed", last.equals(expectedRange) || last.similarTo(defaultRange));
+				}
+			}
 		}
-		assertTrue(!e.hasNext());
+		assertTrue("Presentation has more ranges than expected.", !rangeIterator.hasNext());
+		if (withDefaults) {
+			assertEquals("getDenumerableRanges() failed", expectedRanges.length, fTextPresentation.getDenumerableRanges());
+		}
+		assertEquals("isEmpty() failed", Boolean.valueOf(expectedRanges.length == 0 && defaultRange == null), Boolean.valueOf(fTextPresentation.isEmpty()));
+		IRegion expectedCover;
+		if (defaultRange == null) {
+			expectedCover= (start == -1 && end == -1 ? null : new Region(start, end - start));
+		} else {
+			expectedCover= new Region(defaultRange.start, defaultRange.length);
+		}
+		assertEquals("Wrong coverage", expectedCover, fTextPresentation.getCoverage());
 	}
 
 	@Test
@@ -561,6 +643,395 @@
 		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
 	}
 
+	/**
+	 * Merge range over whole presentation.
+	 */
+	@Test
+	public void testMergeStyleRange15() {
+		StyleRange range= createStyleRange(0, 140, NORMAL, 1, -1);
+
+		Color expectedColor= createColor(1, 1, 1);
+		StyleRange[] expectedAllRanges= new StyleRange[fAllRanges.length];
+		for (int i= 0; i < fAllRanges.length; i++) {
+			expectedAllRanges[i]= (StyleRange) fAllRanges[i].clone();
+			expectedAllRanges[i].foreground= expectedColor;
+		}
+
+		StyleRange[] expectedNonDefaultRanges= expectedAllRanges;
+
+		fTextPresentation.mergeStyleRange(range);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+
+		range= createStyleRange(-100, 200, BOLD);
+
+		for (int i= 0; i < expectedAllRanges.length; i++) {
+			expectedAllRanges[i].fontStyle= BOLD;
+		}
+
+		fTextPresentation.mergeStyleRange(range);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+		fTextPresentation.setDefaultStyleRange(null);
+		range= createStyleRange(-150, 250, NORMAL, -1, 2);
+
+		expectedColor= createColor(2, 2, 2);
+		for (int i= 0; i < expectedAllRanges.length; i++) {
+			expectedAllRanges[i].background= expectedColor;
+		}
+		StyleRange[] newExpectedRanges= new StyleRange[expectedAllRanges.length + 2];
+		System.arraycopy(expectedAllRanges, 0, newExpectedRanges, 1, expectedAllRanges.length);
+		newExpectedRanges[0]= createStyleRange(-150, 0, NORMAL, -1, 2);
+		newExpectedRanges[newExpectedRanges.length - 1]= createStyleRange(140, 250, NORMAL, -1, 2);
+
+		fTextPresentation.mergeStyleRange(range);
+		checkRegions(newExpectedRanges, newExpectedRanges);
+	}
+
+	/**
+	 * Merge range covering existing default range.
+	 */
+	@Test
+	public void testMergeStyleRange16() {
+		StyleRange range= createStyleRange(20, 47, NORMAL, 1, -1);
+		fTextPresentation.mergeStyleRange(range);
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   4, NORMAL),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 54,  96, NORMAL),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(102, 140, NORMAL),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 96, 102, BOLD),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Merge range covering existing non-default range.
+	 */
+	@Test
+	public void testMergeStyleRange17() {
+		StyleRange range= createStyleRange(47, 54, NORMAL, 1, -1);
+		fTextPresentation.mergeStyleRange(range);
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   4, NORMAL),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL),
+				createStyleRange( 47,  54, BOLD, 1, -1),
+				createStyleRange( 54,  96, NORMAL),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(102, 140, NORMAL),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 47,  54, BOLD, 1, -1),
+				createStyleRange( 96, 102, BOLD),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Merge range covering three ranges.
+	 */
+	@Test
+	public void testMergeStyleRange18() {
+		StyleRange range= createStyleRange(20, 96, NORMAL, 1, -1);
+		fTextPresentation.mergeStyleRange(range);
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   4, NORMAL),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  54, BOLD,   1, -1),
+				createStyleRange( 54,  96, NORMAL, 1, -1),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(102, 140, NORMAL),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  54, BOLD,   1, -1),
+				createStyleRange( 54,  96, NORMAL, 1, -1),
+				createStyleRange( 96, 102, BOLD),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Merge range starting and ending outside.
+	 */
+	@Test
+	public void testMergeStyleRange22() {
+		StyleRange range= createStyleRange(-5, 145, BOLD, 1, -1);
+
+		Color expectedColor= createColor(1, 1, 1);
+		StyleRange[] expectedAllRanges= new StyleRange[fAllRanges.length];
+		for (int i= 0; i < fAllRanges.length; i++) {
+			expectedAllRanges[i]= (StyleRange) fAllRanges[i].clone();
+			expectedAllRanges[i].fontStyle= BOLD;
+			expectedAllRanges[i].foreground= expectedColor;
+		}
+		StyleRange[] expectedNonDefaultRanges= expectedAllRanges;
+
+		fTextPresentation.mergeStyleRange(range);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Merge range starting and ending outside without default style.
+	 */
+	@Test
+	public void testMergeStyleRange23() {
+		fTextPresentation.setDefaultStyleRange(null);
+		StyleRange range= createStyleRange(-5, 145, BOLD, 1, -1);
+
+		Color expectedColor= createColor(1, 1, 1);
+		StyleRange[] expectedAllRanges= new StyleRange[fAllRanges.length + 2];
+		expectedAllRanges[0]= createStyleRange(-5, 0, BOLD, 1, -1);
+		for (int i= 0; i < fAllRanges.length; i++) {
+			expectedAllRanges[i + 1]= (StyleRange) fAllRanges[i].clone();
+			expectedAllRanges[i + 1].fontStyle= BOLD;
+			expectedAllRanges[i + 1].foreground= expectedColor;
+		}
+		expectedAllRanges[expectedAllRanges.length - 1]= createStyleRange(140, 145, BOLD, 1, -1);
+
+		StyleRange[] expectedNonDefaultRanges= expectedAllRanges;
+
+		fTextPresentation.mergeStyleRange(range);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Merge/replace styles on default only presentation.
+	 */
+	@Test
+	public void testApplyStyleRange1() {
+		fTextPresentation.clear();
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 140, NORMAL));
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.mergeStyleRange(range);
+		}
+		checkRegions(fAllRanges, fNonDefaultRanges);
+
+		fTextPresentation.clear();
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 140, NORMAL));
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.replaceStyleRange(range);
+		}
+		checkRegions(fAllRanges, fNonDefaultRanges);
+	}
+
+	/**
+	 * Merge/replace styles without default on empty presentation.
+	 */
+	@Test
+	public void testApplyStyleRange2() {
+		fTextPresentation.clear();
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.mergeStyleRange(range);
+		}
+		checkRegions(fAllRanges, fAllRanges);
+
+		fTextPresentation.clear();
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.replaceStyleRange(range);
+		}
+		checkRegions(fAllRanges, fAllRanges);
+	}
+
+	/**
+	 * Merge/replace styles without default on existing styles.
+	 */
+	@Test
+	public void testApplyStyleRange3() {
+		fTextPresentation.clear();
+		fTextPresentation.mergeStyleRange(createStyleRange(0, 140, NORMAL));
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.mergeStyleRange(range);
+		}
+		checkRegions(fAllRanges, fAllRanges);
+
+		fTextPresentation.clear();
+		fTextPresentation.mergeStyleRange(createStyleRange(0, 140, NORMAL));
+		for (StyleRange range : deepClone(fAllRanges)) {
+			fTextPresentation.replaceStyleRange(range);
+		}
+		checkRegions(fAllRanges, fAllRanges);
+	}
+
+	/**
+	 * Merge/replace empty range.
+	 */
+	@Test
+	public void testApplyStyleRange4() {
+		StyleRange empty= createStyleRange(0, 0, BOLD);
+		fTextPresentation.mergeStyleRange(empty);
+		checkRegions(fAllRanges, fNonDefaultRanges);
+		fTextPresentation.replaceStyleRange(empty);
+		checkRegions(fAllRanges, fNonDefaultRanges);
+	}
+
+	/**
+	 * Test merge operation involving link styling.
+	 */
+	@Test
+	public void testLinkMerge() {
+		Color linkColor= createColor(0, 0, 255);
+		StyleRange[] ranges= new StyleRange[] {
+				createStyleRange(  4,  20, NORMAL),        // default underline
+				createStyleRange( 20,  47, NORMAL),        // explicit default underline
+				createStyleRange( 47,  54, NORMAL),        // double underline
+				createStyleRange( 96, 102, NORMAL, 1, -1), // set color for later test
+				createStyleRange(102, 140, NORMAL),        // link
+		};
+
+		ranges[0].underline= true;
+
+		ranges[1].underline= true;
+		ranges[1].underlineStyle= SWT.UNDERLINE_SINGLE;
+
+		ranges[2].underline= true;
+		ranges[2].underlineStyle= SWT.UNDERLINE_DOUBLE;
+
+		ranges[4].underline= true;
+		ranges[4].underlineStyle= SWT.UNDERLINE_LINK;
+		ranges[4].foreground= linkColor;
+
+		for (StyleRange range : deepClone(ranges)) {
+			fTextPresentation.mergeStyleRange(range);
+		}
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   4, NORMAL),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  47, NORMAL),
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 54,  96, NORMAL),
+				createStyleRange( 96, 102, BOLD, 1, -1),
+				createStyleRange(102, 140, NORMAL),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				expectedAllRanges[1],
+				expectedAllRanges[2],
+				expectedAllRanges[3],
+				expectedAllRanges[5],
+				expectedAllRanges[6],
+		};
+
+		expectedNonDefaultRanges[0].underline= true;
+
+		expectedNonDefaultRanges[1].underline= true;
+		expectedNonDefaultRanges[1].underlineStyle= SWT.UNDERLINE_SINGLE;
+
+		expectedNonDefaultRanges[2].underline= true;
+		expectedNonDefaultRanges[2].underlineStyle= SWT.UNDERLINE_DOUBLE;
+
+		expectedNonDefaultRanges[4].underline= true;
+		expectedNonDefaultRanges[4].underlineStyle= SWT.UNDERLINE_LINK;
+		expectedNonDefaultRanges[4].foreground= linkColor;
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+
+		for (int i= 0; i < 3; i++) {
+			ranges[i].underlineStyle= SWT.UNDERLINE_LINK;
+			ranges[i].foreground= linkColor;
+			expectedNonDefaultRanges[i].underlineStyle= SWT.UNDERLINE_LINK;
+			expectedNonDefaultRanges[i].foreground= linkColor;
+		}
+
+		ranges[3].underlineStyle= SWT.UNDERLINE_LINK;
+		ranges[3].foreground= null;
+		expectedNonDefaultRanges[3].underlineStyle= SWT.UNDERLINE_LINK;
+		expectedNonDefaultRanges[3].foreground= null; // merging underline_link force set the foreground
+
+		ranges[4].underlineStyle= SWT.UNDERLINE_SQUIGGLE;
+		// expect no change for expectedNonDefaultRanges[4]. underline_link cannot removed using merge
+
+		for (StyleRange range : ranges) {
+			fTextPresentation.mergeStyleRange(range);
+		}
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Test merge operation with all style parts.
+	 */
+	@Test
+	public void testMergeAll() {
+		StyleRange range= createStyleRange(4, 20, BOLD, 1, 2);
+		range.borderColor= createColor(3, 3, 3);
+		range.borderStyle= SWT.BORDER_DASH;
+		range.font= fDisplay.getSystemFont();
+		range.metrics= new GlyphMetrics(0, 0, 10);
+		range.strikeout= true;
+		range.strikeoutColor= createColor(4, 4, 4);
+		range.underline= true;
+		range.underlineColor= createColor(5, 5, 5);
+		range.underlineStyle= SWT.UNDERLINE_SQUIGGLE;
+		fTextPresentation.mergeStyleRange((StyleRange) range.clone());
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   4, NORMAL),
+				(StyleRange) range.clone(),
+				createStyleRange( 20,  47, NORMAL),
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 54,  96, NORMAL),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(102, 140, NORMAL),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				(StyleRange) range.clone(),
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 96, 102, BOLD),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+		range.background= null;
+		range.borderColor= null;
+		range.borderStyle= SWT.NONE;
+		range.font= null;
+		range.fontStyle= SWT.NORMAL;
+		range.foreground= null;
+		range.metrics= null;
+		range.strikeout= false;
+		range.strikeoutColor= null;
+		range.underline= false;
+		range.underlineColor= null;
+		range.underlineStyle= SWT.UNDERLINE_SINGLE;
+		fTextPresentation.mergeStyleRange((StyleRange) range.clone());
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+		fTextPresentation.replaceStyleRange((StyleRange) range.clone());
+
+		expectedAllRanges[1]= range;
+		expectedNonDefaultRanges= new StyleRange[] {
+				createStyleRange( 47,  54, BOLD),
+				createStyleRange( 96, 102, BOLD),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
 	@Test
 	public void testMergeStyleRanges1() {
 		StyleRange[] ranges= new StyleRange[] {
@@ -573,7 +1044,7 @@
 				createStyleRange( 52,  54, NORMAL, 1, -1), // at end of existing non-default
 				createStyleRange(138, 140, NORMAL, 1, -1), // at end of last existing
 		};
-		fTextPresentation.mergeStyleRanges(ranges);
+		fTextPresentation.mergeStyleRanges(deepClone(ranges));
 
 		StyleRange[] expectedAllRanges= new StyleRange[] {
 				createStyleRange(  0,   2, NORMAL, 1, -1),
@@ -611,6 +1082,181 @@
 		};
 
 		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+		// now swap fore- and background colors for an easy additional test
+		for (StyleRange r : ranges) {
+			Color c= r.foreground;
+			r.foreground= r.background;
+			r.background= c;
+		}
+		for (StyleRange[] rs : new StyleRange[][] { expectedAllRanges, expectedNonDefaultRanges }) {
+			for (StyleRange r : rs) {
+				r.background= r.foreground;
+			}
+		}
+		fTextPresentation.mergeStyleRanges(ranges);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
 	}
 
+	@Test
+	public void testReplaceStyleRanges1() {
+		StyleRange[] ranges= new StyleRange[] {
+				createStyleRange(  0,   2, NORMAL, 1, -1), // at start of first existing
+				createStyleRange( 20,  22, NORMAL, 1, -1), // at start of existing default
+				createStyleRange( 24,  26, NORMAL, 1, -1), // within existing default
+				createStyleRange( 45,  47, NORMAL, 1, -1), // at end of existing default
+				createStyleRange( 47,  49, NORMAL, 1, -1), // at start of existing non-default
+				createStyleRange( 50,  51, NORMAL, 1, -1), // within existing non-default
+				createStyleRange( 52,  54, NORMAL, 1, -1), // at end of existing non-default
+				createStyleRange(138, 140, NORMAL, 1, -1), // at end of last existing
+		};
+		fTextPresentation.replaceStyleRanges(deepClone(ranges));
+
+		StyleRange[] expectedAllRanges= new StyleRange[] {
+				createStyleRange(  0,   2, NORMAL, 1, -1),
+				createStyleRange(  2,   4, NORMAL),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  22, NORMAL, 1, -1),
+				createStyleRange( 22,  24, NORMAL),
+				createStyleRange( 24,  26, NORMAL, 1, -1),
+				createStyleRange( 26,  45, NORMAL),
+				createStyleRange( 45,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  49, NORMAL, 1, -1),
+				createStyleRange( 49,  50, BOLD),
+				createStyleRange( 50,  51, NORMAL, 1, -1),
+				createStyleRange( 51,  52, BOLD),
+				createStyleRange( 52,  54, NORMAL, 1, -1),
+				createStyleRange( 54,  96, NORMAL),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(102, 138, NORMAL),
+				createStyleRange(138, 140, NORMAL, 1, -1),
+		};
+
+		StyleRange[] expectedNonDefaultRanges= new StyleRange[] {
+				createStyleRange(  0,   2, NORMAL, 1, -1),
+				createStyleRange(  4,  20, BOLD),
+				createStyleRange( 20,  22, NORMAL, 1, -1),
+				createStyleRange( 24,  26, NORMAL, 1, -1),
+				createStyleRange( 45,  47, NORMAL, 1, -1),
+				createStyleRange( 47,  49, NORMAL, 1, -1),
+				createStyleRange( 49,  50, BOLD),
+				createStyleRange( 50,  51, NORMAL, 1, -1),
+				createStyleRange( 51,  52, BOLD),
+				createStyleRange( 52,  54, NORMAL, 1, -1),
+				createStyleRange( 96, 102, BOLD),
+				createStyleRange(138, 140, NORMAL, 1, -1),
+		};
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+
+		// now swap all fore- and background colors for an easy additional test
+		for (StyleRange[] rs : new StyleRange[][] { ranges, expectedAllRanges, expectedNonDefaultRanges }) {
+			for (StyleRange r : rs) {
+				Color c= r.foreground;
+				r.foreground= r.background;
+				r.background= c;
+			}
+		}
+		fTextPresentation.replaceStyleRanges(ranges);
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	/**
+	 * Replace whole presentation.
+	 */
+	@Test
+	public void testReplaceStyleRange2() {
+		StyleRange range= createStyleRange(0, 140, BOLD, 1, -1);
+		fTextPresentation.replaceStyleRange(range);
+
+		Color expectedColor= createColor(1, 1, 1);
+		StyleRange[] expectedAllRanges= new StyleRange[fAllRanges.length];
+		for (int i= 0; i < fAllRanges.length; i++) {
+			expectedAllRanges[i]= (StyleRange) fAllRanges[i].clone();
+			expectedAllRanges[i].fontStyle= BOLD;
+			expectedAllRanges[i].foreground= expectedColor;
+		}
+
+		StyleRange[] expectedNonDefaultRanges= expectedAllRanges;
+
+		checkRegions(expectedAllRanges, expectedNonDefaultRanges);
+	}
+
+	@Test
+	public void testExtend() {
+		fTextPresentation.setDefaultStyleRange(null);
+		assertEquals(new Region(0, 140), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 150, NORMAL));
+		assertEquals(new Region(0, 150), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(null);
+		fTextPresentation.setResultWindow(new Region(100, 10));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 150, NORMAL));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+
+		fTextPresentation= new TextPresentation(new Region(0, 160), 10);
+		assertEquals(new Region(0, 160), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 150, NORMAL));
+		assertEquals(new Region(0, 160), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(null);
+		fTextPresentation.setResultWindow(new Region(100, 10));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+		fTextPresentation.setDefaultStyleRange(createStyleRange(0, 150, NORMAL));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+	}
+
+	@Test
+	public void testExtendUndefined() {
+		fTextPresentation= new TextPresentation(new Region(0, 160), 10);
+		fTextPresentation.setResultWindow(new Region(-10, 5));
+		assertEquals(new Region(10, -5), fTextPresentation.getExtent());
+		fTextPresentation.setResultWindow(new Region(-10, 30));
+		assertEquals(new Region(10, 20), fTextPresentation.getExtent());
+		fTextPresentation.setResultWindow(new Region(150, 30));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+		fTextPresentation.setResultWindow(new Region(200, 30));
+		assertEquals(new Region(0, -40), fTextPresentation.getExtent());
+	}
+
+	@Test
+	public void testEmptyPresentation() {
+		fTextPresentation.clear();
+		checkRegions(new StyleRange[0], new StyleRange[0]);
+		fTextPresentation.setResultWindow(new Region(4, 10));
+		checkRegions(new StyleRange[0], new StyleRange[0]);
+		fTextPresentation.setResultWindow(null);
+
+		StyleRange defaultRange= createStyleRange(0, 50, BOLD);
+		fTextPresentation.setDefaultStyleRange(defaultRange);
+		checkRegions(new StyleRange[0], new StyleRange[0]);
+		fTextPresentation.setResultWindow(new Region(4, 10));
+		checkRegions(new StyleRange[0], new StyleRange[0]);
+
+		fTextPresentation= new TextPresentation(new Region(0, 100), 1);
+		assertEquals(new Region(0, 100), fTextPresentation.getExtent());
+		fTextPresentation.setResultWindow(new Region(4, 10));
+		assertEquals(new Region(0, 10), fTextPresentation.getExtent());
+	}
+
+	@Test
+	public void testApplyTextPresentation() {
+		Shell shell= new Shell(fDisplay);
+		try {
+			StyledText text= new StyledText(shell, SWT.NONE);
+			text.setText(String.join("", Collections.nCopies(fTextPresentation.getCoverage().getLength(), ".")));
+			TextPresentation.applyTextPresentation(fTextPresentation, text);
+			assertArrayEquals(fAllRanges, text.getStyleRanges());
+		} finally {
+			shell.dispose();
+		}
+	}
+
+	// helper method required as long as TextPresentation methods manipulate given arguments
+	private static StyleRange[] deepClone(StyleRange[] original) {
+		StyleRange[] clone= new StyleRange[original.length];
+		for (int i= 0; i < original.length; i++) {
+			clone[i]= (StyleRange) original[i].clone();
+		}
+		return clone;
+	}
 }