hslua-module-path-1.1.1/0000755000000000000000000000000007346545000013234 5ustar0000000000000000hslua-module-path-1.1.1/CHANGELOG.md0000644000000000000000000000254307346545000015051 0ustar0000000000000000# Changelog `hslua-module-paths` uses [PVP Versioning][]. ## hslua-module-path-1.1.1 Released 2024-01-18. - Relaxed upper bound for text, and filepath, allowing text-2.1, filepath-1.5. ## hslua-module-path-1.1.0 Released 2023-03-13. - Update to hslua-2.3; this includes the addition of type initializers to the module and type specifiers to the fields. - Fixed tests for `make_relative` on Windows. ## hslua-module-path-1.0.3 Released 2022-08-19. - Fixed `make_relative` for longer base paths: Ensure that the function produces correct results in cases where the root (base) path has more components than the path that should be made relative. ## hslua-module-path-1.0.2 Released 2022-02-19. - Adjusted package bounds, for hslua-core, hslua-marshalling, and hslua-packaging. ## hslua-module-path-1.0.1 - Bumped upper bound of hslua-core and hslua-marshalling to allow their respective version 2.1. ## hslua-module-path-1.0.0 - Updated to hslua 2.0. ## hslua-module-path-0.1.0.1 Released 2021-02-06. - Changed minimal cabal version to 2.2. ## hslua-module-path-0.1.0 Released 2021-02-02. - Fixed `directory`. This was the same as normalize. - Improved documentation. - Added more tests. ## hslua-module-path-0.0.1 Released 2021-02-01. - Initially created. [PVP Versioning]: https://pvp.haskell.org hslua-module-path-1.1.1/LICENSE0000644000000000000000000000206507346545000014244 0ustar0000000000000000MIT License Copyright © 2020-2024 Albert Krewinkel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hslua-module-path-1.1.1/README.md0000644000000000000000000000727007346545000014521 0ustar0000000000000000# hslua-module-path [![GitHub CI][CI badge]](https://github.com/hslua/hslua-module-paths/actions) [![Hackage][Hackage badge]](https://hackage.haskell.org/package/hslua-module-path) [![Stackage Lts][Stackage Lts badge]](http://stackage.org/lts/package/hslua-module-path) [![Stackage Nightly][Stackage Nightly badge]](http://stackage.org/nightly/package/hslua-module-path) [![MIT license][License badge]](LICENSE) [CI badge]: https://img.shields.io/github/workflow/status/hslua/hslua/CI.svg?logo=github [Hackage badge]: https://img.shields.io/hackage/v/hslua-module-path.svg?logo=haskell [Stackage Lts badge]: http://stackage.org/package/hslua-module-path/badge/lts [Stackage Nightly badge]: http://stackage.org/package/hslua-module-path/badge/nightly [License badge]: https://img.shields.io/badge/license-MIT-blue.svg Lua module to work with file paths. ## path Module for file path manipulations. #### separator The character that separates directories. #### search\_path\_separator The character that is used to separate the entries in the `PATH` environment variable. ### Functions #### directory (filepath) Get the directory name; move up one level. Parameters: filepath path (string) Returns: - The filepath up to the last directory separator. (string) #### filename (filepath) Get the file name. Parameters: filepath path (string) Returns: - File name part of the input path. (string) #### is\_absolute (filepath) Checks whether a path is absolute, i.e. not fixed to a root. Parameters: filepath path (string) Returns: - `true` iff `filepath` is an absolute path, `false` otherwise. (boolean) #### is\_relative (filepath) Checks whether a path is relative or fixed to a root. Parameters: filepath path (string) Returns: - `true` iff `filepath` is a relative path, `false` otherwise. (boolean) #### join (filepaths) Join path elements back together by the directory separator. Parameters: filepaths path components (list of strings) Returns: - The joined path. (string) #### make\_relative (path, root, unsafe) Contract a filename, based on a relative path. Note that the resulting path will never introduce `..` paths, as the presence of symlinks means `../b` may not reach `a/b` if it starts from `a/c`. For a worked example see [this blog post](http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html). Parameters: path path to be made relative (string) root root path (string) unsafe whether to allow `..` in the result. (boolean) Returns: - contracted filename (string) #### normalize (filepath) Normalizes a path. - `//` outside of the drive can be made blank - `/` becomes the `path.separator` - `./` -> ’’ - an empty path becomes `.` Parameters: filepath path (string) Returns: - The normalized path. (string) #### split (filepath) Splits a path by the directory separator. Parameters: filepath path (string) Returns: - List of all path components. (list of strings) #### split\_extension (filepath) Splits the last extension from a file path and returns the parts. The extension, if present, includes the leading separator; if the path has no extension, then the empty string is returned as the extension. Parameters: filepath path (string) Returns: - filepath without extension (string) - extension or empty string (string) #### split\_search\_path (search\_path) Takes a string and splits it on the `search_path_separator` character. Blank items are ignored on Windows, and converted to `.` on Posix. On Windows path elements are stripped of quotes. Parameters: search\_path platform-specific search path (string) Returns: - list of directories in search path (list of strings) hslua-module-path-1.1.1/hslua-module-path.cabal0000644000000000000000000000525007346545000017553 0ustar0000000000000000cabal-version: 2.2 name: hslua-module-path version: 1.1.1 synopsis: Lua module to work with file paths. description: Lua module to work with file paths in a platform independent way. homepage: https://hslua.org/ bug-reports: https://github.com/hslua/hslua/issues license: MIT license-file: LICENSE author: Albert Krewinkel maintainer: Albert Krewinkel copyright: © 2020-2024 Albert Krewinkel category: Foreign build-type: Simple extra-doc-files: README.md CHANGELOG.md extra-source-files: test/test-path.lua tested-with: GHC == 8.4.4 , GHC == 8.6.5 , GHC == 8.8.4 , GHC == 8.10.7 , GHC == 9.0.2 , GHC == 9.2.8 , GHC == 9.4.8 , GHC == 9.6.3 , GHC == 9.8.1 source-repository head type: git location: https://github.com/hslua/hslua subdir: hslua-module-path common common-options build-depends: base >= 4.9.1 && < 5 , filepath >= 1.4 && < 1.6 , hslua-core >= 2.1 && < 2.4 , hslua-marshalling >= 2.1 && < 2.4 , hslua-packaging >= 2.3 && < 2.4 , text >= 1.2 && < 2.2 ghc-options: -Wall -Wcompat -Widentities -Wincomplete-uni-patterns -Wincomplete-record-updates if impl(ghc >= 8.0) ghc-options: -Wredundant-constraints if impl(ghc >= 8.2) ghc-options: -fhide-source-paths if impl(ghc >= 8.4) ghc-options: -Wmissing-export-lists -Wpartial-fields if impl(ghc >= 8.8) ghc-options: -Wmissing-deriving-strategies default-language: Haskell2010 library import: common-options hs-source-dirs: src exposed-modules: HsLua.Module.Path test-suite hslua-module-path-test import: common-options type: exitcode-stdio-1.0 hs-source-dirs: test main-is: test-hslua-module-path.hs build-depends: base , hslua-module-path , tasty , tasty-hunit , tasty-lua >= 1.0 && < 1.2 , text ghc-options: -threaded -rtsopts -with-rtsopts=-N hslua-module-path-1.1.1/src/HsLua/Module/0000755000000000000000000000000007346545000016264 5ustar0000000000000000hslua-module-path-1.1.1/src/HsLua/Module/Path.hs0000644000000000000000000002501607346545000017520 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-| Module : HsLua.Module.Path Copyright : © 2021-2024 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with file paths. -} module HsLua.Module.Path ( -- * Module documentedModule -- * Fields , separator , search_path_separator -- * Path manipulation , add_extension , combine , directory , filename , is_absolute , is_relative , join , make_relative , normalize , split , split_extension , split_search_path , treat_strings_as_paths ) where import Data.Text (Text) import Data.Version (Version, makeVersion) import HsLua.Core ( LuaError, getglobal, getmetatable, nth, pop, rawset, remove, top ) import HsLua.Marshalling ( Peeker, peekList, peekString, pushList, pushName, pushString ) import HsLua.Packaging import qualified Data.Text as T import qualified System.FilePath as Path -- | The @path@ module specification. documentedModule :: LuaError e => Module e documentedModule = Module { moduleName = "path" , moduleDescription = "Module for file path manipulations." , moduleFields = fields , moduleFunctions = functions , moduleOperations = [] , moduleTypeInitializers = [] } -- -- Fields -- -- | Exported fields. fields :: [Field e] fields = [ separator , search_path_separator ] -- | Wrapper for @'Path.pathSeparator'@. separator :: Field e separator = Field { fieldName = "separator" , fieldType = "string" , fieldDescription = "The character that separates directories." , fieldPushValue = pushString [Path.pathSeparator] } -- | Wrapper for @'Path.searchPathSeparator'@. search_path_separator :: Field e search_path_separator = Field { fieldName = "search_path_separator" , fieldType = "string" , fieldDescription = "The character that is used to separate the entries in " <> "the `PATH` environment variable." , fieldPushValue = pushString [Path.searchPathSeparator] } -- -- Functions -- functions :: LuaError e => [DocumentedFunction e] functions = [ directory , filename , is_absolute , is_relative , join , make_relative , normalize , split , split_extension , split_search_path , treat_strings_as_paths ] -- | See @Path.takeDirectory@ directory :: DocumentedFunction e directory = defun "directory" ### liftPure Path.takeDirectory <#> filepathParam =#> filepathResult "The filepath up to the last directory separator." #? ("Gets the directory name, i.e., removes the last directory " <> "separator and everything after from the given path.") `since` initialVersion -- | See @Path.takeFilename@ filename :: DocumentedFunction e filename = defun "filename" ### liftPure Path.takeFileName <#> filepathParam =#> filepathResult "File name part of the input path." #? "Get the file name." `since` initialVersion -- | See @Path.isAbsolute@ is_absolute :: DocumentedFunction e is_absolute = defun "is_absolute" ### liftPure Path.isAbsolute <#> filepathParam =#> boolResult ("`true` iff `filepath` is an absolute path, " <> "`false` otherwise.") #? "Checks whether a path is absolute, i.e. not fixed to a root." `since` initialVersion -- | See @Path.isRelative@ is_relative :: DocumentedFunction e is_relative = defun "is_relative" ### liftPure Path.isRelative <#> filepathParam =#> boolResult ("`true` iff `filepath` is a relative path, " <> "`false` otherwise.") #? "Checks whether a path is relative or fixed to a root." `since` initialVersion -- | See @Path.joinPath@ join :: LuaError e => DocumentedFunction e join = defun "join" ### liftPure Path.joinPath <#> parameter (peekList peekFilePath) "{string,...}" "filepaths" "path components" =#> filepathResult "The joined path." #? "Join path elements back together by the directory separator." `since` initialVersion make_relative :: DocumentedFunction e make_relative = defun "make_relative" ### liftPure3 makeRelative <#> parameter peekFilePath "string" "path" "path to be made relative" <#> parameter peekFilePath "string" "root" "root path" <#> opt (boolParam "unsafe" "whether to allow `..` in the result.") =#> filepathResult "contracted filename" #? mconcat [ "Contract a filename, based on a relative path. Note that the " , "resulting path will never introduce `..` paths, as the " , "presence of symlinks means `../b` may not reach `a/b` if it " , "starts from `a/c`. For a worked example see " , "[this blog post](http://neilmitchell.blogspot.co.uk" , "/2015/10/filepaths-are-subtle-symlinks-are-hard.html)." ] `since` initialVersion -- | See @Path.normalise@ normalize :: DocumentedFunction e normalize = defun "normalize" ### liftPure Path.normalise <#> filepathParam =#> filepathResult "The normalized path." #? T.unlines [ "Normalizes a path." , "" , " - `//` makes sense only as part of a (Windows) network drive;" , " elsewhere, multiple slashes are reduced to a single" , " `path.separator` (platform dependent)." , " - `/` becomes `path.separator` (platform dependent)." , " - `./` is removed." , " - an empty path becomes `.`" ] `since` initialVersion -- | See @Path.splitDirectories@. -- -- Note that this does /not/ wrap @'Path.splitPath'@, as that function -- adds trailing slashes to each directory, which is often inconvenient. split :: LuaError e => DocumentedFunction e split = defun "split" ### liftPure Path.splitDirectories <#> filepathParam =#> filepathListResult "List of all path components." #? "Splits a path by the directory separator." `since` initialVersion -- | See @Path.splitExtension@ split_extension :: DocumentedFunction e split_extension = defun "split_extension" ### liftPure Path.splitExtension <#> filepathParam =#> (functionResult (pushString . fst) "string" "filepath without extension" ++ functionResult (pushString . snd) "string" "extension or empty string") #? ("Splits the last extension from a file path and returns the parts. " <> "The extension, if present, includes the leading separator; " <> "if the path has no extension, then the empty string is returned " <> "as the extension.") `since` initialVersion -- | Wraps function @'Path.splitSearchPath'@. split_search_path :: LuaError e => DocumentedFunction e split_search_path = defun "split_search_path" ### liftPure Path.splitSearchPath <#> Parameter { parameterPeeker = peekString , parameterDoc = ParameterDoc { parameterName = "search_path" , parameterType = "string" , parameterDescription = "platform-specific search path" , parameterIsOptional = False } } =#> filepathListResult "list of directories in search path" #? ("Takes a string and splits it on the `search_path_separator` " <> "character. Blank items are ignored on Windows, " <> "and converted to `.` on Posix. " <> "On Windows path elements are stripped of quotes.") `since` initialVersion -- | Join two paths with a directory separator. Wraps @'Path.combine'@. combine :: DocumentedFunction e combine = defun "combine" ### liftPure2 Path.combine <#> filepathParam <#> filepathParam =#> filepathResult "combined paths" #? "Combine two paths with a path separator." -- | Adds an extension to a file path. Wraps @'Path.addExtension'@. add_extension :: DocumentedFunction e add_extension = defun "add_extension" ### liftPure2 Path.addExtension <#> filepathParam <#> Parameter { parameterPeeker = peekString , parameterDoc = ParameterDoc { parameterName = "extension" , parameterType = "string" , parameterDescription = "an extension, with or without separator dot" , parameterIsOptional = False } } =#> filepathResult "filepath with extension" #? "Adds an extension, even if there is already one." `since` initialVersion stringAugmentationFunctions :: LuaError e => [DocumentedFunction e] stringAugmentationFunctions = [ directory , filename , is_absolute , is_relative , normalize , split , split_extension , split_search_path ] treat_strings_as_paths :: LuaError e => DocumentedFunction e treat_strings_as_paths = defun "treat_strings_as_paths" ### do let addFunction fn = do pushName (functionName fn) pushDocumentedFunction fn rawset (nth 3) -- for some reason we can't just dump all functions into the -- string metatable, but have to use the string module for -- non-metamethods. pushString "" *> getmetatable top *> remove (nth 2) mapM_ addFunction [setName "__add" add_extension, setName "__div" combine] pop 1 -- string metatable _ <- getglobal "string" mapM_ addFunction stringAugmentationFunctions pop 1 -- string module =#> [] #? ("Augment the string module such that strings can be used as " <> "path objects.") `since` initialVersion -- -- Parameters -- -- | Retrieves a file path from the stack. peekFilePath :: Peeker e FilePath peekFilePath = peekString -- | Filepath function parameter. filepathParam :: Parameter e FilePath filepathParam = parameter peekFilePath "string" "filepath" "path" -- | Result of a function returning a file path. filepathResult :: Text -- ^ Description -> FunctionResults e FilePath filepathResult = stringResult -- | List of filepaths function result. filepathListResult :: LuaError e => Text -- ^ Description -> FunctionResults e [FilePath] filepathListResult = functionResult (pushList pushString) "{string,...}" -- -- Helpers -- -- | Alternative version of @'Path.makeRelative'@, which introduces @..@ -- paths if desired. makeRelative :: FilePath -- ^ path to be made relative -> FilePath -- ^ root directory from which to start -> Maybe Bool -- ^ whether to use unsafe relative paths. -> FilePath makeRelative path root (Just True) | Path.equalFilePath root path = "." | Path.takeDrive root /= Path.takeDrive path = path | otherwise = let toParts = Path.splitDirectories . Path.normalise go (pp:pps) (rp:rps) | pp == rp = go pps rps go pps rps = Path.joinPath $ replicate (length rps) ".." ++ pps in go (toParts path) (toParts root) makeRelative path root _unsafe = Path.makeRelative root path -- | First published version of this library. initialVersion :: Version initialVersion = makeVersion [0,1,0] hslua-module-path-1.1.1/test/0000755000000000000000000000000007346545000014213 5ustar0000000000000000hslua-module-path-1.1.1/test/test-hslua-module-path.hs0000644000000000000000000000370307346545000021060 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-| Module : Main Copyright : © 2021-2024 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Stability : stable Portability : Requires language extensions ForeignFunctionInterface, OverloadedStrings. Tests for the `path` Lua module. -} module Main (main) where import Control.Monad (void) import HsLua.Core (Lua, top) import HsLua.Packaging ( preloadModule, preloadModuleWithName, pushModule , registerModule) import HsLua.Module.Path (documentedModule) import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit (assertEqual, testCase) import Test.Tasty.Lua (translateResultsFromFile) import qualified HsLua.Core as Lua main :: IO () main = do luaTestResults <- Lua.run @Lua.Exception $ do Lua.openlibs registerModule documentedModule Lua.pop 1 translateResultsFromFile "test/test-path.lua" defaultMain $ testGroup "hslua-module-path" [tests, luaTestResults] -- | HSpec tests for the Lua 'system' module tests :: TestTree tests = testGroup "HsLua path module" [ testCase "path module can be pushed to the stack" $ Lua.run (void (pushModule documentedModule) :: Lua ()) , testCase "path module can be added to the preloader" . Lua.run $ do Lua.openlibs preloadModule documentedModule assertEqual' "function not added to preloader" Lua.TypeFunction =<< do void $ Lua.getglobal "package" *> Lua.getfield top "preload" *> Lua.getfield top "path" Lua.ltype (-1) , testCase "path module can be loaded as hspath" . Lua.run $ do Lua.openlibs preloadModuleWithName documentedModule "hspath" assertEqual' "loading the module fails " Lua.OK =<< Lua.dostring "require 'hspath'" ] assertEqual' :: (Show a, Eq a) => String -> a -> a -> Lua () assertEqual' msg expected = Lua.liftIO . assertEqual msg expected hslua-module-path-1.1.1/test/test-path.lua0000644000000000000000000001403207346545000016627 0ustar0000000000000000-- -- Tests for the system module -- local path = require 'path' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert -- Check existence static fields return { group 'static fields' { test('separator', function () assert.are_equal(type(path.separator), 'string') end), test('search_path_separator', function () assert.are_equal(type(path.search_path_separator), 'string') end), }, group 'directory' { test('directory from file path', function () print(path.join{'/', '2020', 'img', 'mask.jpg'}) assert.are_equal( path.directory(path.join{'/', '2020', 'img', 'mask.jpg'}), path.join{'/', '2020', 'img'} ) end), test('drops trailing path sep', function () assert.are_equal( path.directory(path.join{'this', 'that'} .. path.separator), path.join{'this', 'that'} ) end), test('returns dot if given just a filename', function () assert.are_equal( path.directory('index.html'), '.' ) end) }, group 'filename' { test('removes directories', function () assert.are_equal( path.filename(path.join{'paper', 'refs.bib'}), 'refs.bib' ) assert.are_equal( path.filename(path.join{'www', '.htaccess'}), '.htaccess' ) end), test('empty if no filename present', function () assert.are_equal( path.filename(path.join{'usr', 'bin'} .. path.separator), '' ) end) }, group 'is_absolute' { test('network path is absolute', function () local paths = {'c:', 'Applications'} assert.is_truthy(path.is_absolute '//foo/bar') end), test('pure filename is not absolute', function () assert.is_falsy(path.is_absolute '1337.txt') end), }, group 'is_relative' { test('joined paths are relative', function () local paths = {'one', 'two', 'test'} assert.is_truthy(path.is_relative(path.join(paths))) end), test('pure filename is relative', function () assert.is_truthy(path.is_relative '1337.txt') end), test('network path is not relative', function () assert.is_falsy(path.is_relative '//foo/bar') end), }, group 'splitting/joining' { test('split_path is inverse of join', function () local paths = {'one', 'two', 'test'} assert.are_same(path.split(path.join(paths)), paths) end), }, group 'split_extensions' { test('filename', function () assert.are_equal(path.split_extension 'image.jpg', 'image') end), test('extension', function () assert.is_truthy(select(2, path.split_extension 'thesis.tex'), '.tex') assert.are_equal(select(2, path.split_extension 'fstab'), '') end), test('concat gives inverts split', function () local filenames = {'/etc/passwd', '34a90-1.bat', 'backup.tar.gz'} for _, filename in ipairs(filenames) do local base, ext = path.split_extension(filename) assert.are_equal(base .. ext, filename) end end), }, group 'normalize' { test('removes leading `./`', function () assert.are_equal(path.normalize('./a.md'), 'a.md') end), test('dedupe path separators', function () assert.are_equal(path.normalize('a//b'), path.join{'a', 'b'}) end) }, group 'relative or absolute' { test('xor', function () local test_paths = { path.join{ 'hello', 'rudi'}, path.join{ '.', 'autoexec.bat'}, path.join{ 'C:', 'config.sys'}, path.join{ '/', 'etc', 'passwd'} } for _, fp in ipairs(test_paths) do assert.is_truthy(path.is_relative(fp) == not path.is_absolute(fp)) end end) }, group 'strings as path objects' { test('setup', path.treat_strings_as_paths), test('split extension', function () local img = 'mandrill.jpg' assert.are_equal(img:split_extension(), 'mandrill') end), test('conbine paths with `/`', function () assert.are_equal('a' / 'b', path.join{'a', 'b'}) end), test('add extension with `+`', function () assert.are_equal('a' + 'b', path.join{'a.b'}) end), }, group 'make_relative' { group 'safe' { test('just the filename if file is within path', function() assert.are_equal( path.make_relative('/foo/bar/file.txt', '/foo/bar'), 'file.txt' ) end), test('no change if name outside of reference dir', function() assert.are_equal( path.make_relative('/foo/baz/file.txt', '/foo/bar'), '/foo/baz/file.txt' ) end), test('return dot if both paths are the same', function() assert.are_equal( path.make_relative('/one/two/three', '/one/two/three/'), '.' ) end), }, group 'unsafe' { test('just the filename if file is within path', function() assert.are_equal( path.make_relative('/foo/bar/file.txt', '/foo/bar', true), 'file.txt' ) end), test('use `..` to reach parent directory', function() assert.are_equal( path.make_relative('/foo/baz/file.txt', '/foo/bar', true), path.join{'..', 'baz', 'file.txt'} ) end), test('no change if base differs', function() if path.separator == '\\' then -- we're on windows assert.are_equal( path.make_relative('c:/foo/baz/file.txt', 'd:/foo/bar', true), 'c:/foo/baz/file.txt' ) else assert.are_equal( path.make_relative('foo/baz/file.txt', '/foo/bar', true), 'foo/baz/file.txt' ) end end), test('long base path ', function() assert.are_equal( path.make_relative('a/d.png', 'a/b/c', true), path.join{'..', '..', 'd.png'} ) end), test('return dot if both paths are the same', function() assert.are_equal( path.make_relative('/one/two/three', '/one/two/three/', true), '.' ) end) } }, }