Skip to content

Commit c9dc885

Browse files
authored
Merge pull request #46 from diegoiast/semantic-versioning
Semantic versioning
2 parents 7f4d8c6 + 517054e commit c9dc885

8 files changed

+293
-37
lines changed

CMakeLists.txt

+19
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ project(QSimpleUpdater
33
LANGUAGES CXX
44
)
55

6+
option(QSIMPLE_UPDATER_BUILD_TESTS "Build the unit tests" ON)
7+
68
set(CMAKE_AUTOUIC ON)
79
set(CMAKE_AUTORCC ON)
810
set(CMAKE_AUTOMOC ON)
911

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

15+
if(QSIMPLE_UPDATER_BUILD_TESTS)
16+
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Test)
17+
endif()
18+
1319
add_library(QSimpleUpdater STATIC
1420
etc/resources/qsimpleupdater.qrc
1521
include/QSimpleUpdater.h
@@ -27,3 +33,16 @@ target_include_directories(QSimpleUpdater PUBLIC include)
2733
target_link_libraries(QSimpleUpdater PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets PRIVATE Qt${QT_VERSION_MAJOR}::Network)
2834

2935
add_subdirectory(tutorial)
36+
37+
if(QSIMPLE_UPDATER_BUILD_TESTS)
38+
enable_testing()
39+
add_executable(UnitTests
40+
tests/main.cpp
41+
tests/Test_Versioning.h
42+
tests/Test_Updater.h
43+
tests/Test_QSimpleUpdater.h
44+
tests/Test_Downloader.h
45+
)
46+
add_test(NAME ApiTest COMMAND ApiTest)
47+
target_link_libraries(UnitTests PRIVATE Qt${QT_VERSION_MAJOR}::Test QSimpleUpdater)
48+
endif()

include/QSimpleUpdater.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -67,6 +67,7 @@ class QSU_DECL QSimpleUpdater : public QObject
6767

6868
public:
6969
static QSimpleUpdater *getInstance();
70+
static bool compareVersions(const QString &remote, const QString &local);
7071

7172
bool usesCustomAppcast(const QString &url) const;
7273
bool getNotifyOnUpdate(const QString &url) const;

src/QSimpleUpdater.cpp

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -20,8 +20,9 @@
2020
* THE SOFTWARE.
2121
*/
2222

23-
#include "Updater.h"
2423
#include "QSimpleUpdater.h"
24+
#include "Updater.h"
25+
#include <qregularexpression.h>
2526

2627
static QList<QString> URLS;
2728
static QList<Updater *> UPDATERS;
@@ -45,6 +46,47 @@ QSimpleUpdater *QSimpleUpdater::getInstance()
4546
return &updater;
4647
}
4748

49+
bool QSimpleUpdater::compareVersions(const QString &remote, const QString &local)
50+
{
51+
static QRegularExpression re("v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-(\\w+)(?:(\\d+))?)?");
52+
QRegularExpressionMatch remoteMatch = re.match(remote);
53+
QRegularExpressionMatch localMatch = re.match(local);
54+
55+
if (!remoteMatch.hasMatch() || !localMatch.hasMatch())
56+
{
57+
// Invalid version format
58+
return false;
59+
}
60+
61+
for (int i = 1; i <= 3; ++i)
62+
{
63+
int remoteNum = remoteMatch.captured(i).toInt();
64+
int localNum = localMatch.captured(i).toInt();
65+
66+
if (remoteNum > localNum)
67+
return true;
68+
else if (localNum > remoteNum)
69+
return false;
70+
}
71+
72+
QString remoteSuffix = remoteMatch.captured(4);
73+
QString localSuffix = localMatch.captured(4);
74+
75+
if (remoteSuffix.isEmpty() && !localSuffix.isEmpty())
76+
// Remote is stable, local is pre-release
77+
return true;
78+
if (!remoteSuffix.isEmpty() && localSuffix.isEmpty())
79+
// Remote is pre-release, local is stable
80+
return false;
81+
if (remoteSuffix != localSuffix)
82+
// Compare suffixes lexicographically
83+
return remoteSuffix > localSuffix;
84+
85+
int remoteSuffixNum = remoteMatch.captured(5).toInt();
86+
int localSuffixNum = localMatch.captured(5).toInt();
87+
return remoteSuffixNum > localSuffixNum;
88+
}
89+
4890
/**
4991
* Returns \c true if the \c Updater instance registered with the given \a url
5092
* uses a custom appcast format and/or allows the application to read and

src/Updater.cpp

+2-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* Copyright (c) 2014-2021 Alex Spataru <https://github.com/alex-spataru>
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -511,24 +511,7 @@ void Updater::setUpdateAvailable(const bool available)
511511
*/
512512
bool Updater::compare(const QString &x, const QString &y)
513513
{
514-
QStringList versionsX = x.split(".");
515-
QStringList versionsY = y.split(".");
516-
517-
int count = qMin(versionsX.count(), versionsY.count());
518-
519-
for (int i = 0; i < count; ++i)
520-
{
521-
int a = QString(versionsX.at(i)).toInt();
522-
int b = QString(versionsY.at(i)).toInt();
523-
524-
if (a > b)
525-
return true;
526-
527-
else if (b > a)
528-
return false;
529-
}
530-
531-
return versionsY.count() < versionsX.count();
514+
return QSimpleUpdater::compareVersions(x, y);
532515
}
533516

