Skip to content

Commit

Permalink
Merge pull request #46 from diegoiast/semantic-versioning
Browse files Browse the repository at this point in the history
Semantic versioning
  • Loading branch information
alex-spataru authored Nov 19, 2024
2 parents 7f4d8c6 + 517054e commit c9dc885
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 37 deletions.
19 changes: 19 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ project(QSimpleUpdater
LANGUAGES CXX
)

option(QSIMPLE_UPDATER_BUILD_TESTS "Build the unit tests" ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets Network)

if(QSIMPLE_UPDATER_BUILD_TESTS)
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Test)
endif()

add_library(QSimpleUpdater STATIC
etc/resources/qsimpleupdater.qrc
include/QSimpleUpdater.h
Expand All @@ -27,3 +33,16 @@ target_include_directories(QSimpleUpdater PUBLIC include)
target_link_libraries(QSimpleUpdater PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets PRIVATE Qt${QT_VERSION_MAJOR}::Network)

add_subdirectory(tutorial)

if(QSIMPLE_UPDATER_BUILD_TESTS)
enable_testing()
add_executable(UnitTests
tests/main.cpp
tests/Test_Versioning.h
tests/Test_Updater.h
tests/Test_QSimpleUpdater.h
tests/Test_Downloader.h
)
add_test(NAME ApiTest COMMAND ApiTest)
target_link_libraries(UnitTests PRIVATE Qt${QT_VERSION_MAJOR}::Test QSimpleUpdater)
endif()
3 changes: 2 additions & 1 deletion include/QSimpleUpdater.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down Expand Up @@ -67,6 +67,7 @@ class QSU_DECL QSimpleUpdater : public QObject

public:
static QSimpleUpdater *getInstance();
static bool compareVersions(const QString &remote, const QString &local);

bool usesCustomAppcast(const QString &url) const;
bool getNotifyOnUpdate(const QString &url) const;
Expand Down
46 changes: 44 additions & 2 deletions src/QSimpleUpdater.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -20,8 +20,9 @@
* THE SOFTWARE.
*/

#include "Updater.h"
#include "QSimpleUpdater.h"
#include "Updater.h"
#include <qregularexpression.h>

static QList<QString> URLS;
static QList<Updater *> UPDATERS;
Expand All @@ -45,6 +46,47 @@ QSimpleUpdater *QSimpleUpdater::getInstance()
return &updater;
}

bool QSimpleUpdater::compareVersions(const QString &remote, const QString &local)
{
static QRegularExpression re("v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-(\\w+)(?:(\\d+))?)?");
QRegularExpressionMatch remoteMatch = re.match(remote);
QRegularExpressionMatch localMatch = re.match(local);

if (!remoteMatch.hasMatch() || !localMatch.hasMatch())
{
// Invalid version format
return false;
}

for (int i = 1; i <= 3; ++i)
{
int remoteNum = remoteMatch.captured(i).toInt();
int localNum = localMatch.captured(i).toInt();

if (remoteNum > localNum)
return true;
else if (localNum > remoteNum)
return false;
}

QString remoteSuffix = remoteMatch.captured(4);
QString localSuffix = localMatch.captured(4);

if (remoteSuffix.isEmpty() && !localSuffix.isEmpty())
// Remote is stable, local is pre-release
return true;
if (!remoteSuffix.isEmpty() && localSuffix.isEmpty())
// Remote is pre-release, local is stable
return false;
if (remoteSuffix != localSuffix)
// Compare suffixes lexicographically
return remoteSuffix > localSuffix;

int remoteSuffixNum = remoteMatch.captured(5).toInt();
int localSuffixNum = localMatch.captured(5).toInt();
return remoteSuffixNum > localSuffixNum;
}

/**
* Returns \c true if the \c Updater instance registered with the given \a url
* uses a custom appcast format and/or allows the application to read and
Expand Down
21 changes: 2 additions & 19 deletions src/Updater.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down Expand Up @@ -511,24 +511,7 @@ void Updater::setUpdateAvailable(const bool available)
*/
bool Updater::compare(const QString &x, const QString &y)
{
QStringList versionsX = x.split(".");
QStringList versionsY = y.split(".");

int count = qMin(versionsX.count(), versionsY.count());

for (int i = 0; i < count; ++i)
{
int a = QString(versionsX.at(i)).toInt();
int b = QString(versionsY.at(i)).toInt();

if (a > b)
return true;

else if (b > a)
return false;
}

return versionsY.count() < versionsX.count();
return QSimpleUpdater::compareVersions(x, y);
}

