@@ -327,6 +327,97 @@ def test_basename_empty_suffix(self):
327327 self .assertEqual (str (self ._eval_expr ('basename("file.txt","")' )), '"file.txt"' )
328328 self .assertEqual (str (self ._eval_expr ('basename("/path/to/file.txt",sfx)' , env = env )), '"file.txt"' )
329329
330+ def test_join_paths (self ):
331+ class LocalStdLib (WDL .StdLib .Base ):
332+ def _join_paths_default_directory (self ) -> str :
333+ return "/work"
334+
335+ def eval_join (expr : str , env = None , type_env = None ):
336+ env = env or WDL .Env .Bindings ()
337+ if type_env is None :
338+ type_env = WDL .Env .Bindings ()
339+ for binding in env :
340+ type_env = type_env .bind (binding .name , binding .value .type )
341+ stdlib = LocalStdLib ("1.2" )
342+ ex = WDL .parse_expr (expr , version = "1.2" ).infer_type (type_env , stdlib )
343+ self .assertEqual (str (ex .type ), "String" )
344+ return ex .eval (env , stdlib )
345+
346+ self .assertEqual (str (eval_join ('join_paths("/usr", "bin")' )), '"/usr/bin"' )
347+ self .assertEqual (str (eval_join ('join_paths("/usr", ["bin", "echo"])' )), '"/usr/bin/echo"' )
348+ self .assertEqual (str (eval_join ('join_paths(["/usr", "bin", "echo"])' )), '"/usr/bin/echo"' )
349+ self .assertEqual (str (eval_join ('join_paths("usr", "bin")' )), '"/work/usr/bin"' )
350+ self .assertEqual (str (eval_join ('join_paths(["usr", "bin", "echo"])' )), '"/work/usr/bin/echo"' )
351+ self .assertEqual (str (eval_join ('join_paths("/usr/", "./bin/../bin")' )), '"/usr/bin"' )
352+
353+ env = WDL .Env .Bindings ().bind ("paths" , WDL .Value .Array (WDL .Type .String (), []))
354+ type_env = WDL .Env .Bindings ().bind (
355+ "paths" , WDL .Type .Array (WDL .Type .String (), nonempty = True )
356+ )
357+ with self .assertRaises (WDL .Error .EmptyArray ):
358+ eval_join ("join_paths(paths)" , env = env , type_env = type_env )
359+
360+ for expr in (
361+ 'join_paths("/usr", "/bin")' ,
362+ 'join_paths("/usr", ["bin", "/echo"])' ,
363+ 'join_paths(["/usr", "/bin"])' ,
364+ ):
365+ with self .subTest (expr = expr ):
366+ with self .assertRaisesRegex (
367+ WDL .Error .EvalError , "only the first path may be absolute"
368+ ):
369+ eval_join (expr )
370+
371+ def test_join_paths_typecheck (self ):
372+ with self .assertRaises (WDL .Error .NoSuchFunction ):
373+ self ._infer_expr_type ('join_paths("/usr", "bin")' , version = "1.1" )
374+ with self .assertRaises (WDL .Error .WrongArity ):
375+ self ._infer_expr_type ("join_paths()" , version = "1.2" )
376+ with self .assertRaises (WDL .Error .WrongArity ):
377+ self ._infer_expr_type ('join_paths("/usr", "bin", "echo")' , version = "1.2" )
378+ with self .assertRaises (WDL .Error .StaticTypeMismatch ):
379+ self ._infer_expr_type ("join_paths(1)" , version = "1.2" )
380+ with self .assertRaisesRegex (
381+ WDL .Error .StaticTypeMismatch , "expects String or Array\\ [String\\ ]\\ +"
382+ ):
383+ self ._infer_expr_type ('join_paths("/usr", {"x": 1})' , version = "1.2" )
384+ with self .assertRaises (WDL .Error .StaticTypeMismatch ):
385+ self ._infer_expr_type ("join_paths(None)" , version = "1.2" )
386+ optional_dir = WDL .Env .Bindings ().bind ("d" , WDL .Type .Directory (optional = True ))
387+ with self .assertRaises (WDL .Error .StaticTypeMismatch ):
388+ self ._infer_expr_type ('join_paths(d, "x")' , type_env = optional_dir , version = "1.2" )
389+
390+ def test_join_paths_runtime_context_required (self ):
391+ stdlib = WDL .StdLib .Base ("1.2" )
392+ expr = WDL .parse_expr ('join_paths(["relative", "path"])' , version = "1.2" ).infer_type (
393+ WDL .Env .Bindings (), stdlib
394+ )
395+ with self .assertRaisesRegex (
396+ WDL .Error .EvalError , "relative path resolution requires runtime context"
397+ ):
398+ expr .eval (WDL .Env .Bindings (), stdlib )
399+
400+ doc = WDL .parse_document (
401+ R"""
402+ version 1.2
403+ workflow w {
404+ output {
405+ String path = join_paths(["relative", "path"])
406+ }
407+ }
408+ """
409+ )
410+ doc .typecheck ()
411+ cfg = WDL .runtime .config .Loader (logging .getLogger (self .id ()), [])
412+ state = WDL .runtime .workflow .StateMachine (
413+ self .id (), self ._dir , doc .workflow , WDL .Env .Bindings ()
414+ )
415+ stdlib = WDL .runtime .workflow ._StdLib ("1.2" , cfg , state , None )
416+ with self .assertRaisesRegex (
417+ NotImplementedError , "relative path resolution requires WDL source"
418+ ):
419+ stdlib ._join_paths_default_directory ()
420+
330421 def test_parse_tsv_row_type (self ):
331422 rows = WDL .StdLib ._parse_tsv ("alpha\t beta\n " )
332423 self .assertEqual (rows .json , [["alpha" , "beta" ]])
0 commit comments