/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems) - Adapted for CDT
* Nathan Ridge - refactoring
*******************************************************************************/
package org.eclipse.cdt.ui.tests.text;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.testplugin.Accessor;
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.ui.testplugin.ResourceTestHelper;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlighting;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedPosition;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingPresenter;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Semantic highlighting tests.
*
* <p>Derived from JDT.<p>
*
* @since 4.0
*/
public class SemanticHighlightingTest extends TestCase {
public static Test suite() {
return new TestSuite(SemanticHighlightingTest.class);
}
private File fExternalFile;
private ICProject fCProject;
private CEditor fEditor;
private SourceViewer fSourceViewer;
private IIndex fIndex;
private IASTTranslationUnit fAST;
// The highlighted positions stored in the document don't store any information
// that directly identifies which highligting they are for. To recover this
// information, we assign a different color to each highlighting, and then
// look up the highlighting's preference key based on the color.
private Map<RGB, String> fColorToPreferenceKeyMap;
private static File createExternalFile(final String code) throws Exception {
File dest = File.createTempFile("external", ".h");
FileOutputStream fos = new FileOutputStream(dest);
fos.write(code.getBytes());
fos.close();
return dest;
}
private void enableHighlightingsAndAssignColors() {
fColorToPreferenceKeyMap = new HashMap<>();
IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
store.setValue(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED, true);
SemanticHighlighting[] semanticHighlightings= SemanticHighlightings.getSemanticHighlightings();
int blue = 0; // for assigning colors to preferences below
for (SemanticHighlighting semanticHighlighting : semanticHighlightings) {
// Enable the highlighting.
String enabledPreferenceKey = SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting);
if (!store.getBoolean(enabledPreferenceKey))
store.setValue(enabledPreferenceKey, true);
// Choose a unique color for the highlighting, and save the mapping
// from the color to the highlighting's preference key .
String colorPreferenceKey = SemanticHighlightings.getColorPreferenceKey(semanticHighlighting);
RGB color = new RGB(0, 0, blue++); // every highlighting gets a different shade of blue
PreferenceConverter.setValue(store, colorPreferenceKey, color);
fColorToPreferenceKeyMap.put(color, semanticHighlighting.getPreferenceKey());
}
}
private void restorePreferencesToDefaults() {
IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
store.setToDefault(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED);
SemanticHighlighting[] semanticHighlightings= SemanticHighlightings.getSemanticHighlightings();
for (SemanticHighlighting semanticHighlighting : semanticHighlightings) {
String enabledPreferenceKey= SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting);
if (!store.isDefault(enabledPreferenceKey))
store.setToDefault(enabledPreferenceKey);
String colorPreferenceKey = SemanticHighlightings.getColorPreferenceKey(semanticHighlighting);
store.setToDefault(colorPreferenceKey);
}
fColorToPreferenceKeyMap.clear();
}
// Note: This is not an override of the TestCase.setUp(), but a method called directly
// by the tests, so that they can specify a value for 'isCpp' on a per-test basis.
private void setup(boolean isCpp) throws Exception {
enableHighlightingsAndAssignColors();
StringBuilder[] testData = TestSourceReader.getContentsForTest(CTestPlugin.getDefault().getBundle(), "ui", getClass(), getName(), 0);
if (testData.length == 2) {
fExternalFile= createExternalFile(testData[0].toString());
assertNotNull(fExternalFile);
// Load the file using option -include to make it part of the index.
TestScannerProvider.sIncludeFiles= new String[] {fExternalFile.getAbsolutePath()};
}
fCProject= CProjectHelper.createCCProject("SHTest", "bin", IPDOMManager.ID_FAST_INDEXER);
String sourceFileName = isCpp ? "SHTest.cpp" : "SHTest.c";
IFile sourceFile = TestSourceReader.createFile(fCProject.getProject(), new Path(sourceFileName),
testData.length == 2 ? testData[1].toString() : testData[0].toString());
IIndexManager indexManager= CCorePlugin.getIndexManager();
indexManager.joinIndexer(5000, new NullProgressMonitor());
BaseTestCase.waitForIndexer(fCProject);
fEditor= (CEditor) EditorTestHelper.openInEditor(ResourceTestHelper.findFile("/SHTest/" + sourceFileName), true);
fSourceViewer= EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
EditorTestHelper.joinBackgroundActivities();
fIndex = CCorePlugin.getIndexManager().getIndex(fCProject);
fIndex.acquireReadLock();
fAST = TestSourceReader.createIndexBasedAST(fIndex, fCProject, sourceFile);
}
private void teardown() throws Exception {
fIndex.releaseReadLock();
EditorTestHelper.closeEditor(fEditor);
if (fCProject != null)
CProjectHelper.delete(fCProject);
if (fExternalFile != null) {
fExternalFile.delete();
}
TestScannerProvider.sIncludeFiles= null;
restorePreferencesToDefaults();
}
private Position[] getSemanticHighlightingPositions() throws BadPositionCategoryException {
SemanticHighlightingManager manager= (SemanticHighlightingManager) new Accessor(fEditor, CEditor.class).get("fSemanticManager");
SemanticHighlightingPresenter presenter= (SemanticHighlightingPresenter) new Accessor(manager, manager.getClass()).get("fPresenter");
String positionCategory= (String) new Accessor(presenter, presenter.getClass()).invoke("getPositionCategory", new Object[0]);
IDocument document= fSourceViewer.getDocument();
return document.getPositions(positionCategory);
}
private void makeAssertions(boolean isCpp) throws Exception {
setup(isCpp);
IDocument document = fSourceViewer.getDocument();
int lines = document.getNumberOfLines();
List<String>[] expected = new List[lines];
for (int i = 0; i < lines; ++i) {
expected[i] = new ArrayList<String>();
}
for (IASTComment comment : fAST.getComments()) {
String contents = new String(comment.getComment());
if (contents.length() > 2 && contents.substring(0, 3).equals("//$")) {
for (String component : contents.substring(3).split(",")) {
// subtract 1 to make it into a 0-based line number
expected[comment.getFileLocation().getStartingLineNumber() - 1].add(component);
}
}
}
List<String>[] actual = new List[lines];
for (int i = 0; i < lines; ++i) {
actual[i] = new ArrayList<String>();
}
for (Position p : getSemanticHighlightingPositions()) {
assertTrue(p instanceof HighlightedPosition);
RGB color = ((HighlightedPosition) p).getHighlighting().getTextAttribute().getForeground().getRGB();
assertTrue(fColorToPreferenceKeyMap.containsKey(color));
int line = document.getLineOfOffset(p.getOffset());
actual[line].add(fColorToPreferenceKeyMap.get(color));
}
assertEqualMaps(actual, expected);
teardown();
}
private void makeAssertions() throws Exception {
makeAssertions(true); // default to C++
}
private void assertEqualMaps(List<String>[] actual, List<String>[] expected) {
assertEquals(expected.length, actual.length);
for (int i = 0; i < actual.length; ++i) {
assertEquals("Expected " + expected[i].size() + " positions on line " + i + ", got " + actual[i].size(),
expected[i].size(), actual[i].size());
for (int j = 0; j < actual[i].size(); ++j) {
assertEquals(expected[i].get(j), actual[i].get(j));
}
}
}
// void SDKFunction();
// class SDKClass { public: void SDKMethod(); };\n\n";
//#define INT int //$macroDefinition
//#define FUNCTION_MACRO(arg) globalFunc(arg) //$macroDefinition
//#define EMPTY_MACRO(arg) //$macroDefinition
//#include "SHTest.h"
//enum Enumeration { //$enum
// enumerator //$enumerator
//};
//
//const int globalConstant = 0; //$globalVariable
//int globalVariable = 0; //$globalVariable
//static int globalStaticVariable = 0; //$globalVariable
//
//void globalFunc(int a); //$functionDeclaration,parameterVariable
//static void globalStaticFunc() { //$functionDeclaration
// EMPTY_MACRO(n); //$macroSubstitution
//};
//
//class Base1 {}; //$class
//class Base2 {}; //$class
//
//class ClassContainer : Base1, Base2 { //$class,class,class
// friend void friendFunc(); //$functionDeclaration
// friend class FriendClass; //$class
//
//public:
// static int staticPubField; //$staticField
// const int constPubField; //$field
// const static int constStaticPubField; //$staticField
// int pubField; //$field
//
// static int staticPubMethod(int arg) { //$methodDeclaration,parameterVariable
// FUNCTION_MACRO(arg); //$macroSubstitution,parameterVariable
// globalFunc(arg); //$function,parameterVariable
// return globalStaticVariable; //$globalVariable
// }
// int pubMethod(); //$methodDeclaration
//
// enum pubEnumeration {pubEnumerator}; //$enum,enumerator
// class pubClass{}; //$class
// class pubStruct{}; //$class
// class pubUnion{}; //$class
// typedef pubClass pubTypedef; //$class,typedef
//
//protected:
// static const int constStaticProtField = 12; //$staticField
// static int staticProtField; //$staticField
// const int constProtField; //$field
// int protField; //$field
//
// static int staticProtMethod(); //$methodDeclaration
// int protMethod(); //$methodDeclaration
//
// enum protEnumeration {protEnumerator}; //$enum,enumerator
// class protClass{}; //$class
// class protStruct{}; //$class
// class protUnion{}; //$class
// typedef protClass protTypedef; //$class,typedef
//
//private:
// static const int constStaticPrivField = 12; //$staticField
// static int staticPrivField; //$staticField
// const int constPrivField; //$field
// int privField; //$field
//
// static int staticPrivMethod(); //$methodDeclaration
// int privMethod(); //$methodDeclaration
//
// enum privEnumeration {privEnumerator}; //$enum,enumerator
// class privClass{}; //$class
// class privStruct{}; //$class
// class privUnion{}; //$class
// typedef privClass privTypedef; //$class,typedef
//
//
//};
//
//template<class T1, class T2> class TemplateClass { //$templateParameter,templateParameter,class
// T1 tArg1; //$templateParameter,field
// T2 tArg2; //$templateParameter,field
// TemplateClass(T1 arg1, T2 arg2) { //$methodDeclaration,templateParameter,parameterVariable,templateParameter,parameterVariable
// tArg1 = arg1; //$field,parameterVariable
// tArg2 = arg2; //$field,parameterVariable
// }
//};
//
//template<class T1> class PartialInstantiatedClass //$templateParameter,class
// : TemplateClass<T1, Base1> {}; //$class,templateParameter,class
//
//
//struct CppStruct { //$class
// int structField; //$field
//};
//
//union CppUnion { //$class
// int unionField; //$field
// CppUnion operator+(CppUnion); //$class,methodDeclaration,class
// CppUnion operator[](int); //$class,methodDeclaration
//};
//
//typedef CppUnion TUnion; //$class,typedef
//
//namespace ns { //$namespace
// int namespaceVar = 0; //$globalVariable
// int namespaceFunc() { //$functionDeclaration
// globalStaticFunc(); //$function
// return namespaceVar; //$globalVariable
// }
//}
//
//INT ClassContainer::protMethod() { //$macroSubstitution,methodDeclaration
// return protField; //$field
//}
//
//INT ClassContainer::pubMethod() { //$macroSubstitution,methodDeclaration
// int localVar = 0; //$localVariableDeclaration
// return pubField + localVar; //$field,localVariable
//}
//
//INT ClassContainer::staticPrivMethod() { //$macroSubstitution,methodDeclaration
// CppStruct* st= new CppStruct(); //$class,localVariableDeclaration,class
// st->structField= 1; //$localVariable,field
// CppUnion un; //$class,localVariableDeclaration
// un.unionField= 2; //$localVariable,field
// staticPubMethod(staticPrivField); //$staticMethod,staticField
// un + un[6]; //$localVariable,overloadedOperator,localVariable,overloadedOperator,overloadedOperator
//label: //$label
// FUNCTION_MACRO(0); //$macroSubstitution
// if (un.unionField < st->structField) //$localVariable,field,localVariable,field
// goto label; //$label
// problemMethod(); //$problem
// // external SDK
// SDKClass sdkClass; //$class,localVariableDeclaration
// sdkClass.SDKMethod(); //$localVariable,externalSDK
// SDKFunction(); //$externalSDK
// return 0;
//}
//
////http://bugs.eclipse.org/209203
//template <int n> //$templateParameter
//int f() //$functionDeclaration
//{
// return n; //$templateParameter
//}
//
////http://bugs.eclipse.org/220392
//#define EMPTY //$macroDefinition
//EMPTY int f(); //$macroSubstitution,functionDeclaration
//
////http://bugs.eclipse.org/340492
//template< template<class> class U > //$templateParameter
//class myClass {}; //$class
//
////http://bugs.eclipse.org/372004
//void g() { //$functionDeclaration
// // declared as global near top
// extern int globalVariable; //$globalVariable
//}
//
////http://bugs.eclipse.org/399149
//class C final { //$class,c_keyword
// void finalMethod() final; //$methodDeclaration,c_keyword
// void overrideMethod() override; //$methodDeclaration,c_keyword
//
// // ordinary field, happens to be named 'final'
// int final; //$field
//};
public void testVariousHighlightings() throws Exception {
makeAssertions();
}
// class C { //$class
// template <typename T> void bar(T); //$templateParameter,methodDeclaration,templateParameter
// };
//
// template <typename U> //$templateParameter
// void foo(U u) { //$functionDeclaration,templateParameter,parameterVariable
// C().bar(u); //$class,method,parameterVariable
// }
public void testDependentMethodCall_379626() throws Exception {
makeAssertions();
}
// struct S {}; //$class
// struct S waldo; //$class,globalVariable
public void testCStructureName_451772() throws Exception {
makeAssertions(false /* parse as C file */);
}
// template <typename T> //$templateParameter
// void foo(T t) { //$functionDeclaration,templateParameter,parameterVariable
// bar(t); //$function,parameterVariable
// }
public void testNPE_458317() throws Exception {
makeAssertions();
}
// struct S { }; //$class
// alignas(S) int x; //$class,globalVariable
public void testHighlightingInsideAlignmentSpecifier_451082() throws Exception {
makeAssertions();
}
// struct Duration {}; //$class
// Duration operator "" _d(unsigned long long); //$class,functionDeclaration
// Duration dur = 1000_d; //$class,globalVariable,overloadedOperator
public void testUserDefinedLiteralSuffix_484617() throws Exception {
makeAssertions();
}
// template<typename T, typename U> //$templateParameter,templateParameter
// struct Pair {}; //$class
//
// template<typename T> //$templateParameter
// using PairIntX = Pair<int, T>; //$typedef,class,templateParameter
//
// struct Waldo {}; //$class
//
// int main() { //$functionDeclaration
// PairIntX<Waldo> pair; //$typedef,class,localVariableDeclaration
// }
public void testAliasTemplates_416748() throws Exception {
makeAssertions();
}
// namespace N { //$namespace
// class C { //$class
// enum E1 {}; //$enum
// };
// C::E1 e1; //$class,enum,globalVariable
// enum E2 {}; //$enum
// }
// N::C::E1 e1; //$namespace,class,enum,globalVariable
// N::E2 e2; //$namespace,enum,globalVariable
public void testQualifiedEnum_485709() throws Exception {
makeAssertions();
}
// class Base {}; //$class
// class Derived : Base { //$class,class
// using Base::Base; //$class,method
// };
public void testInheritingConstructor_484898() throws Exception {
makeAssertions();
}
// void foo(int param) { //$functionDeclaration,parameterVariable
// int local; //$localVariableDeclaration
// [local, param](){}; //$class,localVariable,parameterVariable
// }
public void testLocalVariableInLambdaCapture_486679() throws Exception {
makeAssertions();
}
// template <typename T> //$templateParameter
// struct Base { //$class
// enum E { A }; //$enum,enumerator
// enum class F { B }; //$enum,enumerator
// };
// template <typename T> //$templateParameter
// struct Derived : Base<T> { //$class,class,templateParameter
// static typename Base<T>::E x //$class,templateParameter,enum,staticField
// = Base<T>::A; //$class,templateParameter,enumerator
// static typename Base<T>::F y //$class,templateParameter,enum,staticField
// = Base<T>::F::B; //$class,templateParameter,enum,enumerator
// };
public void testDependentEnum_486688() throws Exception {
makeAssertions();
}
// #define WALDO(name) const char* Name() override { return name; } //$macroDefinition
// class S { //$class
// WALDO("name") //$macroSubstitution
// };
public void testOverrideInMacroExpansion_486683a() throws Exception {
// This tests that the 'override' does not cause the entire invocation
// to be colored with the keyword highlighting.
makeAssertions();
}
// #define MIRROR(arg) arg //$macroDefinition
// MIRROR(class S { void foo() override; }) //$macroSubstitution,class,methodDeclaration,c_keyword
public void testOverrideInMacroExpansion_486683b() throws Exception {
// This tests that the 'override' *does* cause the 'override' keyword
// in the argument to be colored with the keyword highlighting.
makeAssertions();
}
// #define MIRROR(arg) arg //$macroDefinition
// struct Foo { //$class
// bool operator==(const Foo&) const; //$methodDeclaration,class
// };
// int main() { //$functionDeclaration
// Foo a, b; //$class,localVariableDeclaration,localVariableDeclaration
// MIRROR(a == b); //$macroSubstitution,localVariable,overloadedOperator,localVariable
// }
public void testOverloadedOperatorInMacroExpansion_371839() throws Exception {
makeAssertions();
}
// template<unsigned... _Indexes> //$templateParameter
// struct _Index_tuple { //$class
// typedef _Index_tuple<_Indexes..., sizeof...(_Indexes)> __next; //$class,templateParameter,templateParameter,typedef
// };
// template<unsigned _Num> //$templateParameter
// struct _Build_index_tuple { //$class
// typedef typename _Build_index_tuple<_Num - 1>::__type::__next __type; //$class,templateParameter,class,class,typedef
// };
//
// template<>
// struct _Build_index_tuple<0> { //$class
// typedef _Index_tuple<> __type; //$class,typedef
// };
public void testRecursion_491834() throws Exception {
makeAssertions();
}
// template <typename T> //$templateParameter
// bool templ = true; //$globalVariable
// struct A {}; //$class
// bool x = templ<A>; //$globalVariable,globalVariable,class
// struct S { //$class
// template <typename U> //$templateParameter
// static bool templ = true; //$staticField
// void bar() { //$methodDeclaration
// bool y = templ<A>; //$localVariableDeclaration,staticField,class
// }
// };
public void testVariableTemplates_486672() throws Exception {
makeAssertions();
}
// #define MACRO(Name, Type) Type Name(); //$macroDefinition
// typedef int Int; //$typedef
// class S { //$class
// MACRO(foo, Int) //$macroSubstitution,methodDeclaration,typedef
// };
public void testMethodNameInsideMacro_486682() throws Exception {
makeAssertions();
}
// #define IF_0(t, f) f //$macroDefinition
// #define IF(bit, t, f) IF_ ## bit(t, f) //$macroDefinition
// #define WALDO //$macroDefinition
// #define MAIN(...) int main() { __VA_ARGS__ } //$macroDefinition
//
// MAIN //$macroSubstitution
// (
// int x; //$localVariableDeclaration
// IF(0, WALDO, WALDO) //$macroSubstitution,macroSubstitution,macroSubstitution
// )
public void testLexicalColoringInsideMacroExpansion_490415() throws Exception {
makeAssertions();
}
// #define N1(x) x
// #define M1(x) N1(x)
// int main() { //$functionDeclaration
// M1(0); //$macroSubstitution
// }
public void testLexicalColoringInsideMacroExpansion_496696() throws Exception {
makeAssertions();
}
// void foo(int&); //$functionDeclaration
// struct S { //$class
// int x; //$field
// };
// void bar(int x) { //$functionDeclaration,parameterVariable
// foo(x); //$function,variablePassedByNonconstRef
// S s; //$class,localVariableDeclaration
// foo(s.x); //$function,variablePassedByNonconstRef
// }
public void testVariablePassedByNonconstRef_487764a() throws Exception {
makeAssertions();
}
// template <typename... Args> //$templateParameter
// void foo(Args&&... args); //$functionDeclaration,templateParameter,parameterVariable
// void bar() { //$functionDeclaration
// const int x; //$localVariableDeclaration
// int y; //$localVariableDeclaration
// foo(x, y, "waldo"); //$function,localVariable,variablePassedByNonconstRef
// }
public void testVariablePassedByNonconstRef_487764b() throws Exception {
makeAssertions();
}
}