#if QSU_INCLUDE_MOC
Expand Down
1 change: 0 additions & 1 deletion tests/Test_Downloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#define TEST_DOWNLOADER_H

#include <QtTest>
#include <Downloader.h>

class Test_Downloader : public QObject
{
Expand Down
7 changes: 2 additions & 5 deletions tests/Test_Updater.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@
* THE SOFTWARE.
*/

#ifndef TEST_UPDATER_H
#define TEST_UPDATER_H
#pragma once

#include <QtTest>
#include <Updater.h>
#include <QSimpleUpdater.h>

class Test_Updater : public QObject
{
Q_OBJECT
};

#endif
193 changes: 193 additions & 0 deletions tests/Test_Versioning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2015-2016 Alex Spataru <alex_spataru@outlook.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#pragma once

#include <QtTest>
#include <QSimpleUpdater.h>

class Test_Versioning : public QObject
{
Q_OBJECT
private slots:
void TestPrefixSameVersion()
{
bool needsUpgrade;
needsUpgrade = QSimpleUpdater::compareVersions("0.0.1", "0.0.1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.0.1", "v0.0.1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.1", "0.0.1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.1", "v0.0.1");
QVERIFY(!needsUpgrade);
}

void MajorMinorRelease()
{
bool needsUpgrade;

// Note how "v" is prefixed in different version, this should not
// matter to us
needsUpgrade = QSimpleUpdater::compareVersions("0.0.2", "0.0.1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.0.2", "v0.0.1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.2", "0.0.1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.2", "v0.0.1");
QVERIFY(needsUpgrade);

// Revisions are treated as numbers, and not text
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "0.0.2");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "v0.0.2");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.9", "0.0.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.0.2");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.0.2");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.12", "v0.0.2");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.112", "v0.0.2");
QVERIFY(needsUpgrade);

// Minor versions test
needsUpgrade = QSimpleUpdater::compareVersions("0.1.2", "0.0.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.1.2", "v0.0.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.1.2", "0.0.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.1.2", "v0.0.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "0.1.2");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "0.1.2");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "v0.1.2");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.1.2");
QVERIFY(!needsUpgrade);

// Major versions test
needsUpgrade = QSimpleUpdater::compareVersions("1.0.2", "0.9.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("1.1.2", "v0.7.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v2.0.2", "1.9.8");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v100.2$.2", "v100.1.8");
QVERIFY(needsUpgrade);
}

void AlphaVersions()
{
bool needsUpgrade;

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha2", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha10", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-alpha2");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-alpha19");
QVERIFY(!needsUpgrade);

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha10");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha18");
QVERIFY(needsUpgrade);

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1000", "v1.0.0");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha88", "v1.0.0");
QVERIFY(!needsUpgrade);
}

void BetaVersions()
{
bool needsUpgrade;

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta0", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-beta2");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha9999", "v1.0.0-beta19");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta19", "v1.0.0-alpha9999");
QVERIFY(needsUpgrade);

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-beta0");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-beta1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v0.1.0-beta9999");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v2.0.0-beta9999");
QVERIFY(!needsUpgrade);
}

void RemoteCandidateVersions()
{
bool needsUpgrade;

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-rc");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-rc1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-rc1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc2", "v1.0.0-rc1");
QVERIFY(needsUpgrade);

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-alpha1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-alpha200");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-beta1");
QVERIFY(needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-beta2000");
QVERIFY(needsUpgrade);

needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-rc1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha200", "v1.0.0-rc1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta1", "v1.0.0-rc1");
QVERIFY(!needsUpgrade);
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2000", "v1.0.0-rc1");
QVERIFY(!needsUpgrade);
}
};
Loading

0 comments on commit c9dc885

Please sign in to comment.