534517
#if QSU_INCLUDE_MOC

tests/Test_Downloader.h

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#define TEST_DOWNLOADER_H
2525

2626
#include <QtTest>
27-
#include <Downloader.h>
2827

2928
class Test_Downloader : public QObject
3029
{

tests/Test_Updater.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,12 @@
2020
* THE SOFTWARE.
2121
*/
2222

23-
#ifndef TEST_UPDATER_H
24-
#define TEST_UPDATER_H
23+
#pragma once
2524

2625
#include <QtTest>
27-
#include <Updater.h>
26+
#include <QSimpleUpdater.h>
2827

2928
class Test_Updater : public QObject
3029
{
3130
Q_OBJECT
3231
};
33-
34-
#endif

tests/Test_Versioning.h

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright (c) 2015-2016 Alex Spataru <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*/
22+
23+
#pragma once
24+
25+
#include <QtTest>
26+
#include <QSimpleUpdater.h>
27+
28+
class Test_Versioning : public QObject
29+
{
30+
Q_OBJECT
31+
private slots:
32+
void TestPrefixSameVersion()
33+
{
34+
bool needsUpgrade;
35+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.1", "0.0.1");
36+
QVERIFY(!needsUpgrade);
37+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.1", "v0.0.1");
38+
QVERIFY(!needsUpgrade);
39+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.1", "0.0.1");
40+
QVERIFY(!needsUpgrade);
41+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.1", "v0.0.1");
42+
QVERIFY(!needsUpgrade);
43+
}
44+
45+
void MajorMinorRelease()
46+
{
47+
bool needsUpgrade;
48+
49+
// Note how "v" is prefixed in different version, this should not
50+
// matter to us
51+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.2", "0.0.1");
52+
QVERIFY(needsUpgrade);
53+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.2", "v0.0.1");
54+
QVERIFY(needsUpgrade);
55+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.2", "0.0.1");
56+
QVERIFY(needsUpgrade);
57+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.2", "v0.0.1");
58+
QVERIFY(needsUpgrade);
59+
60+
// Revisions are treated as numbers, and not text
61+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "0.0.2");
62+
QVERIFY(needsUpgrade);
63+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "v0.0.2");
64+
QVERIFY(needsUpgrade);
65+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.9", "0.0.8");
66+
QVERIFY(needsUpgrade);
67+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.0.2");
68+
QVERIFY(needsUpgrade);
69+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.0.2");
70+
QVERIFY(needsUpgrade);
71+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.12", "v0.0.2");
72+
QVERIFY(needsUpgrade);
73+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.112", "v0.0.2");
74+
QVERIFY(needsUpgrade);
75+
76+
// Minor versions test
77+
needsUpgrade = QSimpleUpdater::compareVersions("0.1.2", "0.0.8");
78+
QVERIFY(needsUpgrade);
79+
needsUpgrade = QSimpleUpdater::compareVersions("0.1.2", "v0.0.8");
80+
QVERIFY(needsUpgrade);
81+
needsUpgrade = QSimpleUpdater::compareVersions("v0.1.2", "0.0.8");
82+
QVERIFY(needsUpgrade);
83+
needsUpgrade = QSimpleUpdater::compareVersions("v0.1.2", "v0.0.8");
84+
QVERIFY(needsUpgrade);
85+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "0.1.2");
86+
QVERIFY(!needsUpgrade);
87+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "0.1.2");
88+
QVERIFY(!needsUpgrade);
89+
needsUpgrade = QSimpleUpdater::compareVersions("0.0.8", "v0.1.2");
90+
QVERIFY(!needsUpgrade);
91+
needsUpgrade = QSimpleUpdater::compareVersions("v0.0.8", "v0.1.2");
92+
QVERIFY(!needsUpgrade);
93+
94+
// Major versions test
95+
needsUpgrade = QSimpleUpdater::compareVersions("1.0.2", "0.9.8");
96+
QVERIFY(needsUpgrade);
97+
needsUpgrade = QSimpleUpdater::compareVersions("1.1.2", "v0.7.8");
98+
QVERIFY(needsUpgrade);
99+
needsUpgrade = QSimpleUpdater::compareVersions("v2.0.2", "1.9.8");
100+
QVERIFY(needsUpgrade);
101+
needsUpgrade = QSimpleUpdater::compareVersions("v100.2$.2", "v100.1.8");
102+
QVERIFY(needsUpgrade);
103+
}
104+
105+
void AlphaVersions()
106+
{
107+
bool needsUpgrade;
108+
109+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha2", "v1.0.0-alpha1");
110+
QVERIFY(needsUpgrade);
111+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha10", "v1.0.0-alpha1");
112+
QVERIFY(needsUpgrade);
113+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-alpha2");
114+
QVERIFY(!needsUpgrade);
115+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-alpha19");
116+
QVERIFY(!needsUpgrade);
117+
118+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha1");
119+
QVERIFY(needsUpgrade);
120+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha10");
121+
QVERIFY(needsUpgrade);
122+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-alpha18");
123+
QVERIFY(needsUpgrade);
124+
125+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0");
126+
QVERIFY(!needsUpgrade);
127+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1000", "v1.0.0");
128+
QVERIFY(!needsUpgrade);
129+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha88", "v1.0.0");
130+
QVERIFY(!needsUpgrade);
131+
}
132+
133+
void BetaVersions()
134+
{
135+
bool needsUpgrade;
136+
137+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta", "v1.0.0-alpha1");
138+
QVERIFY(needsUpgrade);
139+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2", "v1.0.0-alpha1");
140+
QVERIFY(needsUpgrade);
141+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta0", "v1.0.0-alpha1");
142+
QVERIFY(needsUpgrade);
143+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-beta2");
144+
QVERIFY(!needsUpgrade);
145+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha9999", "v1.0.0-beta19");
146+
QVERIFY(!needsUpgrade);
147+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2", "v1.0.0-alpha1");
148+
QVERIFY(needsUpgrade);
149+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta19", "v1.0.0-alpha9999");
150+
QVERIFY(needsUpgrade);
151+
152+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-beta0");
153+
QVERIFY(needsUpgrade);
154+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-beta1");
155+
QVERIFY(needsUpgrade);
156+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v0.1.0-beta9999");
157+
QVERIFY(needsUpgrade);
158+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v2.0.0-beta9999");
159+
QVERIFY(!needsUpgrade);
160+
}
161+
162+
void RemoteCandidateVersions()
163+
{
164+
bool needsUpgrade;
165+
166+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-rc");
167+
QVERIFY(needsUpgrade);
168+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0", "v1.0.0-rc1");
169+
QVERIFY(needsUpgrade);
170+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-rc1");
171+
QVERIFY(!needsUpgrade);
172+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc2", "v1.0.0-rc1");
173+
QVERIFY(needsUpgrade);
174+
175+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-alpha1");
176+
QVERIFY(needsUpgrade);
177+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-alpha200");
178+
QVERIFY(needsUpgrade);
179+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-beta1");
180+
QVERIFY(needsUpgrade);
181+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-rc1", "v1.0.0-beta2000");
182+
QVERIFY(needsUpgrade);
183+
184+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha1", "v1.0.0-rc1");
185+
QVERIFY(!needsUpgrade);
186+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-alpha200", "v1.0.0-rc1");
187+
QVERIFY(!needsUpgrade);
188+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta1", "v1.0.0-rc1");
189+
QVERIFY(!needsUpgrade);
190+
needsUpgrade = QSimpleUpdater::compareVersions("v1.0.0-beta2000", "v1.0.0-rc1");
191+
QVERIFY(!needsUpgrade);
192+
}
193+
};

0 commit comments

Comments
 (0)