diff options
author | Jake Petroules <jake.petroules@qt.io> | 2017-05-25 19:10:27 -0700 |
---|---|---|
committer | Jake Petroules <jake.petroules@qt.io> | 2017-05-30 15:29:51 +0000 |
commit | 8a7beb631787bbc61624f0fead0563b35649be3a (patch) | |
tree | 58b45d4c54238b67399b09d4831c8ced945df2a9 /tests/auto/blackbox/tst_blackboxapple.cpp | |
parent | b13a5e2a07764dbf92736a2c89e65af0d26c4556 (diff) |
Migrate tests of Apple platforms/tools into new tst_blackboxapple target
This new test suite collects all the tests which test qbs' support for
Apple platforms, Xcode, and related tools.
Change-Id: I835e2d5bee46f1b4bb281c02b5b4ef814f196d7c
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'tests/auto/blackbox/tst_blackboxapple.cpp')
-rw-r--r-- | tests/auto/blackbox/tst_blackboxapple.cpp | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/tests/auto/blackbox/tst_blackboxapple.cpp b/tests/auto/blackbox/tst_blackboxapple.cpp new file mode 100644 index 000000000..1ba0a5266 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxapple.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_blackboxapple.h" + +#include "../shared.h" +#include <tools/hostosinfo.h> +#include <tools/profile.h> + +#include <QtXml/qdom.h> + +#include <regex> + +#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(testDataDir) + +using qbs::Internal::HostOsInfo; +using qbs::Profile; + +class QFileInfo2 : public QFileInfo { +public: + QFileInfo2(const QString &path) : QFileInfo(path) { } + bool isRegularFile() const { return isFile() && !isSymLink(); } + bool isRegularDir() const { return isDir() && !isSymLink(); } + bool isFileSymLink() const { return isFile() && isSymLink(); } + bool isDirSymLink() const { return isDir() && isSymLink(); } +}; + +static qbs::Internal::Version findXcodeVersion() +{ + QProcess process; + process.start("pkgutil", QStringList("--pkg-info-plist=com.apple.pkg.Xcode")); + process.waitForFinished(); + + QDomDocument xcodeVersionDoc; + if (xcodeVersionDoc.setContent(process.readAllStandardOutput())) { + QDomNodeList nodes = xcodeVersionDoc.elementsByTagName(QStringLiteral("key")); + for (int i = 0; i < nodes.count(); ++i) { + QDomElement elem = nodes.at(i).toElement(); + if (elem.text().compare(QStringLiteral("pkg-version")) == 0) { + return qbs::Internal::Version::fromString( + QStringList(elem.nextSiblingElement().text().split( + QLatin1Char('.')).mid(0, 3)).join(QLatin1Char('.')), true); + } + } + } + + return qbs::Internal::Version(); +} + +static QString getEmbeddedBinaryPlist(const QString &file) +{ + QProcess p; + p.start("otool", QStringList() << "-v" << "-X" << "-s" << "__TEXT" << "__info_plist" << file); + p.waitForFinished(); + return QString::fromUtf8(p.readAllStandardOutput()).trimmed(); +} + +TestBlackboxApple::TestBlackboxApple() + : TestBlackboxBase (SRCDIR "/testdata-apple", "blackbox-apple") +{ +} + +void TestBlackboxApple::initTestCase() +{ + if (!HostOsInfo::isMacosHost()) { + QSKIP("only applies on macOS"); + return; + } + + TestBlackboxBase::initTestCase(); +} + +void TestBlackboxApple::appleMultiConfig() +{ + QDir::setCurrent(testDataDir + "/apple-multiconfig"); + QCOMPARE(runQbs(), 0); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/MacOS/singleapp").isExecutable()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/PkgInfo").isRegularFile()); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/MacOS/singleapp_agg").isExecutable()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/PkgInfo").isRegularFile()); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/singlelib").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/singlelib").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/Current").isDirSymLink()); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp").isExecutable()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/PkgInfo").isRegularFile()); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/MacOS/fatmultiapp").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/MacOS/fatmultiapp_debug").isExecutable()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/PkgInfo").isRegularFile()); + + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_debug").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_profile").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/Current").isDirSymLink()); + + for (const QString &variant : { "release", "debug", "profile" }) { + for (const QString &arch : { "x86_64" }) { + QProcess process; + process.setProgram("/usr/bin/arch"); + process.setArguments({ + "-arch", arch, + "-e", "DYLD_IMAGE_SUFFIX=_" + variant, + defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp" + }); + process.start(); + process.waitForFinished(); + QCOMPARE(process.exitCode(), 0); + const auto processStdout = process.readAllStandardOutput(); + QVERIFY2(processStdout.contains("Hello from " + variant.toUtf8() + " " + arch.toUtf8()), + processStdout.constData()); + } + } +} + +void TestBlackboxApple::assetCatalog() +{ + QFETCH(bool, flatten); + + if (HostOsInfo::hostOsVersion() < qbs::Internal::Version(10, 9)) + QSKIP("This test needs at least macOS 10.9."); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/assetcatalog")); + + rmDirR(relativeBuildDir()); + + QbsRunParameters params; + const QString flattens = "ib.flatten:" + QString(flatten ? "true" : "false"); + + // Make sure a dry run does not write anything + params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs" << "--dry-run" << flattens; + QCOMPARE(runQbs(params), 0); + QVERIFY(!directoryExists(relativeBuildDir())); + + params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs" << flattens; + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets")); + + // should not produce a CAR since minimumMacosVersion will be < 10.9 + QVERIFY(!regularFileExists(relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + rmDirR(relativeBuildDir()); + params.arguments.append("cpp.minimumMacosVersion:10.9"); // force CAR generation + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets")); + QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + // this asset catalog happens to have an embedded icon set, + // but this should NOT be built since it is not in the files list + QVERIFY(!(bool)m_qbsStdout.contains(".iconset")); + + // now we'll add the iconset + rmDirR(relativeBuildDir()); + params.arguments.append("project.includeIconset:true"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!(bool)m_qbsStdout.contains("compiling empty.xcassets")); + QVERIFY((bool)m_qbsStdout.contains("compiling empty.iconset")); + + // make sure the nibs/storyboards are in there + QString nib = relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/MainMenu.nib"; + QStringList nibFiles; + if (flatten) { + QVERIFY(regularFileExists(nib)); + } else { + QVERIFY(directoryExists(nib)); + nibFiles = QStringList() << "designable.nib" << "keyedobjects.nib"; + } + + QString storyboardc = relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Storyboard.storyboardc"; + QStringList storyboardcFiles; + if (HostOsInfo::hostOsVersion() >= qbs::Internal::Version(10, 10)) { + QVERIFY(directoryExists(storyboardc)); + + storyboardcFiles = QStringList() + << "1os-k8-h10-view-qKA-a5-eUe.nib" + << "Info.plist" + << "Iqk-Fi-Vhk-view-HRv-3O-Qxh.nib" + << "Main.nib" + << "NSViewController-Iqk-Fi-Vhk.nib" + << "NSViewController-Yem-rc-72E.nib" + << "Yem-rc-72E-view-ODp-aO-Dmf.nib"; + + if (!flatten) { + storyboardcFiles << "designable.storyboard"; + storyboardcFiles.sort(); + } + } + + QCOMPARE(QDir(nib).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), nibFiles); + QCOMPARE(QDir(storyboardc).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), storyboardcFiles); + QbsRunParameters params2 = params; + params2.command = "clean"; + QCOMPARE(runQbs(params2), 0); + QCOMPARE(QDir(nib).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), QStringList()); + QCOMPARE(QDir(storyboardc).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), QStringList()); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/multiple-asset-catalogs")); + rmDirR(relativeBuildDir()); + params.arguments = QStringList(); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("compiling assetcatalog1.xcassets"), m_qbsStdout); + QVERIFY2(m_qbsStdout.contains("compiling assetcatalog2.xcassets"), m_qbsStdout); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/empty-asset-catalogs")); + rmDirR(relativeBuildDir()); + params.arguments = QStringList(); + QCOMPARE(runQbs(params), 0); + QVERIFY2(!m_qbsStdout.contains("compiling assetcatalog1.xcassets"), m_qbsStdout); + QVERIFY2(!m_qbsStdout.contains("compiling assetcatalog2.xcassets"), m_qbsStdout); +} + +void TestBlackboxApple::assetCatalog_data() +{ + QTest::addColumn<bool>("flatten"); + QTest::newRow("flattened") << true; + QTest::newRow("unflattened") << false; +} + +void TestBlackboxApple::bundleStructure() +{ + QFETCH(QString, productName); + QFETCH(QString, productTypeIdentifier); + QFETCH(bool, isShallow); + + QDir::setCurrent(testDataDir + "/bundle-structure"); + QbsRunParameters params; + params.arguments << "project.buildableProducts:" + productName; + if (isShallow) { + // Coerce shallow bundles - don't set bundle.isShallow directly because we want to test the + // automatic detection + params.arguments + << "qbs.targetOS:ios,darwin,bsd,unix" + << "qbs.architectures:arm64"; + } + + if (productName == "ABadApple" || productName == "ABadThirdParty") + params.expectFailure = true; + + rmDirR(relativeBuildDir()); + const int status = runQbs(params); + if (status != 0) { + QVERIFY2(m_qbsStderr.contains("Bundle product type " + + productTypeIdentifier.toLatin1() + + " is not supported."), + m_qbsStderr.constData()); + return; + } + + QCOMPARE(status, 0); + + if (!isShallow) { + if (productName == "A") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/MacOS/A").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/PkgInfo").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "B") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/B").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers").isDirSymLink()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules").isDirSymLink()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/B.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/B").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Headers/dummy.h").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Modules/module.modulemap").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/Current").isDirSymLink()); + } + + if (productName == "C") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/C").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers").isDirSymLink()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules").isDirSymLink()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/C.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/C").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Headers/dummy.h").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Modules/module.modulemap").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/Current").isDirSymLink()); + } + + if (productName == "D") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/MacOS/D").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "E") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/MacOS/E").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/E.appex/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "F") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/MacOS/F").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "G") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/G").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile()); + } + } else { + if (productName == "A") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/A").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/PkgInfo").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/resource.txt").isRegularFile()); + } + + if (productName == "B") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/B").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Info.plist").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules/module.modulemap").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/B.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/resource.txt").isRegularFile()); + } + + if (productName == "C") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/C").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Info.plist").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules/module.modulemap").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/C.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/resource.txt").isRegularFile()); + } + + if (productName == "D") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/D").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/D.bundle/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/resource.txt").isRegularFile()); + } + + if (productName == "E") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/E").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/E.appex/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/resource.txt").isRegularFile()); + } + + if (productName == "F") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/F").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/F.xpc/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/resource.txt").isRegularFile()); + } + + if (productName == "G") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/G").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile()); + } + } +} + +void TestBlackboxApple::bundleStructure_data() +{ + QTest::addColumn<QString>("productName"); + QTest::addColumn<QString>("productTypeIdentifier"); + QTest::addColumn<bool>("isShallow"); + + const auto addRows = [](bool isShallow) { + const QString s = (isShallow ? " shallow" : ""); + QTest::newRow(("A" + s).toLatin1()) << "A" << "com.apple.product-type.application" << isShallow; + QTest::newRow(("ABadApple" + s).toLatin1()) << "ABadApple" << "com.apple.product-type.will.never.exist.ever.guaranteed" << isShallow; + QTest::newRow(("ABadThirdParty" + s).toLatin1()) << "ABadThirdParty" << "org.special.third.party.non.existent.product.type" << isShallow; + QTest::newRow(("B" + s).toLatin1()) << "B" << "com.apple.product-type.framework" << isShallow; + QTest::newRow(("C" + s).toLatin1()) << "C" << "com.apple.product-type.framework.static" << isShallow; + QTest::newRow(("D" + s).toLatin1()) << "D" << "com.apple.product-type.bundle" << isShallow; + QTest::newRow(("E" + s).toLatin1()) << "E" << "com.apple.product-type.app-extension" << isShallow; + QTest::newRow(("F" + s).toLatin1()) << "F" << "com.apple.product-type.xpc-service" << isShallow; + QTest::newRow(("G" + s).toLatin1()) << "G" << "com.apple.product-type.in-app-purchase-content" << isShallow; + }; + + addRows(true); + addRows(false); +} + +void TestBlackboxApple::deploymentTarget() +{ + QFETCH(QString, sdk); + QFETCH(QString, os); + QFETCH(QString, arch); + QFETCH(QString, cflags); + QFETCH(QString, lflags); + + QDir::setCurrent(testDataDir + "/deploymentTarget"); + + QbsRunParameters params; + params.arguments = QStringList() + << "--command-echo-mode" + << "command-line" + << "qbs.targetOS:" + os + << "qbs.architectures:" + arch; + + rmDirR(relativeBuildDir()); + int status = runQbs(params); + + const QStringList skippableMessages = QStringList() + << "There is no matching SDK available for " + sdk + "." + << "x86_64h will be mis-detected as x86_64 with Apple Clang < 6.0" + << "clang: error: unknown argument: '-mtvos-version-min" + << "clang: error: unknown argument: '-mtvos-simulator-version-min" + << "clang: error: unknown argument: '-mwatchos-version-min" + << "clang: error: unknown argument: '-mwatchos-simulator-version-min"; + if (status != 0) { + for (const auto &message : skippableMessages) { + if (m_qbsStderr.contains(message.toUtf8())) + QSKIP(message.toUtf8()); + } + } + + QCOMPARE(status, 0); + QVERIFY2(m_qbsStderr.contains(cflags.toLatin1()), m_qbsStderr.constData()); + QVERIFY2(m_qbsStderr.contains(lflags.toLatin1()), m_qbsStderr.constData()); +} + +void TestBlackboxApple::deploymentTarget_data() +{ + static const QString macos = QStringLiteral("macos,darwin,bsd,unix"); + static const QString ios = QStringLiteral("ios,darwin,bsd,unix"); + static const QString ios_sim = QStringLiteral("ios-simulator,") + ios; + static const QString tvos = QStringLiteral("tvos,darwin,bsd,unix"); + static const QString tvos_sim = QStringLiteral("tvos-simulator,") + tvos; + static const QString watchos = QStringLiteral("watchos,darwin,bsd,unix"); + static const QString watchos_sim = QStringLiteral("watchos-simulator,") + watchos; + + QTest::addColumn<QString>("sdk"); + QTest::addColumn<QString>("os"); + QTest::addColumn<QString>("arch"); + QTest::addColumn<QString>("cflags"); + QTest::addColumn<QString>("lflags"); + + QTest::newRow("macos x86") << "macosx" << macos << "x86" + << "-triple i386-apple-macosx10.4" + << "-macosx_version_min 10.4"; + QTest::newRow("macos x86_64") << "macosx" << macos << "x86_64" + << "-triple x86_64-apple-macosx10.4" + << "-macosx_version_min 10.4"; + + const auto xcodeVersion = findXcodeVersion(); + if (xcodeVersion >= qbs::Internal::Version(6)) + QTest::newRow("macos x86_64h") << "macosx" << macos << "x86_64h" + << "-triple x86_64h-apple-macosx10.12" + << "-macosx_version_min 10.12"; + + QTest::newRow("ios armv7a") << "iphoneos" << ios << "armv7a" + << "-triple thumbv7-apple-ios6.0" + << "-iphoneos_version_min 6.0"; + QTest::newRow("ios armv7s") << "iphoneos" <<ios << "armv7s" + << "-triple thumbv7s-apple-ios7.0" + << "-iphoneos_version_min 7.0"; + QTest::newRow("ios arm64") << "iphoneos" <<ios << "arm64" + << "-triple arm64-apple-ios7.0" + << "-iphoneos_version_min 7.0"; + QTest::newRow("ios-simulator x86") << "iphonesimulator" << ios_sim << "x86" + << "-triple i386-apple-ios6.0" + << "-ios_simulator_version_min 6.0"; + QTest::newRow("ios-simulator x86_64") << "iphonesimulator" << ios_sim << "x86_64" + << "-triple x86_64-apple-ios7.0" + << "-ios_simulator_version_min 7.0"; + + if (xcodeVersion >= qbs::Internal::Version(7)) { + if (xcodeVersion >= qbs::Internal::Version(7, 1)) { + QTest::newRow("tvos arm64") << "appletvos" << tvos << "arm64" + << "-triple arm64-apple-tvos9.0" + << "-tvos_version_min 9.0"; + QTest::newRow("tvos-simulator x86_64") << "appletvsimulator" << tvos_sim << "x86_64" + << "-triple x86_64-apple-tvos9.0" + << "-tvos_simulator_version_min 9.0"; + } + + QTest::newRow("watchos armv7k") << "watchos" << watchos << "armv7k" + << "-triple thumbv7k-apple-watchos2.0" + << "-watchos_version_min 2.0"; + QTest::newRow("watchos-simulator x86") << "watchsimulator" << watchos_sim << "x86" + << "-triple i386-apple-watchos2.0" + << "-watchos_simulator_version_min 2.0"; + } +} + +void TestBlackboxApple::embedInfoPlist() +{ + QDir::setCurrent(testDataDir + QLatin1String("/embedInfoPlist")); + + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/app").isEmpty()); + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/liblib.dylib").isEmpty()); + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/mod.bundle").isEmpty()); + + params.arguments = QStringList(QLatin1String("modules.bundle.embedInfoPlist:false")); + params.expectFailure = true; + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/app").isEmpty()); + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/liblib.dylib").isEmpty()); + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/mod.bundle").isEmpty()); +} + +void TestBlackboxApple::frameworkStructure() +{ + QDir::setCurrent(testDataDir + QLatin1String("/frameworkStructure")); + + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Widget")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Headers/Widget.h")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/PrivateHeaders/WidgetPrivate.h")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Resources/BaseResource")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Resources/en.lproj/EnglishResource")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/Current")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Widget")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Resources")); + + params.command = "resolve"; + params.arguments = QStringList() << "project.includeHeaders:false"; + QCOMPARE(runQbs(params), 0); + QCOMPARE(runQbs(), 0); + + QVERIFY(!directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers")); + QVERIFY(!directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders")); +} + +void TestBlackboxApple::iconset() +{ + QDir::setCurrent(testDataDir + QLatin1String("/ib/iconset")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "iconset.qbs"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("iconset") + "/white.icns")); +} + +void TestBlackboxApple::iconsetApp() +{ + QDir::setCurrent(testDataDir + QLatin1String("/ib/iconsetapp")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "iconsetapp.qbs"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("iconsetapp") + "/iconsetapp.app/Contents/Resources/white.icns")); +} + +void TestBlackboxApple::infoPlist() +{ + QDir::setCurrent(testDataDir + "/infoplist"); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "infoplist.qbs"; + QCOMPARE(runQbs(params), 0); + + QFile infoplist(relativeProductBuildDir("infoplist") + "/infoplist.app/Contents/Info.plist"); + QVERIFY(infoplist.open(QIODevice::ReadOnly)); + const QByteArray fileContents = infoplist.readAll(); + QVERIFY2(fileContents.contains("<key>LSMinimumSystemVersion</key>"), fileContents.constData()); + QVERIFY2(fileContents.contains("<string>10.7</string>"), fileContents.constData()); + QVERIFY2(fileContents.contains("<key>NSPrincipalClass</key>"), fileContents.constData()); +} + +void TestBlackboxApple::objcArc() +{ + QDir::setCurrent(testDataDir + QLatin1String("/objc-arc")); + + QCOMPARE(runQbs(), 0); +} + +void TestBlackboxApple::xcode() +{ + QProcess xcodeSelect; + xcodeSelect.start("xcode-select", QStringList() << "--print-path"); + QVERIFY2(xcodeSelect.waitForStarted(), qPrintable(xcodeSelect.errorString())); + QVERIFY2(xcodeSelect.waitForFinished(), qPrintable(xcodeSelect.errorString())); + QVERIFY2(xcodeSelect.exitCode() == 0, qPrintable(xcodeSelect.readAllStandardError().constData())); + const QString developerPath(QString::fromLocal8Bit(xcodeSelect.readAllStandardOutput().trimmed())); + + std::multimap<std::string, std::string> sdks; + + QProcess xcodebuildShowSdks; + xcodebuildShowSdks.start("xcrun", QStringList() << "xcodebuild" << "-showsdks"); + QVERIFY2(xcodebuildShowSdks.waitForStarted(), qPrintable(xcodebuildShowSdks.errorString())); + QVERIFY2(xcodebuildShowSdks.waitForFinished(), qPrintable(xcodebuildShowSdks.errorString())); + QVERIFY2(xcodebuildShowSdks.exitCode() == 0, qPrintable(xcodebuildShowSdks.readAllStandardError().constData())); + for (const QString &line : QString::fromLocal8Bit(xcodebuildShowSdks.readAllStandardOutput().trimmed()).split('\n', QString::SkipEmptyParts)) { + static const std::regex regexp("^.+\\s+\\-sdk\\s+([a-z]+)([0-9]+\\.[0-9]+)$"); + const auto ln = line.toStdString(); + std::smatch match; + if (std::regex_match(ln, match, regexp)) + sdks.insert({ match[1], match[2] }); + } + + auto range = sdks.equal_range("macosx"); + QStringList sdkValues; + for (auto i = range.first; i != range.second; ++i) + sdkValues.push_back(QString::fromStdString(i->second)); + + QDir::setCurrent(testDataDir + "/xcode"); + QbsRunParameters params; + params.arguments = (QStringList() + << (QStringLiteral("xcode.developerPath:") + developerPath) + << (QStringLiteral("project.sdks:['") + sdkValues.join("','") + "']")); + QCOMPARE(runQbs(params), 0); +} + +QTEST_MAIN(TestBlackboxApple) |