summaryrefslogtreecommitdiffstats
path: root/tests/auto/tools/moc/tst_moc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/tools/moc/tst_moc.cpp')
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp227
1 files changed, 183 insertions, 44 deletions
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index edb6488eaa..08dc9581e1 100644
--- a/tests/auto/tools/moc/tst_moc.cpp
+++ b/tests/auto/tools/moc/tst_moc.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: http://www.qt-project.org/legal
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
@@ -11,9 +11,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -24,8 +24,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -72,6 +72,12 @@
#include "related-metaobjects-in-gadget.h"
#include "related-metaobjects-name-conflict.h"
+#include "non-gadget-parent-class.h"
+#include "grand-parent-gadget-class.h"
+
+Q_DECLARE_METATYPE(const QMetaObject*);
+
+
QT_USE_NAMESPACE
template <bool b> struct QTBUG_31218 {};
@@ -415,7 +421,7 @@ public:
enum TestEnum { One, Two, Three };
- Q_ENUMS(TestEnum)
+ Q_ENUM(TestEnum)
};
class PropertyUseClass : public QObject
@@ -434,7 +440,7 @@ class EnumSourceClass : public QObject
public:
enum TestEnum { Value = 37 };
- Q_ENUMS(TestEnum)
+ Q_ENUM(TestEnum)
};
class EnumUserClass : public QObject
@@ -572,6 +578,9 @@ private slots:
void relatedMetaObjectsInGadget();
void relatedMetaObjectsNameConflict_data();
void relatedMetaObjectsNameConflict();
+ void strignLiteralsInMacroExtension();
+ void veryLongStringData();
+ void gadgetHierarchy();
signals:
void sigWithUnsignedArg(unsigned foo);
@@ -594,6 +603,7 @@ private:
void setMember3( const QString &sVal ) { sMember = sVal; }
private:
+ QString m_moc;
QString m_sourceDirectory;
QString qtIncludePath;
class PrivateClass;
@@ -604,12 +614,16 @@ private:
void tst_Moc::initTestCase()
{
+ QString binpath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
+ QString qmake = QString("%1/qmake").arg(binpath);
+ m_moc = QString("%1/moc").arg(binpath);
+
const QString testHeader = QFINDTESTDATA("backslash-newlines.h");
QVERIFY(!testHeader.isEmpty());
m_sourceDirectory = QFileInfo(testHeader).absolutePath();
#if defined(Q_OS_UNIX) && !defined(QT_NO_PROCESS)
QProcess proc;
- proc.start("qmake", QStringList() << "-query" << "QT_INSTALL_HEADERS");
+ proc.start(qmake, QStringList() << "-query" << "QT_INSTALL_HEADERS");
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray output = proc.readAllStandardOutput();
@@ -653,7 +667,7 @@ void tst_Moc::oldStyleCasts()
#endif
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
- proc.start("moc", QStringList(m_sourceDirectory + QStringLiteral("/oldstyle-casts.h")));
+ proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/oldstyle-casts.h")));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -684,7 +698,7 @@ void tst_Moc::warnOnExtraSignalSlotQualifiaction()
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
const QString header = m_sourceDirectory + QStringLiteral("/extraqualification.h");
- proc.start("moc", QStringList(header));
+ proc.start(m_moc, QStringList(header));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -723,7 +737,7 @@ void tst_Moc::inputFileNameWithDotsButNoExtension()
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
proc.setWorkingDirectory(m_sourceDirectory + QStringLiteral("/task71021"));
- proc.start("moc", QStringList("../Header"));
+ proc.start(m_moc, QStringList("../Header"));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -971,7 +985,7 @@ void tst_Moc::warnOnMultipleInheritance()
QStringList args;
const QString header = m_sourceDirectory + QStringLiteral("/warn-on-multiple-qobject-subclasses.h");
args << "-I" << qtIncludePath + "/QtGui" << header;
- proc.start("moc", args);
+ proc.start(m_moc, args);
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -996,7 +1010,7 @@ void tst_Moc::ignoreOptionClashes()
const QString includeDir = m_sourceDirectory + "/Test.framework/Headers";
// given --ignore-option-clashes, -pthread should be ignored, but the -I path should not be.
args << "--ignore-option-clashes" << "-pthread" << "-I" << includeDir << "-fno-builtin" << header;
- proc.start("moc", args);
+ proc.start(m_moc, args);
bool finished = proc.waitForFinished();
if (!finished)
qWarning("waitForFinished failed. QProcess error: %d", (int)proc.error());
@@ -1034,7 +1048,7 @@ void tst_Moc::forgottenQInterface()
QStringList args;
const QString header = m_sourceDirectory + QStringLiteral("/forgotten-qinterface.h");
args << "-I" << qtIncludePath + "/QtCore" << header;
- proc.start("moc", args);
+ proc.start(m_moc, args);
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -1118,7 +1132,7 @@ void tst_Moc::frameworkSearchPath()
;
QProcess proc;
- proc.start("moc", args);
+ proc.start(m_moc, args);
bool finished = proc.waitForFinished();
if (!finished)
qWarning("waitForFinished failed. QProcess error: %d", (int)proc.error());
@@ -1136,12 +1150,18 @@ void tst_Moc::frameworkSearchPath()
void tst_Moc::cstyleEnums()
{
const QMetaObject &obj = CStyleEnums::staticMetaObject;
- QCOMPARE(obj.enumeratorCount(), 1);
+ QCOMPARE(obj.enumeratorCount(), 2);
QMetaEnum metaEnum = obj.enumerator(0);
QCOMPARE(metaEnum.name(), "Baz");
QCOMPARE(metaEnum.keyCount(), 2);
QCOMPARE(metaEnum.key(0), "Foo");
QCOMPARE(metaEnum.key(1), "Bar");
+
+ QMetaEnum metaEnum2 = obj.enumerator(1);
+ QCOMPARE(metaEnum2.name(), "Baz2");
+ QCOMPARE(metaEnum2.keyCount(), 2);
+ QCOMPARE(metaEnum2.key(0), "Foo2");
+ QCOMPARE(metaEnum2.key(1), "Bar2");
}
void tst_Moc::templateGtGt()
@@ -1151,7 +1171,7 @@ void tst_Moc::templateGtGt()
#endif
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
- proc.start("moc", QStringList(m_sourceDirectory + QStringLiteral("/template-gtgt.h")));
+ proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/template-gtgt.h")));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -1172,7 +1192,7 @@ void tst_Moc::defineMacroViaCmdline()
args << "-DFOO";
args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h");
- proc.start("moc", args);
+ proc.start(m_moc, args);
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QCOMPARE(proc.readAllStandardError(), QByteArray());
@@ -1299,13 +1319,13 @@ public:
PrivatePropertyTest(QObject *parent = 0) : QObject(parent), mFoo(0), d (new MyDPointer) {}
int foo() { return mFoo ; }
void setFoo(int value) { mFoo = value; }
- MyDPointer *d_func() {return d;}
+ MyDPointer *d_func() {return d.data();}
signals:
void blub4Changed();
void blub5Changed(const QString &newBlub);
private:
int mFoo;
- MyDPointer *d;
+ QScopedPointer<MyDPointer> d;
};
@@ -1350,7 +1370,7 @@ void tst_Moc::warnOnPropertyWithoutREAD()
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
const QString header = m_sourceDirectory + QStringLiteral("/warn-on-property-without-read.h");
- proc.start("moc", QStringList(header));
+ proc.start(m_moc, QStringList(header));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -1409,19 +1429,19 @@ void tst_Moc::constructors()
QCOMPARE(mo->indexOfConstructor("CtorTestClass2(QObject*)"), -1);
QCOMPARE(mo->indexOfConstructor("CtorTestClass(float,float)"), -1);
- QObject *o1 = mo->newInstance();
+ QScopedPointer<QObject> o1(mo->newInstance());
QVERIFY(o1 != 0);
QCOMPARE(o1->parent(), (QObject*)0);
- QVERIFY(qobject_cast<CtorTestClass*>(o1) != 0);
+ QVERIFY(qobject_cast<CtorTestClass*>(o1.data()) != 0);
- QObject *o2 = mo->newInstance(Q_ARG(QObject*, o1));
+ QObject *o2 = mo->newInstance(Q_ARG(QObject*, o1.data()));
QVERIFY(o2 != 0);
- QCOMPARE(o2->parent(), o1);
+ QCOMPARE(o2->parent(), o1.data());
QString str = QString::fromLatin1("hello");
- QObject *o3 = mo->newInstance(Q_ARG(QString, str));
+ QScopedPointer<QObject> o3(mo->newInstance(Q_ARG(QString, str)));
QVERIFY(o3 != 0);
- QCOMPARE(qobject_cast<CtorTestClass*>(o3)->m_str, str);
+ QCOMPARE(qobject_cast<CtorTestClass*>(o3.data())->m_str, str);
{
//explicit constructor
@@ -1461,7 +1481,7 @@ void tst_Moc::warnOnVirtualSignal()
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
const QString header = m_sourceDirectory + QStringLiteral("/pure-virtual-signals.h");
- proc.start("moc", QStringList(header));
+ proc.start(m_moc, QStringList(header));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -1593,7 +1613,7 @@ void tst_Moc::notifyError()
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
const QString header = m_sourceDirectory + QStringLiteral("/error-on-wrong-notify.h");
- proc.start("moc", QStringList(header));
+ proc.start(m_moc, QStringList(header));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 1);
QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
@@ -1638,7 +1658,6 @@ class VersionTest : public QObject
Q_OBJECT
Q_PROPERTY(int prop1 READ foo)
Q_PROPERTY(int prop2 READ foo REVISION 2)
- Q_ENUMS(TestEnum);
public:
int foo() const { return 0; }
@@ -1647,6 +1666,8 @@ public:
Q_INVOKABLE Q_REVISION(4) void method2() {}
enum TestEnum { One, Two };
+ Q_ENUM(TestEnum);
+
public slots:
void slot1() {}
@@ -1671,7 +1692,6 @@ class VersionTestNotify : public QObject
Q_OBJECT
Q_PROPERTY(int prop1 READ foo NOTIFY fooChanged)
Q_PROPERTY(int prop2 READ foo REVISION 2)
- Q_ENUMS(TestEnum);
public:
int foo() const { return 0; }
@@ -1680,6 +1700,7 @@ public:
Q_INVOKABLE Q_REVISION(4) void method2() {}
enum TestEnum { One, Two };
+ Q_ENUM(TestEnum);
public slots:
void slot1() {}
@@ -1871,7 +1892,7 @@ void tst_Moc::warnings()
env.insert("QT_MESSAGE_PATTERN", "no qDebug or qWarning please");
proc.setProcessEnvironment(env);
- proc.start("moc", args);
+ proc.start(m_moc, args);
QVERIFY(proc.waitForStarted());
QCOMPARE(proc.write(input), qint64(input.size()));
@@ -1909,19 +1930,26 @@ void tst_Moc::privateClass()
void tst_Moc::cxx11Enums_data()
{
+ QTest::addColumn<const QMetaObject *>("meta");
QTest::addColumn<QByteArray>("enumName");
QTest::addColumn<char>("prefix");
- QTest::newRow("EnumClass") << QByteArray("EnumClass") << 'A';
- QTest::newRow("TypedEnum") << QByteArray("TypedEnum") << 'B';
- QTest::newRow("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C';
- QTest::newRow("NormalEnum") << QByteArray("NormalEnum") << 'D';
-}
+ const QMetaObject *meta1 = &CXX11Enums::staticMetaObject;
+ const QMetaObject *meta2 = &CXX11Enums2::staticMetaObject;
+ QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << 'A';
+ QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << 'A';
+ QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << 'B';
+ QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << 'B';
+ QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << 'C';
+ QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << 'C';
+ QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << 'D';
+ QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << 'D';
+}
void tst_Moc::cxx11Enums()
{
- const QMetaObject *meta = &CXX11Enums::staticMetaObject;
+ QFETCH(const QMetaObject *,meta);
QCOMPARE(meta->enumeratorOffset(), 0);
QFETCH(QByteArray, enumName);
@@ -2529,8 +2557,6 @@ void tst_Moc::finalClasses()
QCOMPARE(className, expected);
}
-Q_DECLARE_METATYPE(const QMetaObject*);
-
void tst_Moc::explicitOverrideControl_data()
{
QTest::addColumn<const QMetaObject*>("mo");
@@ -3094,7 +3120,7 @@ void tst_Moc::preprocessorOnly()
#endif
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
- proc.start("moc", QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/pp-dollar-signs.h"));
+ proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/pp-dollar-signs.h"));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
QByteArray mocOut = proc.readAllStandardOutput();
@@ -3115,7 +3141,7 @@ void tst_Moc::unterminatedFunctionMacro()
#endif
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(QT_NO_PROCESS)
QProcess proc;
- proc.start("moc", QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/unterminated-function-macro.h"));
+ proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/unterminated-function-macro.h"));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 1);
QCOMPARE(proc.readAllStandardOutput(), QByteArray());
@@ -3269,6 +3295,119 @@ void tst_Moc::relatedMetaObjectsNameConflict()
QCOMPARE(dependency.size(), relatedMetaObjects.size());
}
+class StringLiteralsInMacroExtension: public QObject
+{
+ Q_OBJECT
+#define Macro(F) F " " F
+ Q_CLASSINFO(Macro("String"), Macro("Literal"))
+#undef Macro
+
+#define Macro(F) F
+ Q_CLASSINFO("String" Macro("!"), "Literal" Macro("!"))
+ Q_CLASSINFO(Macro("!") "String", Macro("!") "Literal")
+#undef Macro
+
+#define Macro "foo"
+ Q_CLASSINFO("String" Macro, "Literal" Macro)
+ Q_CLASSINFO(Macro "String", Macro "Literal")
+#undef Macro
+};
+
+void tst_Moc::strignLiteralsInMacroExtension()
+{
+ const QMetaObject *mobj = &StringLiteralsInMacroExtension::staticMetaObject;
+ QCOMPARE(mobj->classInfoCount(), 5);
+
+ QCOMPARE(mobj->classInfo(0).name(), "String String");
+ QCOMPARE(mobj->classInfo(0).value(), "Literal Literal");
+
+ QCOMPARE(mobj->classInfo(1).name(), "String!");
+ QCOMPARE(mobj->classInfo(1).value(), "Literal!");
+
+ QCOMPARE(mobj->classInfo(2).name(), "!String");
+ QCOMPARE(mobj->classInfo(2).value(), "!Literal");
+
+ QCOMPARE(mobj->classInfo(3).name(), "Stringfoo");
+ QCOMPARE(mobj->classInfo(3).value(), "Literalfoo");
+
+ QCOMPARE(mobj->classInfo(4).name(), "fooString");
+ QCOMPARE(mobj->classInfo(4).value(), "fooLiteral");
+}
+
+class VeryLongStringData : public QObject
+{
+ Q_OBJECT
+
+ #define repeat2(V) V V
+ #define repeat4(V) repeat2(V) repeat2(V)
+ #define repeat8(V) repeat4(V) repeat4(V)
+ #define repeat16(V) repeat8(V) repeat8(V)
+ #define repeat32(V) repeat16(V) repeat16(V)
+ #define repeat64(V) repeat32(V) repeat32(V)
+ #define repeat128(V) repeat64(V) repeat64(V)
+ #define repeat256(V) repeat128(V) repeat128(V)
+ #define repeat512(V) repeat256(V) repeat256(V)
+ #define repeat1024(V) repeat512(V) repeat512(V)
+ #define repeat2048(V) repeat1024(V) repeat1024(V)
+ #define repeat4096(V) repeat2048(V) repeat2048(V)
+ #define repeat8192(V) repeat4096(V) repeat4096(V)
+ #define repeat16384(V) repeat8192(V) repeat8192(V)
+ #define repeat32768(V) repeat16384(V) repeat16384(V)
+ #define repeat65534(V) repeat32768(V) repeat16384(V) repeat8192(V) repeat4096(V) repeat2048(V) repeat1024(V) repeat512(V) repeat256(V) repeat128(V) repeat64(V) repeat32(V) repeat16(V) repeat8(V) repeat4(V) repeat2(V)
+
+ Q_CLASSINFO(repeat65534("n"), repeat65534("i"))
+ Q_CLASSINFO(repeat65534("e"), repeat65534("r"))
+ Q_CLASSINFO(repeat32768("o"), repeat32768("b"))
+ Q_CLASSINFO(":", ")")
+
+ #undef repeat2
+ #undef repeat4
+ #undef repeat8
+ #undef repeat16
+ #undef repeat32
+ #undef repeat64
+ #undef repeat128
+ #undef repeat256
+ #undef repeat512
+ #undef repeat1024
+ #undef repeat2048
+ #undef repeat4096
+ #undef repeat8192
+ #undef repeat16384
+ #undef repeat32768
+ #undef repeat65534
+};
+
+void tst_Moc::veryLongStringData()
+{
+ const QMetaObject *mobj = &VeryLongStringData::staticMetaObject;
+ QCOMPARE(mobj->classInfoCount(), 4);
+
+ QCOMPARE(mobj->classInfo(0).name()[0], 'n');
+ QCOMPARE(mobj->classInfo(0).value()[0], 'i');
+ QCOMPARE(mobj->classInfo(1).name()[0], 'e');
+ QCOMPARE(mobj->classInfo(1).value()[0], 'r');
+ QCOMPARE(mobj->classInfo(2).name()[0], 'o');
+ QCOMPARE(mobj->classInfo(2).value()[0], 'b');
+ QCOMPARE(mobj->classInfo(3).name()[0], ':');
+ QCOMPARE(mobj->classInfo(3).value()[0], ')');
+
+ QCOMPARE(strlen(mobj->classInfo(0).name()), static_cast<size_t>(65534));
+ QCOMPARE(strlen(mobj->classInfo(0).value()), static_cast<size_t>(65534));
+ QCOMPARE(strlen(mobj->classInfo(1).name()), static_cast<size_t>(65534));
+ QCOMPARE(strlen(mobj->classInfo(1).value()), static_cast<size_t>(65534));
+ QCOMPARE(strlen(mobj->classInfo(2).name()), static_cast<size_t>(32768));
+ QCOMPARE(strlen(mobj->classInfo(2).value()), static_cast<size_t>(32768));
+ QCOMPARE(strlen(mobj->classInfo(3).name()), static_cast<size_t>(1));
+ QCOMPARE(strlen(mobj->classInfo(3).value()), static_cast<size_t>(1));
+}
+
+void tst_Moc::gadgetHierarchy()
+{
+ QCOMPARE(NonGadgetParent::Derived::staticMetaObject.superClass(), static_cast<const QMetaObject*>(Q_NULLPTR));
+ QCOMPARE(GrandParentGadget::DerivedGadget::staticMetaObject.superClass(), &GrandParentGadget::BaseGadget::staticMetaObject);
+}
+
QTEST_MAIN(tst_Moc)
// the generated code must compile with QT_NO_KEYWORDS