| /* |
| * Copyright (c) 2013, 2015 QNX Software Systems and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| */ |
| package org.eclipse.cdt.qt.tests; |
| |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.cdt.core.index.IIndex; |
| import org.eclipse.cdt.internal.core.model.ext.SourceRange; |
| import org.eclipse.cdt.internal.qt.core.index.IQMethod; |
| import org.eclipse.cdt.internal.qt.core.index.IQObject; |
| import org.eclipse.cdt.internal.qt.core.index.QtIndex; |
| import org.eclipse.cdt.internal.ui.editor.CEditor; |
| import org.eclipse.cdt.internal.ui.search.CSearchResult; |
| import org.eclipse.cdt.internal.ui.search.CSearchTextSelectionQuery; |
| import org.eclipse.cdt.ui.CUIPlugin; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.search.ui.ISearchResult; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| |
| @SuppressWarnings("restriction") |
| public class QtRegressionTests extends BaseQtTestCase { |
| |
| private static Map<String, Set<String>> buildExpectedMap(String mocOutput) { |
| Map<String, Set<String>> expected = new HashMap<>(); |
| for (String moc_signature : mocOutput.split("\0")) { |
| String name = moc_signature.split("\\(")[0]; |
| Set<String> set = expected.get(name); |
| if (set == null) { |
| set = new HashSet<>(); |
| expected.put(name, set); |
| } |
| set.add(moc_signature); |
| } |
| |
| return expected; |
| } |
| |
| // #include "junit-QObject.hh" |
| // struct T {}; |
| // class Q : public QObject |
| // { |
| // Q_OBJECT |
| // public: |
| // void func(); |
| // signals: |
| // void sig_int(int i = 5); |
| // void sig_const_int(const int i = 5); |
| // void sig_T(T * t = 0); |
| // void sig_const_T(const T * const t = 0); |
| // }; |
| public void testDefaultParameters() throws Exception { |
| loadComment("defaultParams.hh"); |
| |
| QtIndex qtIndex = QtIndex.getIndex(fProject); |
| assertNotNull(qtIndex); |
| |
| IQObject qobj = qtIndex.findQObject(new String[] { "Q" }); |
| if (!isIndexOk("Q", qobj)) |
| return; |
| assertNotNull(qobj); |
| |
| // Based on the moc output, but modified to manage our handling for default parameters. The |
| // moc generates two signatures, sig(N::TS::M) and sig(), we just mark optional parameters |
| // with a trailing =. However, QMethod#getSignature is currently modified to strip the |
| // default value indication. So, we're only dealing with the full signature here. |
| String moc = "sig_int(int)\0sig_int()\0sig_const_int(int)\0" + "sig_const_int()\0sig_T(T*)\0sig_T()\0" |
| + "sig_const_T(T*const)\0sig_const_T()\0"; |
| Map<String, Set<String>> expected = buildExpectedMap(moc); |
| |
| IQObject.IMembers<IQMethod> sigs = qobj.getSignals(); |
| assertNotNull(sigs); |
| Collection<IQMethod> locals = sigs.locals(); |
| assertNotNull(locals); |
| for (IQMethod method : locals) { |
| Set<String> set = expected.get(method.getName()); |
| assertNotNull("unexpected method " + method.getName() + " (" + method.getSignatures() + ')', set); |
| for (String signature : method.getSignatures()) { |
| assertTrue(set.remove(signature)); |
| } |
| assertTrue("did not find all signatures for " + method.getName(), set.isEmpty()); |
| expected.remove(method.getName()); |
| } |
| assertEquals(0, expected.size()); |
| } |
| |
| // #include "junit-QObject.hh" |
| // typedef int Tl1; |
| // typedef Tl1 Tl2; |
| // enum _E {}; |
| // struct S { typedef int M; typedef char Mb; }; |
| // template<typename T> struct S_TEMPLATE { }; |
| // namespace N |
| // { |
| // typedef int Ib; |
| // typedef _E Eb; |
| // enum E {}; |
| // namespace N2 { enum E2 {}; typedef E2 TE2; } |
| // typedef E TEa; |
| // typedef TEa TEb; |
| // typedef N2::E2 N2_E2; |
| // typedef N2::TE2 N2_TE2; |
| // typedef S TS; |
| // } |
| // typedef N::E N_E; |
| // namespace N2 { typedef N::Ib Ic; } |
| // class Q : public QObject |
| // { |
| // Q_OBJECT |
| // public: |
| // void func(); |
| // signals: |
| // void sig_int(int); |
| // void sig_Tl1(Tl1); |
| // void sig_Tl2(Tl2); |
| // void sig_int_ptr(int *); |
| // void sig_Tl1_ptr(Tl1 *); |
| // void sig_Tl2_ptr(Tl2 *); |
| // |
| // void sig_qual1(N::E); |
| // void sig_qual2(N::N2::E2); |
| // void sig_typedef1(N_E); |
| // void sig_typedef2(N::TEa); |
| // void sig_typedef3(N::TEb); |
| // void sig_typedef4(N::N2_E2); |
| // void sig_typedef5(N::N2_TE2); |
| // void sig_typedef6(N::N2::TE2); |
| // |
| // void sig_nested1(S::Mb); |
| // void sig_nested2(N::TS::M); |
| // void sig_nested3(N::Ib); |
| // void sig_nested4(N::Eb); |
| // void sig_nested5(N2::Ic); |
| // |
| // void sig_S ( S ); // sig_S ( S ) |
| // void const_S (const S ); // const_S ( S ) |
| // void S_const ( S const ); // S_const ( S ) |
| // void S_ref ( S & ); // S_ref ( S & ) |
| // void const_S_ref (const S & ); // const_S_ref ( S ) |
| // void S_const_ref( S const &); // S_const_ref( S ) |
| // void S_ptr ( S * ); |
| // void S_ptr_const( S * const); |
| // void const_S_ptr (const S * ); |
| // void const_S_ptr_const(const S * const); |
| // void const_S_ptr_const_def(const S * const s = 0); |
| // void S_ptr_ref ( S * &); |
| // void S_ptr_const_ref( S * const &); |
| // void const_S_ptr_const_ref(const S * const &); |
| // void S_ptr_ptr ( S * *); |
| // void S_ptr_const_ptr( S * const *); |
| // void const_S_ptr_ptr (const S * *); |
| // void const_S_ptr_const_ptr(const S * const *); |
| // void S_ptr_ptr_const ( S * * const); |
| // void S_ptr_const_ptr_const( S * const * const); |
| // void const_S_ptr_ptr_const (const S * * const); |
| // void const_S_ptr_const_ptr_const(const S * const * const); |
| // |
| // void S_template_1(const S_TEMPLATE<const S *> & p); |
| // void S_template_2(const S_TEMPLATE<S const *> & p); |
| // void S_template_3(S_TEMPLATE<const S *> const & p); |
| // void S_template_4(S_TEMPLATE<S const *> const & p); |
| // void S_template_X(const S_TEMPLATE<const S const *> const & p); |
| // }; |
| public void testBug338930() throws Exception { |
| loadComment("bug338930.hh"); |
| |
| QtIndex qtIndex = QtIndex.getIndex(fProject); |
| assertNotNull(qtIndex); |
| |
| IQObject qobj = qtIndex.findQObject(new String[] { "Q" }); |
| if (!isIndexOk("Q", qobj)) |
| return; |
| assertNotNull(qobj); |
| |
| // Copy and pasted moc output (signals only) to make sure we're getting an exact match. |
| String moc_output = "sig_int(int)\0" + "sig_Tl1(Tl1)\0sig_Tl2(Tl2)\0sig_int_ptr(int*)\0" |
| + "sig_Tl1_ptr(Tl1*)\0sig_Tl2_ptr(Tl2*)\0" + "sig_qual1(N::E)\0sig_qual2(N::N2::E2)\0" |
| + "sig_typedef1(N_E)\0sig_typedef2(N::TEa)\0" + "sig_typedef3(N::TEb)\0sig_typedef4(N::N2_E2)\0" |
| + "sig_typedef5(N::N2_TE2)\0" + "sig_typedef6(N::N2::TE2)\0sig_nested1(S::Mb)\0" |
| + "sig_nested2(N::TS::M)\0sig_nested3(N::Ib)\0" + "sig_nested4(N::Eb)\0sig_nested5(N2::Ic)\0" |
| |
| + "sig_S(S)\0const_S(S)\0S_const(S)\0S_ref(S&)\0" + "const_S_ref(S)\0S_const_ref(S)\0S_ptr(S*)\0" |
| + "S_ptr_const(S*const)\0const_S_ptr(const S*)\0" |
| + "const_S_ptr_const(S*const)\0const_S_ptr_const_def(S*const)\0" |
| + "const_S_ptr_const_def()\0S_ptr_ref(S*&)\0" + "S_ptr_const_ref(S*)\0const_S_ptr_const_ref(S*const)\0" |
| + "S_ptr_ptr(S**)\0S_ptr_const_ptr(S*const*)\0" + "const_S_ptr_ptr(const S**)\0" |
| + "const_S_ptr_const_ptr(const S*const*)\0" + "S_ptr_ptr_const(S**const)\0" |
| + "S_ptr_const_ptr_const(S*const*const)\0" + "const_S_ptr_ptr_const(S**const)\0" |
| + "const_S_ptr_const_ptr_const(S*const*const)\0" + "S_template_1(S_TEMPLATE<const S*>)\0" |
| + "S_template_2(S_TEMPLATE<const S*>)\0" + "S_template_3(S_TEMPLATE<const S*>)\0" |
| + "S_template_4(S_TEMPLATE<const S*>)\0" + "S_template_X(S_TEMPLATE<const S*>)"; |
| |
| Map<String, Set<String>> expected = buildExpectedMap(moc_output); |
| |
| IQObject.IMembers<IQMethod> sigs = qobj.getSignals(); |
| assertNotNull(sigs); |
| Collection<IQMethod> locals = sigs.locals(); |
| assertNotNull(locals); |
| for (IQMethod method : locals) { |
| Set<String> set = expected.get(method.getName()); |
| assertNotNull("unexpected signal " + method.getName() + " (" + method.getSignatures() + ')', set); |
| for (String signature : method.getSignatures()) |
| assertTrue(set.remove(signature)); |
| assertTrue("did not find all signatures for " + method.getName(), set.isEmpty()); |
| expected.remove(method.getName()); |
| } |
| assertEquals(0, expected.size()); |
| } |
| |
| // #include "junit-QObject.hh" |
| // class Q : public QObject |
| // { |
| // Q_OBJECT |
| // Q_SLOT void const_ref(const QString &); |
| // Q_SLOT void const_val(const QString ); |
| // Q_SLOT void reference( QString &); |
| // Q_SLOT void value( QString ); |
| // enum E { }; |
| // Q_SIGNAL void signalEnum_const_ref(const E &); |
| // Q_SIGNAL void signalEnum_reference(E &); |
| // Q_SIGNAL void signalEnum_qualified(Q::E); |
| // void func() |
| // { |
| // connect(this, SIGNAL(destroyed(QObject*), this, SLOT(const_ref(QString)))); |
| // connect(this, SIGNAL(destroyed(QObject*), this, SLOT(const_val(QString)))); |
| // connect(this, SIGNAL(signalEnum_const_ref(E), this, SLOT(reference(QString&)))); |
| // connect(this, SIGNAL(signalEnum_reference(E&), this, SLOT(value(QString)))); |
| // } |
| // }; |
| public void testBug344931() throws Exception { |
| loadComment("bug344931.hh"); |
| |
| QtIndex qtIndex = QtIndex.getIndex(fProject); |
| assertNotNull(qtIndex); |
| |
| IQObject qobj = qtIndex.findQObject(new String[] { "Q" }); |
| if (!isIndexOk("Q", qobj)) |
| return; |
| assertNotNull(qobj); |
| |
| IQObject.IMembers<IQMethod> slotMembers = qobj.getSlots(); |
| assertNotNull(slotMembers); |
| |
| Collection<IQMethod> slots = slotMembers.locals(); |
| assertNotNull(slots); |
| assertEquals(4, slots.size()); |
| |
| for (IQMethod slot : slots) { |
| if ("const_ref".equals(slot.getName())) |
| assertTrue(slot.getSignatures().contains("const_ref(QString)")); |
| else if ("const_val".equals(slot.getName())) |
| assertTrue(slot.getSignatures().contains("const_val(QString)")); |
| else if ("reference".equals(slot.getName())) |
| assertTrue(slot.getSignatures().contains("reference(QString&)")); |
| else if ("value".equals(slot.getName())) |
| assertTrue(slot.getSignatures().contains("value(QString)")); |
| else |
| fail("unexpected slot " + slot.getName()); |
| } |
| |
| IQObject.IMembers<IQMethod> signalMembers = qobj.getSignals(); |
| assertNotNull(signalMembers); |
| |
| Collection<IQMethod> signals = signalMembers.locals(); |
| assertNotNull(signals); |
| assertEquals(3, signals.size()); |
| |
| for (IQMethod signal : signals) { |
| if ("signalEnum_const_ref".equals(signal.getName())) |
| assertTrue(signal.getSignatures().contains("signalEnum_const_ref(E)")); |
| else if ("signalEnum_reference".equals(signal.getName())) |
| assertTrue(signal.getSignatures().contains("signalEnum_reference(E&)")); |
| else if ("signalEnum_qualified".equals(signal.getName())) |
| assertTrue(signal.getSignatures().contains("signalEnum_qualified(Q::E)")); |
| else |
| fail("unexpected signal " + signal.getName()); |
| } |
| } |
| |
| // #include "junit-QObject.hh" |
| // class Q : public QObject |
| // { |
| // Q_OBJECT |
| // public: |
| // void func(); |
| // private slots: |
| // void slot1(); |
| // private: |
| // Q_SLOT void slot2(); |
| // Q_SLOT void slot3(); |
| // }; |
| // void Q::slot1() { } |
| // void Q::slot2() { } |
| // void Q::func() |
| // { |
| // QObject::connect( this, destroyed( QObject * ), this, slot1() ); |
| // QObject::connect( this, destroyed( QObject * ), this, slot2() ); |
| // QObject::connect( this, destroyed( QObject * ), this, slot3() ); |
| // } |
| public void testSlotDefn() throws Exception { |
| loadComment("slotDefn.hh"); |
| |
| QtIndex qtIndex = QtIndex.getIndex(fProject); |
| assertNotNull(qtIndex); |
| |
| IQObject qobj = qtIndex.findQObject(new String[] { "Q" }); |
| if (!isIndexOk("Q", qobj)) |
| return; |
| assertNotNull(qobj); |
| |
| IQObject.IMembers<IQMethod> slots = qobj.getSlots(); |
| assertNotNull(slots); |
| Collection<IQMethod> localSlots = slots.locals(); |
| assertNotNull(localSlots); |
| |
| // make sure that the three slot functions are found, but none of the inherited or |
| // non-slot functions |
| Set<String> expected = new HashSet<>(Arrays.asList("slot1", "slot2", "slot3")); |
| for (IQMethod method : localSlots) |
| assertTrue("unexpected slot " + method.getName(), expected.remove(method.getName())); |
| assertEquals("missing slots " + expected.toString(), 0, expected.size()); |
| } |
| |
| // #include "junit-QObject.hh" |
| // class Q : public QObject |
| // { |
| // Q_OBJECT |
| // Q_SIGNAL void signal1(); |
| // Q_SLOT void slot1(); |
| // void function() |
| // { |
| // signal1(); |
| // QObject::connect( |
| // this, SIGNAL( signal1() ), |
| // this, SLOT( slot1() ) ); |
| // } |
| // }; |
| public void testBug424499_FindQMethodReferences() throws Exception { |
| String filename = "signalRefs.hh"; |
| loadComment(filename); |
| waitForIndexer(fCProject); |
| |
| // The search query uses the ASTProvider which relies on the target translation unit being |
| // loaded in a CEditor. The following opens a CEditor for the test file so that it will |
| // be the one used by the ASTProvider. |
| IFile file = fProject.getFile(filename); |
| assertNotNull(file); |
| assertTrue(file.exists()); |
| IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| assertNotNull(page); |
| IEditorPart editor = IDE.openEditor(page, file, CUIPlugin.EDITOR_ID); |
| assertNotNull(editor); |
| CEditor ceditor = editor.getAdapter(CEditor.class); |
| assertNotNull(ceditor); |
| |
| // NOTE: This offset relies on the above comment being exactly as expected. If it is edited, |
| // then this offset should be adjusted to match. It needs to put the cursor in the |
| // declaration for signal1. |
| ceditor.setSelection(new SourceRange(86, 0), true); |
| ISelection sel = ceditor.getSelectionProvider().getSelection(); |
| assertNotNull(sel); |
| assertTrue(sel instanceof ITextSelection); |
| |
| // Now a query is created and executed. |
| CSearchTextSelectionQuery query = new CSearchTextSelectionQuery(null, ceditor.getInputCElement(), |
| (ITextSelection) sel, IIndex.FIND_REFERENCES); |
| |
| // The query sometimes fails (with Status.CANCEL_STATUS) if the TU is not open. I |
| // haven't found a way to be notified when the TU gets "opened" -- the test case just |
| // looks that case and then try again. |
| IStatus status = null; |
| long end_ms = System.currentTimeMillis() + 1000; |
| do { |
| status = query.run(npm()); |
| if (status == Status.CANCEL_STATUS) { |
| Thread.sleep(100); |
| } |
| } while (!status.isOK() && System.currentTimeMillis() < end_ms); |
| assertTrue("query failed: " + status.getMessage(), status.isOK()); |
| |
| // The query should have found two references, one for the function call and another |
| // for the SIGNAL expansion. |
| ISearchResult result = query.getSearchResult(); |
| assertNotNull(result); |
| assertTrue(result instanceof CSearchResult); |
| |
| CSearchResult searchResult = (CSearchResult) result; |
| assertEquals(2, searchResult.getMatchCount()); |
| } |
| } |