Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function "extension" does not work properly when the basename path starts with dot (.) and has multiple levels. #723

Open
ArturAssisComp opened this issue Nov 22, 2024 · 2 comments

Comments

@ArturAssisComp
Copy link

ArturAssisComp commented Nov 22, 2024

The function extension does not properly extract multiple levels of paths started with dot (.). The following examples does not work:

import 'package:path/path.dart' as p;
import 'package:test/test.dart';

void main() {
  group('Function: extension', () {
    final testCases = [
      (input: '...txt', level: 3, expected: '..txt'),
      (input: '.gitignore.exe.txt.abc', level: 4, expected: '.exe.txt.abc'),
      (input: '.gitignore.exe.txt.abc', level: 40, expected: '.exe.txt.abc'),
      (input: '/this/is/a/path.hello.there/./.', level: 1, expected: '.there'),
      (input: '/this/is/a/path.hello.there/./.', level: 2, expected: '.hello.there'),
      (input: '/this/is/a/path.hello.there/./.', level: 3, expected: '.hello.there'),
      (input: '/this/is/a/.path.hello.there/./.', level: 10, expected: '.hello.there'),
    ];
    for (final (input: input, level: level, expected: expected) in testCases) {
      test('Should return "$expected" when the input is "$input" (lvl: $level)',
          () {
        expect(p.extension(input, level), equals(expected));
      });
    }
  });
}

Extra: (the complete set of tests used):

import 'package:path/path.dart' as p;
import 'package:test/test.dart';

void main() {
  group('Function: extension', () {
    final testCases = [
      (input: '', level: 1, expected: ''),
      (input: 'abc', level: 1, expected: ''),
      (input: 'c://anc/abc', level: 1, expected: ''),
      (input: '.gitignore', level: 1, expected: ''),
      (input: '/a/b/.gitignore', level: 1, expected: ''),
      (input: '..txt', level: 1, expected: '.txt'),
      (input: '...txt', level: 1, expected: '.txt'),
      (input: '...txt', level: 2, expected: '..txt'),
      (input: '...txt', level: 3, expected: '..txt'),
      (input: 'foo.bar.dart.js', level: 1, expected: '.js'),
      (input: 'foo.bar.dart.js', level: 2, expected: '.dart.js'),
      (input: 'foo.bar.dart.js', level: 3, expected: '.bar.dart.js'),
      (input: 'foo.bar.dart.js', level: 10, expected: '.bar.dart.js'),
      (input: '.gitignore.exe.txt.abc', level: 1, expected: '.abc'),
      (input: '.gitignore.exe.txt.abc', level: 2, expected: '.txt.abc'),
      (input: '.gitignore.exe.txt.abc', level: 3, expected: '.exe.txt.abc'),
      (input: '.gitignore.exe.txt.abc', level: 4, expected: '.exe.txt.abc'),
      (input: '.gitignore.exe.txt.abc', level: 40, expected: '.exe.txt.abc'),
      (input: 'gitignore.exe.txt.abc', level: 1, expected: '.abc'),
      (input: 'gitignore.exe.txt.abc', level: 2, expected: '.txt.abc'),
      (input: 'gitignore.exe.txt.abc', level: 3, expected: '.exe.txt.abc'),
      (input: 'gitignore.exe.txt.abc', level: 4, expected: '.exe.txt.abc'),
      (input: 'gitignore.exe.txt.abc', level: 40, expected: '.exe.txt.abc'),
      (input: '/this/is/a/path.hello.there/./.', level: 1, expected: '.there'),
      (input: '/this/is/a/path.hello.there/./.', level: 2, expected: '.hello.there'),
      (input: '/this/is/a/path.hello.there/./.', level: 3, expected: '.hello.there'),
      (input: '/this/is/a/.path.hello.there/./.', level: 10, expected: '.hello.there'),
    ];
    for (final (input: input, level: level, expected: expected) in testCases) {
      test('Should return "$expected" when the input is "$input" (lvl: $level)',
          () {
        expect(p.extension(input, level), equals(expected));
      });
    }
  });
}
@ArturAssisComp
Copy link
Author

Are the situations below, extracted from the package tests, correct?
expect(context.setExtension(r'a\b\', '.x'), r'a\b\.x'); -> given the path a\b\.x, the .x is not an extension in the sense that was defined by the extension function of this package.
expect(context.setExtension(r'a\.', '.x'), r'a\..x'); -> Isn't the path a\. equivalent to the path a?

@lrhn
Copy link
Member

lrhn commented Nov 23, 2024

The description of the extension function is clear about a leading . not counting as starting an extension, so /.x.y has only one extension (.y), and .x will always be a base name.
It seems that multiple leading .s behave the the same way, so extension('..txt') is also an empty string, like extension('.txt') is documented as being. That's probably intentional.

It seems the package does not try to normalize path.ext/./ into path.ext/ first, it just tries to find an extension in ., which starts with a ., so that's an empty extension.
I don't know if it intends to normalize paths, so maybe that's working as intended. It does ignore any number of trailing /s.
Might have to call normalize to be sure that there are no trailing /.s.

I'm not sure this isn't all working as intended. It's definitely within reason.

The setExtension seems somewhat consistent with that behavior, except that it it's probably bugged.

print(p.setExtension('a.b/', '.x')); // a/.x

This recognizes and removes the .b as the extension, but puts the replacement after the /. That's probably not intended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants