erosman / support

Support Location for all my extensions
Mozilla Public License 2.0
174 stars 12 forks source link

[FireMonkey] Script updates rejected with major version number of zero (0.*) #437

Closed Tcc100 closed 2 years ago

Tcc100 commented 2 years ago

When I click Update on a script with a major version number of zero, FireMonkey incorrectly shows "There is no new update" regardless of the actual version differences.

I found that the higherVersion function reports incorrect results for some values:

a b expected current suggested
3.0 2.0 true true true
3.0 3.0 false false false
3.0.0 3.0 false false false
3.0 3.0.0 false false false
3.0 3.0.1 false false false
3.0.1 3.0 true FALSE true
0.3.0 0.2.0 true FALSE true
0.3 0.3.1 false false false
0.3.1 0.3 true FALSE true

I suggest to use a function like this instead:

function higherVersion(a, b) {
    a = a.split('.').map(n => parseInt(n));
    b = b.split('.').map(n => parseInt(n));

    for (let i = 0, len = Math.max(a.length, b.length); i < len; i++) {
        if (!a[i]) { return false; }
        else if ((a[i] && !b[i]) || a[i] > b[i]) { return true; }
        else if (a[i] < b[i]) { return false; }
    }
    return false;
}

function higherVersion_new(a, b) {
    a = a.split('.').map(n => parseInt(n));
    b = b.split('.').map(n => parseInt(n));

    for (let i = 0, len = Math.max(a.length, b.length); i < len; i++) {
        if ((a[i] ?? 0) > (b[i] ?? 0)) {
            return true;
        }
    }
    return false;
}

// test code
for (const [a, b, t] of [
        ["3.0",   "2.0",   true ],
        ["3.0",   "3.0",   false],

        ["3.0.0", "3.0",   false],
        ["3.0",   "3.0.0", false],
        ["3.0",   "3.0.1", false],

        ["3.0.1", "3.0",   true ],
        ["0.3.0", "0.2.0", true ],

        ["0.3",   "0.3.1", false],
        ["0.3.1", "0.3",   true ],
    ]) {
    let o = higherVersion(a, b);
    let n = higherVersion_new(a, b);
    console.log(
        `${a}\t${b}\t${t}\t` +
        `${o === t ? o : ("" + o).toUpperCase()}\t` +
        `${n === t ? n : ("" + n).toUpperCase()}`
    )
}

Thanks!

erosman commented 2 years ago

Thank you... I will add it to v2.51

erosman commented 2 years ago

Actually the suggested higherVersion_new function has problem, since it doesn't break early

function higherVersion_new(a, b) {
  a = a.split('.').map(n => parseInt(n));
  b = b.split('.').map(n => parseInt(n));

  for (let i = 0; i < Math.max(a.length, b.length); i++) {
    console.log(a[i] ?? 0, '>', b[i] ?? 0, (a[i] ?? 0) > (b[i] ?? 0));
    if ((a[i] ?? 0) > (b[i] ?? 0)) { return true; }
  }
  return false;
}

console.log(higherVersion('1.1.1', '2.0.0')); 
/*
1 > 2 false
1 > 0 true
true
*/
Tcc100 commented 2 years ago

Indeed, seems that comparing versions is quite hard.

Here is an early-aborting version:

function higherVersion_new2(a, b) {
    a = a.split('.').map(n => parseInt(n));
    b = b.split('.').map(n => parseInt(n));

    for (let i = 0, len = Math.max(a.length, b.length); i < len; i++) {
        if ((a[i] ?? 0) !== (b[i] ?? 0)) {
            return (a[i] ?? 0) > (b[i] ?? 0);
        }
    }
    return false;
}
erosman commented 2 years ago

The tests seems OK. You can try more tests to make sure.


