Merge pull request #21 from unmojang/evan-goode/custom-authlib-injector
Allow using custom authlib-injector JAR
This commit is contained in:
commit
37fb235492
@ -100,3 +100,7 @@ AppImages are available in the [releases section](https://github.com/unmojang/Fj
|
||||
## Building
|
||||
|
||||
To build the launcher yourself, follow [the instructions on the Prism Launcher website](https://prismlauncher.org/wiki/development/build-instructions) but clone this repo instead.
|
||||
|
||||
## Notes
|
||||
|
||||
- You can easily use a custom version of authlib-injector on an instance. Select the instance in the main window, click "Edit" (or Ctrl+I/Command+I), go to the Version tab, click "Add Agents", and select your authlib-injector JAR. If your JAR is not correctly identified as authlib-injector, make sure the `Agent-Class` field in the JAR's MANIFEST.MF is `moe.yushi.authlibinjector.Premain`.
|
||||
|
@ -95,6 +95,10 @@ set(CORE_SOURCES
|
||||
MMCTime.cpp
|
||||
|
||||
MTPixmapCache.h
|
||||
|
||||
# Manifest.mf parser
|
||||
Manifest.h
|
||||
Manifest.cpp
|
||||
)
|
||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||
set(CORE_SOURCES
|
||||
|
@ -146,9 +146,15 @@ void LaunchController::login()
|
||||
if (m_accountToUse->usesCustomApiServers()) {
|
||||
MinecraftInstancePtr inst = std::dynamic_pointer_cast<MinecraftInstance>(m_instance);
|
||||
|
||||
const auto& authlibInjectorVersion = inst->getPackProfile()->getComponentVersion("moe.yushi.authlibinjector");
|
||||
bool authlibInjectorInstalled = false;
|
||||
const auto& agents = inst->getPackProfile()->getProfile()->getAgents();
|
||||
for (const auto& agent : agents) {
|
||||
if (agent->library()->artifactPrefix() == "moe.yushi:authlibinjector") {
|
||||
authlibInjectorInstalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (authlibInjectorVersion == "") {
|
||||
if (!authlibInjectorInstalled) {
|
||||
// Account uses custom API servers, but authlib-injector is missing
|
||||
|
||||
int globalMissingBehavior = APPLICATION->settings()->get("MissingAuthlibInjectorBehavior").toInt();
|
||||
|
210
launcher/Manifest.cpp
Normal file
210
launcher/Manifest.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Fjord Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2024 Evan Goode <mail@evangoo.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work from OpenJDK, covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
* The GNU General Public License version 2 is available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Manifest.h"
|
||||
|
||||
// Manifest specification:
|
||||
// https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Manifest_Specification
|
||||
|
||||
// The implementation of the Manifest class was ported to C++ from OpenJDK:
|
||||
// https://github.com/openjdk/jdk/blob/0584af23255b6b8f49190eaf2618f3bcc299adfe/src/java.base/share/classes/java/util/jar/Manifest.java#L274
|
||||
|
||||
Manifest::Manifest(std::istream& is)
|
||||
{
|
||||
read(is, std::nullopt);
|
||||
}
|
||||
|
||||
Manifest::Manifest(std::istream& is, const std::string& jar_filename)
|
||||
{
|
||||
read(is, jar_filename);
|
||||
}
|
||||
|
||||
bool Manifest::isValidName(const std::string& name)
|
||||
{
|
||||
const auto len = name.length();
|
||||
return 0 < len && len <= 70;
|
||||
}
|
||||
|
||||
std::string Manifest::getErrorPosition(const std::optional<std::string>& filename, int line_number)
|
||||
{
|
||||
if (!filename.has_value()) {
|
||||
return "line " + std::to_string(line_number);
|
||||
}
|
||||
return "manifest of " + *filename + ":" + std::to_string(line_number);
|
||||
}
|
||||
|
||||
int Manifest::readAttributes(manifest_section_t& section,
|
||||
std::istream& is,
|
||||
lbuf_t lbuf,
|
||||
const std::optional<std::string>& filename,
|
||||
int line_number)
|
||||
{
|
||||
std::optional<std::string> name;
|
||||
std::string value;
|
||||
|
||||
std::string full_line;
|
||||
|
||||
while (!is.eof() && is.getline(lbuf, MANIFEST_MAX_LINE_LENGTH)) {
|
||||
std::size_t len = strlen(lbuf);
|
||||
|
||||
line_number += 1;
|
||||
|
||||
if (len > 0 && lbuf[len - 1] == '\r') {
|
||||
len -= 1;
|
||||
}
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
std::size_t i = 0;
|
||||
if (lbuf[0] == ' ') {
|
||||
// continuation of previous line
|
||||
if (!name.has_value()) {
|
||||
throw std::runtime_error("misplaced continuation line (" + getErrorPosition(filename, line_number) + ")");
|
||||
}
|
||||
full_line.append(lbuf + 1, len - 1);
|
||||
|
||||
if (is.peek() == ' ') {
|
||||
continue;
|
||||
}
|
||||
value = full_line;
|
||||
full_line.clear();
|
||||
} else {
|
||||
while (lbuf[i++] != ':') {
|
||||
if (i >= len) {
|
||||
throw std::runtime_error("invalid header field (" + getErrorPosition(filename, line_number) + ")");
|
||||
}
|
||||
}
|
||||
if (lbuf[i++] != ' ') {
|
||||
throw std::runtime_error("invalid header field (" + getErrorPosition(filename, line_number) + ")");
|
||||
}
|
||||
name = std::string{ lbuf, i - 2 };
|
||||
if (is.peek() == ' ') {
|
||||
full_line.clear();
|
||||
full_line.append(lbuf + i, len - i);
|
||||
continue;
|
||||
}
|
||||
value = std::string{ lbuf + i, len - i };
|
||||
}
|
||||
if (!isValidName(*name)) {
|
||||
throw std::runtime_error("invalid header field name (" + getErrorPosition(filename, line_number) + ")");
|
||||
}
|
||||
section[*name] = value;
|
||||
}
|
||||
if (!is.eof() && is.fail()) {
|
||||
throw std::length_error("line too long (" + getErrorPosition(filename, line_number) + ")");
|
||||
}
|
||||
return line_number;
|
||||
}
|
||||
|
||||
std::optional<std::string> Manifest::parseName(lbuf_t lbuf, std::size_t len)
|
||||
{
|
||||
if (tolower(lbuf[0]) == 'n' && tolower(lbuf[1]) && tolower(lbuf[2]) == 'm' && tolower(lbuf[3]) && lbuf[4] == ':' && lbuf[5] == ' ') {
|
||||
return std::string{ lbuf, 6, len - 6 };
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Manifest::read(std::istream& is, const std::optional<std::string>& jar_filename)
|
||||
{
|
||||
// Line buffer
|
||||
char lbuf[MANIFEST_MAX_LINE_LENGTH];
|
||||
// Read the main attributes for the manifest
|
||||
int line_number = readAttributes(m_main_section, is, lbuf, jar_filename, 0);
|
||||
|
||||
std::optional<std::string> name;
|
||||
|
||||
bool skip_empty_lines = true;
|
||||
std::optional<std::string> lastline;
|
||||
|
||||
while (!is.eof() && is.getline(lbuf, MANIFEST_MAX_LINE_LENGTH)) {
|
||||
std::size_t len = strlen(lbuf);
|
||||
|
||||
line_number += 1;
|
||||
|
||||
if (len > 0 && lbuf[len - 1] == '\r') {
|
||||
len -= 1;
|
||||
}
|
||||
if (len == 0 && skip_empty_lines) {
|
||||
continue;
|
||||
}
|
||||
skip_empty_lines = false;
|
||||
|
||||
if (!name.has_value()) {
|
||||
name = parseName(lbuf, len);
|
||||
if (!name.has_value()) {
|
||||
throw std::runtime_error("invalid manifest format (" + getErrorPosition(jar_filename, line_number) + ")");
|
||||
}
|
||||
if (is.peek() == ' ') {
|
||||
// name is wrapped
|
||||
lastline = std::string{ lbuf + 6, len - 6 };
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// continuation line
|
||||
std::string buf{ *lastline };
|
||||
buf.append(lbuf + 1, len - 1);
|
||||
if (is.peek() == ' ') {
|
||||
// name is wrapped
|
||||
lastline = buf;
|
||||
continue;
|
||||
}
|
||||
name = buf;
|
||||
lastline = std::nullopt;
|
||||
}
|
||||
|
||||
manifest_section_t& attr = m_individual_sections[*name];
|
||||
line_number = readAttributes(attr, is, lbuf, jar_filename, line_number);
|
||||
|
||||
name = std::nullopt;
|
||||
skip_empty_lines = true;
|
||||
}
|
||||
if (!is.eof() && is.fail()) {
|
||||
throw std::length_error("manifest line too long (" + getErrorPosition(jar_filename, line_number) + ")");
|
||||
}
|
||||
}
|
67
launcher/Manifest.h
Normal file
67
launcher/Manifest.h
Normal file
@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Fjord Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2024 Evan Goode <mail@evangoo.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
constexpr const int MANIFEST_MAX_LINE_LENGTH = 512;
|
||||
using lbuf_t = char[MANIFEST_MAX_LINE_LENGTH];
|
||||
|
||||
using manifest_section_t = std::map<std::string, std::string>;
|
||||
using manifest_sections_t = std::map<std::string, manifest_section_t>;
|
||||
|
||||
class Manifest {
|
||||
public:
|
||||
Manifest(std::istream& is);
|
||||
Manifest(std::istream& is, const std::string& jar_filename);
|
||||
Manifest(const Manifest& other)
|
||||
{
|
||||
m_main_section = other.m_main_section;
|
||||
m_individual_sections = other.m_individual_sections;
|
||||
}
|
||||
manifest_section_t& getMainAttributes() { return m_main_section; }
|
||||
manifest_sections_t& getEntries() { return m_individual_sections; }
|
||||
manifest_section_t& getAttributes(const std::string& name) { return m_individual_sections.at(name); }
|
||||
Manifest& operator=(const Manifest& other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
m_main_section = other.m_main_section;
|
||||
m_individual_sections = other.m_individual_sections;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const Manifest& other) const
|
||||
{
|
||||
return m_main_section == other.m_main_section && m_individual_sections == other.m_individual_sections;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string getErrorPosition(const std::optional<std::string>& filename, int line_number);
|
||||
static std::optional<std::string> parseName(lbuf_t lbuf, std::size_t len);
|
||||
static bool isValidName(const std::string& name);
|
||||
int readAttributes(manifest_section_t& section,
|
||||
std::istream& is,
|
||||
lbuf_t lbuf,
|
||||
const std::optional<std::string>& jar_filename,
|
||||
int line_number);
|
||||
void read(std::istream& is, const std::optional<std::string>& jar_filename);
|
||||
manifest_section_t m_main_section;
|
||||
manifest_sections_t m_individual_sections;
|
||||
};
|
@ -1,9 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Library.h"
|
||||
|
||||
static const std::unordered_set<std::string> MANAGED_AGENTS = { "moe.yushi:authlibinjector" };
|
||||
static const std::map<std::string, std::string> AGENT_CLASS_TO_MANAGED_AGENT = { { "moe.yushi.authlibinjector.Premain",
|
||||
"moe.yushi:authlibinjector" } };
|
||||
|
||||
class Agent;
|
||||
|
||||
using AgentPtr = std::shared_ptr<Agent>;
|
||||
|
@ -38,13 +38,10 @@
|
||||
#include <java/JavaVersion.h>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include <unordered_set>
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
const std::unordered_set<std::string> MANAGED_AGENTS = { "moe.yushi:authlibinjector" };
|
||||
|
||||
class ModFolderModel;
|
||||
class ResourceFolderModel;
|
||||
class ResourcePackFolderModel;
|
||||
|
@ -40,6 +40,9 @@
|
||||
#include "Application.h"
|
||||
|
||||
#include <Version.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
@ -49,10 +52,12 @@
|
||||
#include <QSaveFile>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
#include <sstream>
|
||||
|
||||
#include "Exception.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "Manifest.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/ProfileUtils.h"
|
||||
@ -874,6 +879,30 @@ bool PackProfile::installCustomJar_internal(QString filepath)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Manifest> getJarManifest(const QFileInfo& fileinfo)
|
||||
{
|
||||
QuaZip zip(fileinfo.filePath());
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Manifest> manifest;
|
||||
QuaZipFile file(&zip);
|
||||
if (zip.setCurrentFile("META-INF/MANIFEST.MF") && file.open(QIODevice::ReadOnly)) {
|
||||
try {
|
||||
const auto& file_bytes = file.readAll();
|
||||
std::string file_contents(file_bytes.constData(), file_bytes.size());
|
||||
std::istringstream iss{ file_contents };
|
||||
manifest = Manifest(iss, fileinfo.fileName().toStdString());
|
||||
} catch (std::exception& ex) {
|
||||
qDebug() << "Error parsing META/MANIFEST.MF inside " << fileinfo.path() << ":" << ex.what();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
zip.close();
|
||||
return manifest;
|
||||
}
|
||||
|
||||
bool PackProfile::installAgents_internal(QStringList filepaths)
|
||||
{
|
||||
// FIXME code duplication
|
||||
@ -903,7 +932,26 @@ bool PackProfile::installAgents_internal(QStringList filepaths)
|
||||
|
||||
auto agent = std::make_shared<Library>();
|
||||
|
||||
agent->setRawName("custom.agents:" + id + ":1");
|
||||
QString rawName = "custom.agents:" + id + ":1";
|
||||
|
||||
auto manifest = getJarManifest(sourceInfo);
|
||||
if (manifest.has_value()) {
|
||||
const auto& attrs = manifest->getMainAttributes();
|
||||
|
||||
if (auto ac = attrs.find("Agent-Class"); ac != attrs.end()) {
|
||||
const auto& agentClass = ac->second;
|
||||
if (auto ma = AGENT_CLASS_TO_MANAGED_AGENT.find(agentClass); ma != AGENT_CLASS_TO_MANAGED_AGENT.end()) {
|
||||
const auto& artifactPrefix = QString::fromStdString(ma->second);
|
||||
QString version = "1";
|
||||
if (auto iv = attrs.find("Implementation-Version"); iv != attrs.end()) {
|
||||
version = QString::fromStdString(iv->second);
|
||||
}
|
||||
rawName = artifactPrefix + ":" + version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
agent->setRawName(rawName);
|
||||
agent->setFilename(targetBaseName);
|
||||
agent->setDisplayName(sourceInfo.completeBaseName());
|
||||
agent->setHint("local");
|
||||
|
@ -27,6 +27,9 @@ ecm_add_test(ResourceModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION
|
||||
ecm_add_test(TexturePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME TexturePackParse)
|
||||
|
||||
ecm_add_test(Manifest_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Manifest)
|
||||
|
||||
ecm_add_test(DataPackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME DataPackParse)
|
||||
|
||||
|
122
tests/Manifest_test.cpp
Normal file
122
tests/Manifest_test.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include <QTest>
|
||||
#include <QTimer>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Manifest.h"
|
||||
|
||||
class ManifestTest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void test_emptyManifest()
|
||||
{
|
||||
std::istringstream iss;
|
||||
const auto& manifest = new Manifest(iss);
|
||||
QVERIFY(manifest->getMainAttributes().empty());
|
||||
QVERIFY(manifest->getEntries().empty());
|
||||
}
|
||||
void test_parseManifest()
|
||||
{
|
||||
std::string manifest_text{ R"(Manifest-Version: 1.0
|
||||
Created-By: 1.8.0 (Oracle Inc.)
|
||||
|
||||
Name: common/class1.class
|
||||
SHA-256-Digest: D7fzW7bq0W+7YRCfxfZ4LY5LlCy+PXisjRgMCIiebS4=
|
||||
|
||||
Name: common/class2.class
|
||||
SHA-256-Digest: TwUa/b/a2EQRsHWKupdFAR7S/BTeL52xTBvaB8C78Kc=
|
||||
)" };
|
||||
|
||||
std::istringstream iss{ manifest_text };
|
||||
const auto& manifest = new Manifest(iss);
|
||||
|
||||
QVERIFY(manifest->getEntries().size() == 2);
|
||||
|
||||
auto& main_attributes = manifest->getMainAttributes();
|
||||
QVERIFY(main_attributes.size() == 2);
|
||||
QVERIFY(main_attributes["Manifest-Version"] == "1.0");
|
||||
QVERIFY(main_attributes["Created-By"] == "1.8.0 (Oracle Inc.)");
|
||||
|
||||
auto& class1_attributes = manifest->getAttributes("common/class1.class");
|
||||
QVERIFY(class1_attributes.size() == 1);
|
||||
QVERIFY(class1_attributes["SHA-256-Digest"] == "D7fzW7bq0W+7YRCfxfZ4LY5LlCy+PXisjRgMCIiebS4=");
|
||||
|
||||
auto& class2_attributes = manifest->getAttributes("common/class2.class");
|
||||
QVERIFY(class2_attributes.size() == 1);
|
||||
QVERIFY(class2_attributes["SHA-256-Digest"] == "TwUa/b/a2EQRsHWKupdFAR7S/BTeL52xTBvaB8C78Kc=");
|
||||
|
||||
// Manifest should parse even without the trailing newline
|
||||
std::string manifest_text_no_newline{ manifest_text };
|
||||
manifest_text_no_newline.pop_back();
|
||||
|
||||
std::istringstream iss_no_newline{ manifest_text_no_newline };
|
||||
const auto& manifest_no_newline = new Manifest(iss_no_newline);
|
||||
|
||||
QVERIFY(*manifest_no_newline == *manifest);
|
||||
}
|
||||
void test_invalidName()
|
||||
{
|
||||
std::istringstream iss{ R"(Manifest-Version: 1.0
|
||||
|
||||
A-Name-That-Is-Way-Too-Loooooooooooooooooooooooooooooooooooooooooooooooonoooooooooong: 1
|
||||
)" };
|
||||
bool caught = false;
|
||||
try {
|
||||
new Manifest(iss);
|
||||
} catch (const std::runtime_error&) {
|
||||
caught = true;
|
||||
}
|
||||
QVERIFY(caught);
|
||||
}
|
||||
void test_lineTooLong()
|
||||
{
|
||||
std::string manifest_text{ "Manifest-Version: " };
|
||||
manifest_text.append(std::string(MANIFEST_MAX_LINE_LENGTH, '1'));
|
||||
std::istringstream iss{ manifest_text };
|
||||
bool caught = false;
|
||||
try {
|
||||
new Manifest(iss);
|
||||
} catch (const std::length_error&) {
|
||||
caught = true;
|
||||
}
|
||||
QVERIFY(caught);
|
||||
}
|
||||
void test_misplacedContinuation()
|
||||
{
|
||||
std::istringstream iss{ " Manifest-Version: 1.0" };
|
||||
bool caught = false;
|
||||
try {
|
||||
new Manifest(iss);
|
||||
} catch (const std::runtime_error&) {
|
||||
caught = true;
|
||||
}
|
||||
QVERIFY(caught);
|
||||
}
|
||||
void test_misingColon()
|
||||
{
|
||||
std::istringstream iss{ "Manifest-Version 1.0" };
|
||||
bool caught = false;
|
||||
try {
|
||||
new Manifest(iss);
|
||||
} catch (const std::runtime_error&) {
|
||||
caught = true;
|
||||
}
|
||||
QVERIFY(caught);
|
||||
}
|
||||
void test_misingSpace()
|
||||
{
|
||||
std::istringstream iss{ "Manifest-Version:1.0" };
|
||||
bool caught = false;
|
||||
try {
|
||||
new Manifest(iss);
|
||||
} catch (const std::runtime_error&) {
|
||||
caught = true;
|
||||
}
|
||||
QVERIFY(caught);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(ManifestTest)
|
||||
|
||||
#include "Manifest_test.moc"
|
Loading…
Reference in New Issue
Block a user