aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@users.noreply.github.com>2020-05-24 13:25:01 -0400
committerGitHub <noreply@github.com>2020-05-24 13:25:01 -0400
commitc205840e737b23614a686a9675b896106cee5c64 (patch)
treef2eb9d93edb1540221daea3682bf13254d28128c
parent03e39ae5dcd4002ac9657a550c48b8e9f85c449c (diff)
parenta38fbb5766b48a9695ea139f79634fc746642bdd (diff)
downloadspmc-c205840e737b23614a686a9675b896106cee5c64.tar.gz
Merge pull request #37 from jhunkeler/version-fixups
Version fixups
-rw-r--r--include/str.h2
-rw-r--r--include/version_spec.h24
-rw-r--r--lib/str.c27
-rw-r--r--lib/version_spec.c422
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--tests/framework.h4
-rw-r--r--tests/test_str_isdigit_s.c21
-rw-r--r--tests/test_str_tolower_s.c25
-rw-r--r--tests/test_version.c95
9 files changed, 306 insertions, 322 deletions
diff --git a/include/str.h b/include/str.h
index 468f674..ae54997 100644
--- a/include/str.h
+++ b/include/str.h
@@ -34,5 +34,7 @@ int isquoted(char *sptr);
char *normalize_space(char *s);
char **strdup_array(char **array);
int strcmp_array(const char **a, const char **b);
+int isdigit_s(char *s);
+char *tolower_s(char *s);
#endif //SPM_STR_H
diff --git a/include/version_spec.h b/include/version_spec.h
index aab6540..b8d3260 100644
--- a/include/version_spec.h
+++ b/include/version_spec.h
@@ -4,10 +4,11 @@
#ifndef SPM_VERSION_SPEC_H
#define SPM_VERSION_SPEC_H
-#define VERSION_BASE 16
-#define VERSION_ADDENDUM_BITS 16
+#define VERSION_MAX 8
+#define VERSION_BASE 10
#define VERSION_DELIM "."
-#define VERSION_LOCAL "="
+#define VERSION_DELIM_LOCAL "+"
+#define VERSION_LOCAL_MAX 255
#define VERSION_OPERATORS " ~!=<>"
#define VERSION_NOOP 1 << 0
#define VERSION_EQ 1 << 1
@@ -16,13 +17,18 @@
#define VERSION_LT 1 << 4
#define VERSION_COMPAT 1 << 5
-// version_spec.c
-char *version_suffix_get_alpha(char *str);
-char *version_suffix_get_modifier(char *str);
-int64_t version_suffix_modifier_calc(char *str);
-int version_suffix_alpha_calc(char *str);
+struct Version {
+ char *local;
+ uint64_t asInt;
+ uint32_t part[VERSION_MAX];
+};
+
+struct Version *version_init();
+int version_read(struct Version **version, char *s);
+void version_info(struct Version *version);
+
uint64_t version_from(const char *str);
-int version_spec_from(const char *op);
+unsigned int version_spec_from(const char *op);
ManifestPackage **find_by_spec(const Manifest *manifest, const char *name, const char *op, const char *version_str);
int pep440_match(const char *version);
struct PEP440 *pep440_version(const char *version);
diff --git a/lib/str.c b/lib/str.c
index 9b5b201..5fbb8ad 100644
--- a/lib/str.c
+++ b/lib/str.c
@@ -817,3 +817,30 @@ int strcmp_array(const char **a, const char **b) {
}
return result;
}
+
+/**
+ * Determine whether a string is comprised of digits
+ * @param s
+ * @return 0=no, 1=yes
+ */
+int isdigit_s(char *s) {
+ for (size_t i = 0; s[i] != '\0'; i++) {
+ if (isdigit(s[i]) == 0) {
+ return 0; // non-digit found, fail
+ }
+ }
+ return 1; // all digits, succeed
+}
+
+/**
+ * Convert input string to lowercase
+ * @param s
+ * @return pointer to input string
+ */
+char *tolower_s(char *s) {
+ for (size_t i = 0; s[i] != '\0'; i++) {
+ s[i] = (char)tolower(s[i]);
+ }
+ return s;
+}
+
diff --git a/lib/version_spec.c b/lib/version_spec.c
index 928fc0f..54f814c 100644
--- a/lib/version_spec.c
+++ b/lib/version_spec.c
@@ -4,358 +4,158 @@
#include "spm.h"
/**
- *
- * @param str
- * @return
- */
-char *version_suffix_get_alpha(char *str) {
- size_t i;
- size_t len = strlen(str);
- for (i = 0; i < len; i++) {
- // return pointer to the first alphabetic character we find
- if (isalpha(str[i])) {
- return &str[i];
- }
- }
- return NULL;
-}
-
-/**
- *
- * @param str
+ * Get the ASCII index of a character from '0'
+ * @param c
* @return
*/
-char *version_suffix_get_modifier(char *str) {
- size_t i;
- char *modifiers[] = {
- "r", // VCS revision
- "rc", // Release Candidate
- "pre", // Pre-Release
- "dev", // Development
- "post", // Post-Release
- NULL,
- };
- for (i = 0; i < strlen(str); i++) {
- for (int m = 0; modifiers[m] != NULL; m++) {
- if (strncasecmp(&str[i], modifiers[m], strlen(modifiers[m])) == 0) {
- return &str[i];
- }
- }
- }
- return NULL;
-}
-
-/**
- *
- * @param str
- * @return
- */
-int64_t version_suffix_modifier_calc(char *str) {
- int64_t result = 0;
- char *tmp_s = str;
-
- if (strncasecmp(str, "rc", 2) == 0) {
- // do rc
- tmp_s += strlen("rc");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "pre", 3) == 0) {
- // do pre
- tmp_s += strlen("pre");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "dev", 3) == 0) {
- // do dev
- tmp_s += strlen("dev");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "post", 4) == 0) {
- // do post
- tmp_s += strlen("post");
- if (isdigit(*tmp_s)) {
- result += atoi(tmp_s);
- }
- else {
- result += 1;
- }
- }
-
+static int version_reindex(char c) {
+ int result;
+ result = c - '0';
return result;
}
/**
- *
- * @param str
- * @return
+ * Initialize `struct Version` for use by other `version_` functions
+ * @return pointer to `struct Version`
*/
-int version_suffix_alpha_calc(char *str) {
- int x = 0;
- char chs[255];
- char *ch = chs;
- memset(chs, '\0', sizeof(chs));
- strncpy(chs, str, strlen(str));
-
- // Handle cases where the two suffixes are not delimited by anything
- // Start scanning one character ahead of the alphabetic suffix and terminate the string
- // when/if we reach another alphabetic character (presumably a version modifer)
- for (int i = 1; chs[i] != '\0'; i++) {
- if (isalpha(chs[i])) {
- chs[i] = '\0';
- }
- }
-
- // Convert character to hex-ish
- x = (*ch - 'a') + 0xa;
-
- // Ensure the string ends with a digit
- if (strlen(str) == 1) {
- strcat(ch, "0");
- }
-
- // Convert trailing numerical value to an integer
- while (*ch != '\0') {
- if (!isdigit(*ch)) {
- ch++;
- continue;
- }
- x += (int)strtol(ch, NULL, 10);
- break;
- }
+struct Version *version_init() {
+ struct Version *result;
- return x;
-}
+ result = calloc(1, sizeof(struct Version));
+ result->local = calloc(VERSION_LOCAL_MAX, sizeof(char));
-static int isdigits(char *s) {
- for (size_t i = 0; s[i] != '\0'; i++) {
- if (isdigit(s[i]) == 0) {
- return 0; // non-digit found, fail
- }
+ for (size_t i = 0; i < VERSION_MAX; i++) {
+ result->part[i] = 0;
}
- return 1; // all digits, succeed
-}
-static int reindex(char c) {
- int result = c - '0';
return result;
}
-static char *tolower_s(char *s) {
- for (size_t i = 0; s[i] != '\0'; i++) {
- s[i] = tolower(s[i]);
- }
- return s;
-}
-
/**
- * Convert a string (`x.y.z.nnnn` or `1abcdefg2` - doesn't matter) to an integer
- * @param s
- * @return
+ * Populate `Version` struct with information derived from input string `s`
+ * @param version `struct Version`
+ * @param s version string
+ * @return 0=success, <0=error
*/
-uint64_t version_from(const char *str) {
- uint64_t result;
- uint64_t addendum;
- char *vstr;
- char *result_tmp;
+int version_read(struct Version **version, char *s) {
+ char *str;
char **part;
- StrList *vconv;
-
- // Check input string isn't NULL
- if (str == NULL) {
- return 0;
- }
+ char *local_version = NULL;
+ size_t num_parts;
- // Make a copy of the input string
- vstr = strdup(str);
- if (vstr == NULL) {
- spmerrno = errno;
- return 0;
+ if (s == NULL) {
+ return -1;
}
- // Initialize StrList
- vconv = strlist_init();
- if (vconv == NULL) {
- spmerrno = errno;
- return 0;
+ str = strdup(s);
+ if (str == NULL) {
+ return -1;
}
- // Convert any uppercase letters to lowercase
- vstr = tolower_s(vstr);
+ str = tolower_s(str);
- // Pop local version suffix
- char *local_version = strstr(vstr, VERSION_LOCAL);
- if (local_version != NULL) {
+ // Store any local version data stored in the input string (unused)
+ if ((local_version = strstr(str, VERSION_DELIM_LOCAL)) != NULL) {
+ strncpy((*version)->local, local_version + 1, VERSION_LOCAL_MAX - 1);
*local_version = '\0';
}
- // Split version string on periods, if any
- part = split(vstr, VERSION_DELIM);
- if (part == NULL) {
- spmerrno = errno;
- return 0;
- }
+ // Split the input string
+ part = split(str, VERSION_DELIM);
- // Populate our StrList with version information
- for (size_t i = 0; part[i] != NULL; i++) {
- char tmp[255] = {0};
- memset(tmp, '\0', sizeof(tmp));
+ // Count records split from the input string
+ for (num_parts = 0; part[num_parts] != NULL; num_parts++);
- if (isdigits(part[i])) {
- // Every character in the string is a digit, so append it as-is
- strlist_append(vconv, part[i]);
+ // Last record of version->part is reserved for additional information
+ uint32_t *addendum = &(*version)->part[VERSION_MAX - 1];
+
+ // Turn those numbers into... numbers
+ for (size_t i = 0; i < num_parts; i++) {
+ uint32_t value = 0;
+
+ if (isdigit_s(part[i])) {
+ // Store the value as-is
+ value = strtoul(part[i], NULL, VERSION_BASE);
} else {
- // Not all characters were digits
- char *data = part[i];
- int digit = isdigit(*data);
-
- // The first character is a digit. Try to consume the whole value
- if (digit > 0) {
- for (size_t ch = 0; *data != '\0' && isdigit(*data) > 0; ch++, data++) {
- tmp[ch] = *data;
+ char tmp[255] = {0};
+ char *other = part[i];
+ if (isdigit(part[i][0])) {
+ for (size_t c = 0; isdigit(part[i][c]); c++) {
+ tmp[c] = part[i][c];
+ other++;
}
- strlist_append(vconv, tmp);
+ value = strtoul(tmp, NULL, VERSION_BASE);
}
-
- while (*data != '\0') {
- // The "+" prefix below indicates the value shall be added to the addendum
- // Not a digit so subtract its ASCII value from '0' with reindex
- // Calculate character index + 1 (i.e. a=1, b=2, ..., z=25)
- sprintf(tmp, "+%d", reindex(*data) + 1);
-
- strlist_append(vconv, tmp);
- data++;
+ // Reindex all alphanumeric characters from ASCII '0'. Ignore everything else.
+ for (size_t c = 0; c < strlen(other); c++) {
+ if (isalnum(other[c])) {
+ // Assign version addendum field
+ *addendum += version_reindex(other[c]);
+ }
}
}
+ // Assign part with integer value
+ (*version)->part[i] = value;
}
- split_free(part);
- // Construct the suffix portion of the version. This value is added to the result to maximize
- // the resolution between different versions (making (1.1.0a > 1.1.0 && 1.1.1 > 1.1.0a) possible)
- addendum = 0L;
- for (size_t i = 0; i < strlist_count(vconv); i++) {
- char *item = strlist_item(vconv, i);
- if (*item == '+') {
- addendum += strtoull(&item[1], NULL, VERSION_BASE);
- // Purge this record from the StrList now that it has fulfilled its purpose
- strlist_remove(vconv, i);
- i--;
- }
+ // Convert all of the individual parts into a single integer
+ for (size_t i = 0; i < VERSION_MAX; i++) {
+ (*version)->asInt = (*version)->asInt << 8 | (*version)->part[i];
}
- result_tmp = join(vconv->data, "");
-
- result = strtoull(result_tmp, NULL, VERSION_BASE);
- result <<= VERSION_ADDENDUM_BITS;
- result += addendum + strlen(result_tmp);
-
- free(vstr);
- strlist_free(vconv);
- return result;
+ split_free(part);
+ free(str);
+ return 0;
}
+
/**
- *
- * @param version_str
- * @return
+ * Free `struct Version` populated by `version_init`
+ * @param version
*/
-int64_t version_from_old(const char *version_str) {
- const char *delim = ".";
- int64_t result = 0;
- if (version_str == NULL) {
- return 0;
- }
-
- int seen_alpha = 0; // Does the tail contain a single character, but not a modifier?
- int seen_modifier = 0; // Does the tail contain "rc", "dev", "pre", and so forth?
- char head[255]; // digits of the string
- char tail[255]; // alphabetic characters of the string
- char *suffix_alpha = NULL; // pointer to location of the first character after the version
- char *suffix_modifier = NULL; // pointer to location of the modifier after the version
- char *x = NULL; // pointer to each string delimited by "."
- char *vstr = strdup(version_str);
- if (!vstr) {
- perror("Version string copy");
- return -1;
+void version_free(struct Version *version) {
+ if (version == NULL) {
+ return;
}
- memset(head, '\0', sizeof(head));
- memset(tail, '\0', sizeof(tail));
-
- // Split the version into parts
- while ((x = strsep(&vstr, delim)) != NULL) {
- int64_t tmp = 0;
-
- // populate the head (numeric characters)
- int has_digit = isdigit(*x);
- int has_alpha = isalpha(*x);
-
- strncpy(head, x, strlen(x));
- for (size_t i = 0; i < strlen(head); i++) {
- if (isalpha(head[i])) {
- // populate the tail (alphabetic characters)
- strncpy(tail, &head[i], strlen(&head[i]));
- head[i] = '\0';
- break;
- }
- }
-
- // Detect alphabetic suffix
- if (!seen_alpha) {
- if ((suffix_alpha = version_suffix_get_alpha(x)) != NULL) {
- seen_alpha = 1;
- }
- }
-
- // Detect modifier suffix
- if (!seen_modifier) {
- if ((suffix_modifier = version_suffix_get_modifier(x)) != NULL) {
- seen_modifier = 1;
- }
- }
+ free(version->local);
+ free(version);
+}
- // Stop processing if the head starts with something other than numbers
- if (!isdigit(head[0])) {
- break;
- }
+/**
+ * Print information about a version (stdout)
+ * @param version `struct Version`
+ */
+void version_info(struct Version *version) {
+ printf("major: %d, ", version->part[0]);
+ printf("minor: %d, ", version->part[1]);
+ printf("patch: %d, ", version->part[2]);
+ for (size_t i = 3; i < VERSION_MAX; i++) {
+ printf("other[%zu]: %d, ", i, version->part[i]);
+ }
+ printf("local: '%s', ", version->local);
+ printf("int : %#llx (%llu)\n", version->asInt, version->asInt);
+}
- // Convert the head to an integer
- tmp = strtoul(head, NULL, 10);
- // Update result. Each portion of the numeric version is its own byte
- // Version PARTS are limited to 255
- result = result << 8 | tmp;
+/**
+ * Convert version string to an integer
+ * @param str version string
+ * @return version as integer
+ */
+uint64_t version_from(const char *str) {
+ // Accept NULL as version "zero"
+ if (str == NULL) {
+ return 0;
}
- if (suffix_alpha != NULL) {
- // Convert the alphabetic suffix to an integer
- int64_t sac = version_suffix_alpha_calc(suffix_alpha);
- result += sac;
- }
+ uint64_t result = 0LL;
+ char *v = strdup(str);
+ struct Version *version = version_init();
- if (suffix_modifier != NULL) {
- // Convert the modifier string to an integer
- int64_t smc = version_suffix_modifier_calc(suffix_modifier);
- if (smc < 0) {
- result -= ~smc + 1;
- }
- else {
- result += smc;
- }
- }
+ version_read(&version, v);
+ result = version->asInt;
- free(vstr);
+ free(v);
+ version_free(version);
return result;
}
@@ -364,8 +164,8 @@ int64_t version_from_old(const char *version_str) {
* @param op
* @return
*/
-int version_spec_from(const char *op) {
- int flags = VERSION_NOOP;
+unsigned int version_spec_from(const char *op) {
+ unsigned int flags = VERSION_NOOP;
size_t len = strlen(op);
for (size_t i = 0; i < len; i++) {
if (op[i] == '>') {
@@ -396,8 +196,8 @@ int version_spec_from(const char *op) {
static int _find_by_spec_compare(const void *a, const void *b) {
const ManifestPackage *aa = *(const ManifestPackage**)a;
const ManifestPackage *bb = *(const ManifestPackage**)b;
- int64_t version_a = version_from(aa->version);
- int64_t version_b = version_from(bb->version);
+ uint64_t version_a = version_from(aa->version);
+ uint64_t version_b = version_from(bb->version);
return version_a > version_b;
}
@@ -420,9 +220,9 @@ ManifestPackage **find_by_spec(const Manifest *manifest, const char *name, const
for (size_t i = 0; i < manifest->records; i++) {
if (strcmp(manifest->packages[i]->name, name) == 0) {
- int64_t version_a = version_from(manifest->packages[i]->version);
- int64_t version_b = version_from(version_str);
- int spec = version_spec_from(op);
+ uint64_t version_a = version_from(manifest->packages[i]->version);
+ uint64_t version_b = version_from(version_str);
+ unsigned int spec = version_spec_from(op);
int res = 0;
if (spec & VERSION_GT && spec & VERSION_EQ) {
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6d27935..623ea68 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,6 +12,14 @@ add_executable(spm
target_link_directories(spm PUBLIC ${OpenSSL_LIBRARY_DIRS} ${CURL_LIBRARY_DIRS})
target_link_libraries(spm libspm ${OpenSSL_LIBRARIES} ${CURL_LIBRARIES})
+## Uncomment to build a scratch executable (i.e. for quick library development / verification)
+#add_executable(spm_scratch
+# scratch.c
+#)
+#
+#target_link_directories(spm_scratch PUBLIC ${OpenSSL_LIBRARY_DIRS} ${CURL_LIBRARY_DIRS})
+#target_link_libraries(spm_scratch libspm ${OpenSSL_LIBRARIES} ${CURL_LIBRARIES})
+
if (LINUX)
target_link_libraries(spm libspm rt)
endif()
diff --git a/tests/framework.h b/tests/framework.h
index aefdef2..c9170b6 100644
--- a/tests/framework.h
+++ b/tests/framework.h
@@ -153,8 +153,8 @@ char *mock_size(size_t size, const char *fill_byte) {
char *mock_image(int img_type, const char *img_name, char **_extra_compiler_args, int *exit_code) {
char libsuffix[10] = {0,};
char code[255] = {0,};
- char code_filename[FILENAME_MAX] = {0,};
- char img_filename[FILENAME_MAX] = {0,};
+ char code_filename[255] = {0,};
+ char img_filename[255] = {0,};
char cmd[PATH_MAX] = {0,};
char *extra_compiler_args = NULL;
Process *proc = NULL;
diff --git a/tests/test_str_isdigit_s.c b/tests/test_str_isdigit_s.c
new file mode 100644
index 0000000..5dd729c
--- /dev/null
+++ b/tests/test_str_isdigit_s.c
@@ -0,0 +1,21 @@
+#include "spm.h"
+#include "framework.h"
+
+const char *testFmt = "case '%s': returned '%d', expected '%d'\n";
+struct TestCase testCase[] = {
+ {.caseValue.str = "1234", .truthValue.signed_int = 1},
+ {.caseValue.str = "1234000000000", .truthValue.signed_int = 1},
+ {.caseValue.str = "1234aa", .truthValue.signed_int = 0},
+ {.caseValue.str = "z1234a", .truthValue.signed_int = 0},
+ {.caseValue.str = "gabcde", .truthValue.signed_int = 0},
+};
+size_t numCases = sizeof(testCase) / sizeof(struct TestCase);
+
+int main(int argc, char *argv[]) {
+ for (size_t i = 0; i < numCases; i++) {
+ int result = isdigit_s(testCase[i].caseValue.str);
+ int truth = testCase[i].truthValue.signed_int;
+ myassert(result == truth, testFmt, testCase[i].caseValue.str, result, truth);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/tests/test_str_tolower_s.c b/tests/test_str_tolower_s.c
new file mode 100644
index 0000000..f36b23d
--- /dev/null
+++ b/tests/test_str_tolower_s.c
@@ -0,0 +1,25 @@
+#include "spm.h"
+#include "framework.h"
+
+const char *testFmt = "case '%s': returned '%d', expected '%d'\n";
+struct TestCase testCase[] = {
+ {.caseValue.str = "ThE", .truthValue.sptr = "the"},
+ {.caseValue.str = "KIdS", .truthValue.sptr = "kids"},
+ {.caseValue.str = "AREn'T", .truthValue.sptr= "aren't"},
+ {.caseValue.str = "aLL", .truthValue.sptr = "all"},
+ {.caseValue.str = "RiGHt", .truthValue.sptr = "right"},
+ {.caseValue.str = "By", .truthValue.sptr = "by"},
+ {.caseValue.str = "THe oFfSpRiNg", .truthValue.sptr = "the offspring"},
+};
+size_t numCases = sizeof(testCase) / sizeof(struct TestCase);
+
+int main(int argc, char *argv[]) {
+ for (size_t i = 0; i < numCases; i++) {
+ char *orig = strdup(testCase[i].caseValue.str);
+ char *result = tolower_s(testCase[i].caseValue.str);
+ const char *truth = testCase[i].truthValue.sptr;
+ myassert(strcmp(result, truth) == 0, testFmt, orig, result, truth);
+ free(orig);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/tests/test_version.c b/tests/test_version.c
new file mode 100644
index 0000000..79c02d0
--- /dev/null
+++ b/tests/test_version.c
@@ -0,0 +1,95 @@
+#include "spm.h"
+#include "framework.h"
+
+
+const char *testFmt = "case %s: returned %llu (0x%llX), expected >%llu (0x%llX)\n";
+struct TestCase testCase[] = {
+ {},
+};
+size_t numCases = sizeof(testCase) / sizeof(struct TestCase);
+
+char *versions[] = {
+ "0.0.1", "0.0.2", "0.0.3", "0.0.4", "0.0.5", "0.0.6", "0.0.7", "0.0.8", "0.0.9", "0.0.10",
+ "0.1", "0.1.1", "0.1.2", "0.1.3", "0.1.4", "0.1.5", "0.1.6", "0.1.7", "0.1.8", "0.1.9", "0.1.10",
+ "0.2", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10",
+ "0.9", "0.9.1", "0.9.2", "0.9.3", "0.9.4", "0.9.5", "0.9.6", "0.9.7", "0.9.8", "0.9.9", "0.9.10",
+ "0.10.0", "0.10.1", "0.10.2", "0.10.3", "0.10.4", "0.10.5", "0.10.6", "0.10.7", "0.10.8", "0.10.9",
+ "0.10.10",
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
+ "100.1a", "100.1a2", "100.3a10", "101", "101.1",
+ "2019.1", "2019.2a", "2019.3",
+ "2020.1", "2020.2a", "2020.3", "2020.4.1",
+};
+char *nonsense[] = {
+ "big_fat_sausage_waffles+glass_of_juice",
+ "big_fat_sausage_waffles.1+cup_of_fresh_broken_glass",
+};
+
+int main(int argc, char *argv[]) {
+ struct Version *prev = NULL;
+
+ for (size_t i = 0; i < (sizeof(versions) / sizeof(char *)); i++) {
+ struct Version *version = version_init();
+ version_read(&version, versions[i]);
+
+ myassert(spmerrno == 0, "Unexpected error: %s\n", spm_strerror(spmerrno));
+
+ if (prev != NULL) {
+ myassert(version->asInt > prev->asInt,
+ testFmt, versions[i],
+ version->asInt, version->asInt,
+ prev->asInt, prev->asInt);
+ }
+
+ prev = version;
+ }
+
+ prev = NULL;
+ for (size_t i = 0; i < (sizeof(nonsense) / sizeof(char *)); i++) {
+ struct Version *version = version_init();
+ version_read(&version, nonsense[i]);
+
+ myassert(spmerrno == 0, "Unexpected error: %s\n", spm_strerror(spmerrno));
+
+ if (prev != NULL) {
+ myassert(version->asInt > prev->asInt,
+ testFmt, nonsense[i],
+ version->asInt, version->asInt,
+ prev->asInt, prev->asInt);
+ }
+
+ prev = version;
+ }
+
+ // Check the wrapper, version_from(), produces the same results as "the long way" above
+ uint64_t prev_i = 0;
+ for (size_t i = 0; i < (sizeof(versions) / sizeof(char *)); i++) {
+ myassert(spmerrno == 0, "Unexpected error: %s\n", spm_strerror(spmerrno));
+
+ uint64_t result = version_from(versions[i]);
+ if (prev_i > 0) {
+ myassert(result > prev_i,
+ testFmt, versions[i],
+ result, result,
+ prev_i, prev_i);
+ }
+
+ prev_i = result;
+ }
+
+ prev_i = 0;
+ for (size_t i = 0; i < (sizeof(nonsense) / sizeof(char *)); i++) {
+ myassert(spmerrno == 0, "Unexpected error: %s\n", spm_strerror(spmerrno));
+
+ uint64_t result = version_from(nonsense[i]);
+ if (prev_i > 0) {
+ myassert(result > prev_i,
+ testFmt, nonsense[i],
+ result, result,
+ prev_i, prev_i);
+ }
+
+ prev_i = result;
+ }
+ return 0;
+}