function higherVersion(a, b) {
  a = a.split('.').map(n => parseInt(n));
  b = b.split('.').map(n => parseInt(n));

  for (let i = 0; i < Math.max(a.length, b.length); i++) {
    const [x, y] = [a[i] ?? 0, b[i] ?? 0];
    console.log(a.join('.'), '-', b.join('.'), x, '>', y, x === y ? 'continue' : x > y);
    if (x !== y) { return x > y; }
  }
  return false;
}

for (const [a, b, t] of [
  ["3.0",   "2.0",   true ],
  ["3.0",   "3.0",   false],

  ["3.0.0", "3.0",   false],
  ["3.0",   "3.0.0", false],
  ["3.0",   "3.0.1", false],

  ["3.0.1", "3.0",   true ],
  ["0.3.0", "0.2.0", true ],

  ["0.3",   "0.3.1", false],
  ["0.3.1", "0.3",   true ],
  ["1.1.1", "2.0",   false ],
 ["2.0.0", "2.0.rc1",   ?? ], // <- it is troublesome here
]) {
  const o = higherVersion(a, b);
  console.log(`${a}\t${b}\t${t}\t` + (o === t ? o : ("" + o).toUpperCase()));
}
Tcc100 commented 2 years ago

I just checked greasy fork and did not find a version using -rc1 or alike. Some use a description like "1.1.1 New Features".

You also may consider

return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }) > 0;

(https://stackoverflow.com/a/65687141). This succeeds for -rc1, but considers "3.0.0" and "3.0" differently.

erosman commented 2 years ago

That is not too bad. Although it considers "3.0.0" and "3.0" differently, but that should be OK.. since the script has changed (it didn't have .0 before).

Update: localeCompare seems to work better and there are less mismatches.

function higherVersion(a, b) {
  a = a.split('.').map(n => parseInt(n));
  b = b.split('.').map(n => parseInt(n));

  for (let i = 0; i < Math.max(a.length, b.length); i++) {
    const [x, y] = [a[i] ?? 0, b[i] ?? 0];
    console.log(a.join('.'), '-', b.join('.'), x, '>', y, x === y ? 'continue' : x > y);
    if (x !== y) { return x > y; }
  }
  return false;
}

for (const [a, b, t] of [
  ['3.0',   '2.0',   true ],
  ['3.0',   '3.0',   false],

  ['3.0.0', '3.0',   false],
  ['3.0',   '3.0.0', false],
  ['3.0',   '3.0.1', false],

  ['3.0.1', '3.0',   true ],
  ['0.3.0', '0.2.0', true ],

  ['0.3',   '0.3.1', false],
  ['0.3.1', '0.3',   true ],
  ['1.1.1', '2.0',   false ],
  ['2.0.0', '2.0.rc1',   false ],
  ['2.0.rc2', '2.0.rc1', true ],
  ['2.0.rc2', '2.0.rc3', false ],
  ['1.2.0', '1.10.0',    false ],
  ['1.02.0', '1.1.0',    true ],
  ['1.2.0', '1.10.0',    false ],
  ['1.02.0', '1.1.0',    true ],
  ['1.2.0', '1.10.0',    false ],
  ['1.02.0', '1.1.0',    true ],
  ['1.0.0.0.0.0', '1.0', true],
  ['1.0.0.0.0.0', '1.1', false],
  ['1.0',         '1.0.1', false],
  ['1.0b1',       '1.0', true],
  ['1.0a',        '1.0b', false],
  ['1.1',         '1.0.1b', true],
  ['1.1alpha',    '1.1beta', false],
  ['1.1rc1',      '1.1beta', true],
  ['1.0001',      '1.00000.1.0.0.0.01', true],
  ['1.0.0',       '1.0.0', false],
]) {
  let o = higherVersion(a, b);
  (o === t ? console.log : console.error)(`higherVersion: ${a}\t${b}\t${t}\t` + (o === t ? o : ('' + o).toUpperCase()));

  o = a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }) > 0;
  (o === t ? console.log : console.error)(`localeCompare: ${a}\t${b}\t${t}\t` + (o === t ? o : ('' + o).toUpperCase()));
}