Skip to content

Commit

Permalink
version comparator from Sparkle
Browse files Browse the repository at this point in the history
  • Loading branch information
pypt committed May 31, 2012
1 parent 288acc5 commit 05361b9
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
164 changes: 164 additions & 0 deletions fvversioncomparator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#include "fvversioncomparator.h"
#include <string>
#include <algorithm>
#include <cctype>

//
// Clone of Sparkle's SUStandardVersionComparator.m, so here's original author's
// copyright too:
//
// Copyright 2007 Andy Matuschak. All rights reserved.
//
// Everything's the same except for TypeOfCharacter()
// (because who knows how Foundation does isdigit() and such.)
//


FvVersionComparator::FvVersionComparator()
{
// noop
}

FvVersionComparator::CharacterType FvVersionComparator::TypeOfCharacter(std::string character)
{
if (character == ".") {
return kSeparatorType;
} else if (isdigit(character[0])) {
return kNumberType;
} else if (isspace(character[0])) {
return kSeparatorType;
} else if (ispunct(character[0])) {
return kSeparatorType;
} else {
return kStringType;
}

}

std::vector<std::string> FvVersionComparator::SplitVersionString(std::string version)
{
std::string character;
std::string s;
unsigned long i = 0, n = 0;
CharacterType oldType, newType;
std::vector<std::string> parts;

if (version.length() == 0) {
// Nothing to do here
return parts;
}

s = version.substr(0, 1);
oldType = TypeOfCharacter(s);
n = version.length() - 1;
for (i = 1; i <= n; ++i) {
character = version.substr(i, 1)[0];
newType = TypeOfCharacter(character);
if (oldType != newType || oldType == kSeparatorType) {
// We've reached a new segment
std::string aPart = s;
parts.push_back(aPart);
s = character;
} else {
// Add character to string and continue
s.append(character);
}
oldType = newType;
}

// Add the last part onto the array
parts.push_back(s);
return parts;
}


FvVersionComparator::ComparatorResult FvVersionComparator::CompareVersions(std::string versionA,
std::string versionB)
{
std::vector<std::string> partsA = SplitVersionString(versionA);
std::vector<std::string> partsB = SplitVersionString(versionB);

std::string partA = std::string(""), partB = std::string("");
unsigned long i = 0, n = 0;
int intA, intB;
CharacterType typeA, typeB;

n = std::min(partsA.size(), partsB.size());
for (i = 0; i < n; ++i) {
partA = partsA.at(i);
partB = partsB.at(i);

typeA = TypeOfCharacter(partA);
typeB = TypeOfCharacter(partB);

// Compare types
if (typeA == typeB) {
// Same type; we can compare
if (typeA == kNumberType) {
intA = atoi(partA.c_str());
intB = atoi(partB.c_str());

if (intA > intB) {
return kDescending;
} else if (intA < intB) {
return kAscending;
}
} else if (typeA == kStringType) {
short result = partA.compare(partB);
switch (result) {
case -1: return kAscending; break;
case 1: return kDescending; break;
case 0: /* do nothing */ break;
};
}
} else {
// Not the same type? Now we have to do some validity checking
if (typeA != kStringType && typeB == kStringType) {
// typeA wins
return kDescending;
} else if (typeA == kStringType && typeB != kStringType) {
// typeB wins
return kAscending;
} else {
// One is a number and the other is a period. The period is invalid
if (typeA == kNumberType) {
return kDescending;
} else {
return kAscending;
}
}
}
}
// The versions are equal up to the point where they both still have parts
// Lets check to see if one is larger than the other
if (partsA.size() != partsB.size()) {
// Yep. Lets get the next part of the larger
// n holds the index of the part we want.
std::string missingPart = std::string("");
CharacterType missingType;
ComparatorResult shorterResult, largerResult;

if (partsA.size() > partsB.size()) {
missingPart = partsA.at(n);
shorterResult = kAscending;
largerResult = kDescending;
} else {
missingPart = partsB.at(n);
shorterResult = kDescending;
largerResult = kAscending;
}

missingType = TypeOfCharacter(missingPart);
// Check the type
if (missingType == kStringType) {
// It's a string. Shorter version wins
return shorterResult;
} else {
// It's a number/period. Larger version wins
return largerResult;
}
}

// The 2 strings are identical
return kSame;
}
36 changes: 36 additions & 0 deletions fvversioncomparator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef FVVERSIONCOMPARATOR_H
#define FVVERSIONCOMPARATOR_H

#include <iosfwd>
#include <vector>


class FvVersionComparator
{
public:

typedef enum {
kSame = 0,
kDescending = 1,
kAscending = -1
} ComparatorResult;

static ComparatorResult CompareVersions(std::string versionA,
std::string versionB);

private:

FvVersionComparator();

typedef enum {
kNumberType,
kStringType,
kSeparatorType
} CharacterType;

static CharacterType TypeOfCharacter(std::string character);
static std::vector<std::string> SplitVersionString(std::string version);

};

#endif // FVVERSIONCOMPARATOR_H
44 changes: 44 additions & 0 deletions tests/fvversioncomparatortest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "fvversioncomparatortest.h"
#include "fvversioncomparator.h"


void FvVersionComparatorTest::runAll()
{
testNumbers();
testPrereleases();
testVersionsWithBuildNumbers();
}

void FvVersionComparatorTest::testNumbers()
{
QVERIFY(FvVersionComparator::CompareVersions("1.0", "1.1") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0", "1.0") == FvVersionComparator::kSame);
QVERIFY(FvVersionComparator::CompareVersions("2.0", "1.1") == FvVersionComparator::kDescending);
QVERIFY(FvVersionComparator::CompareVersions("0.1", "0.0.1") == FvVersionComparator::kDescending);
QVERIFY(FvVersionComparator::CompareVersions("0.1", "0.1.2") == FvVersionComparator::kAscending);
}

void FvVersionComparatorTest::testPrereleases()
{
QVERIFY(FvVersionComparator::CompareVersions("1.0a1", "1.0b1") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b1", "1.0") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("0.9", "1.0a1") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b", "1.0b2") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b10", "1.0b11") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b9", "1.0b10") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0rc", "1.0") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b", "1.0") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0pre1", "1.0") == FvVersionComparator::kAscending);

QVERIFY(FvVersionComparator::CompareVersions("1.0", "1.0pre1") == FvVersionComparator::kDescending);
}

void FvVersionComparatorTest::testVersionsWithBuildNumbers()
{
QVERIFY(FvVersionComparator::CompareVersions("1.0 (1234)", "1.0 (1235)") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b1 (1234)", "1.0 (1234)") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b5 (1234)", "1.0b5 (1235)") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0b5 (1234)", "1.0.1b5 (1234)") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("1.0.1b5 (1234)", "1.0.1b6 (1234)") == FvVersionComparator::kAscending);
QVERIFY(FvVersionComparator::CompareVersions("3.3 (5847)", "3.3.1b1 (5902)") == FvVersionComparator::kAscending);
}
21 changes: 21 additions & 0 deletions tests/fvversioncomparatortest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef FVVERSIONCOMPARATORTEST_H
#define FVVERSIONCOMPARATORTEST_H

#include <QtTest/QtTest>


class FvVersionComparatorTest : public QObject
{
Q_OBJECT

public slots:

void runAll();

void testNumbers();
void testPrereleases();
void testVersionsWithBuildNumbers();

};

#endif // FVVERSIONCOMPARATORTEST_H

0 comments on commit 05361b9

Please sign in to comment.