diff --git a/contrib/cf-doc/example_cf-doc.cf b/contrib/cf-doc/example_cf-doc.cf index 8930365b44..ec33512045 100755 --- a/contrib/cf-doc/example_cf-doc.cf +++ b/contrib/cf-doc/example_cf-doc.cf @@ -1,17 +1,19 @@ #!/var/cfengine/bin/cf-agent -f- bundle agent example_cf_doc { - meta: - "inventory" string => "Example: This bundle does Inventory"; - "config" string => "Example: This bundle configures /tmp/example.txt"; + meta: + "inventory" string => "Example: This bundle does Inventory"; + "config" string => "Example: This bundle configures /tmp/example.txt"; - vars: - "h" - string => "$(sys.fqhost)", - meta => { "docinv=Fully Qualified Hostname according to CFEngine" }; + vars: + "h" + string => "$(sys.fqhost)", + meta => { "docinv=Fully Qualified Hostname according to CFEngine" }; - files: - "/tmp/example.txt" - create => 'true', - meta => { "docconfig=Make sure the important file /tmp/example.txt exists" }; + files: + "/tmp/example.txt" + create => 'true', + meta => { + "docconfig=Make sure the important file /tmp/example.txt exists" + }; } diff --git a/contrib/cf-profile/tests/lib/namespace/ns1.cf b/contrib/cf-profile/tests/lib/namespace/ns1.cf index df33116ab5..7c5fb0a5ef 100644 --- a/contrib/cf-profile/tests/lib/namespace/ns1.cf +++ b/contrib/cf-profile/tests/lib/namespace/ns1.cf @@ -1,14 +1,17 @@ -body file control { - namespace => "ns1"; - inputs => { "$(ns1:d.dir)/ns2.cf" }; +body file control +{ + namespace => "ns1"; + inputs => { "$(ns1:d.dir)/ns2.cf" }; } -bundle common d { - vars: - "dir" string => "$(this.promise_dirname)"; +bundle common d +{ + vars: + "dir" string => "$(this.promise_dirname)"; } -bundle agent ns1 { - methods: - "c" usebundle => ns2:ns2; +bundle agent ns1 +{ + methods: + "c" usebundle => ns2:ns2; } diff --git a/contrib/cf-profile/tests/lib/namespace/ns2.cf b/contrib/cf-profile/tests/lib/namespace/ns2.cf index 8a24a92697..120eb42e6e 100644 --- a/contrib/cf-profile/tests/lib/namespace/ns2.cf +++ b/contrib/cf-profile/tests/lib/namespace/ns2.cf @@ -1,7 +1,10 @@ -body file control { - namespace => "ns2"; +body file control +{ + namespace => "ns2"; } -bundle agent ns2 { - commands: - "/bin/sleep 2"; + +bundle agent ns2 +{ + commands: + "/bin/sleep 2"; } diff --git a/contrib/cf-profile/tests/namespace.cf b/contrib/cf-profile/tests/namespace.cf index 7bc45323c0..97e3c6ebea 100644 --- a/contrib/cf-profile/tests/namespace.cf +++ b/contrib/cf-profile/tests/namespace.cf @@ -1,20 +1,24 @@ -body common control { - bundlesequence => { "main" }; - inputs => { "$(d.dir)/lib/namespace/ns1.cf" }; +body common control +{ + bundlesequence => { "main" }; + inputs => { "$(d.dir)/lib/namespace/ns1.cf" }; } -bundle common d { - vars: - "dir" string => "$(this.promise_dirname)"; +bundle common d +{ + vars: + "dir" string => "$(this.promise_dirname)"; } -bundle agent main { - methods: - "a" usebundle => ns1:ns1; - "b" usebundle => ns1; +bundle agent main +{ + methods: + "a" usebundle => ns1:ns1; + "b" usebundle => ns1; } -bundle agent ns1 { - commands: - "/bin/sleep 3"; +bundle agent ns1 +{ + commands: + "/bin/sleep 3"; } diff --git a/contrib/cf-profile/tests/promises.cf b/contrib/cf-profile/tests/promises.cf index eb8b424807..f9d7588402 100644 --- a/contrib/cf-profile/tests/promises.cf +++ b/contrib/cf-profile/tests/promises.cf @@ -1,33 +1,41 @@ -body common control { - bundlesequence => { "main1", "main2" }; +body common control +{ + bundlesequence => { "main1", "main2" }; } -bundle agent main1 { +bundle agent main1 +{ + commands: + "/bin/sleep 1"; - commands: - "/bin/sleep 1"; - - methods: - "a" usebundle => a("arg1 somevalue", "arg2 somevalue", "arg3 somevalue"); - "b" usebundle => b("arg4 somevalue", "arg5 somevalue", "arg6 somevalue"); + methods: + "a" usebundle => a("arg1 somevalue", "arg2 somevalue", "arg3 somevalue"); + "b" usebundle => b("arg4 somevalue", "arg5 somevalue", "arg6 somevalue"); } -bundle agent a (a1, a2, a3) { - commands: - "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; +bundle agent a(a1, a2, a3) +{ + commands: + "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; } -bundle agent b (a1, a2, a3) { - methods: - "c" usebundle => c("arg7 somevalue", "arg8 somevalue", "arg9 somevalue"); - commands: - "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; + +bundle agent b(a1, a2, a3) +{ + methods: + "c" usebundle => c("arg7 somevalue", "arg8 somevalue", "arg9 somevalue"); + + commands: + "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; } -bundle agent c (a1, a2, a3) { - commands: - "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; + +bundle agent c(a1, a2, a3) +{ + commands: + "/bin/echo $(this.bundle):$(a1),$(a2),$(a3)"; } -bundle agent main2 { - commands: - "/bin/sleep 2"; +bundle agent main2 +{ + commands: + "/bin/sleep 2"; } diff --git a/contrib/masterfiles/git-failsafe.cf b/contrib/masterfiles/git-failsafe.cf index a03600493f..5eb48a5459 100644 --- a/contrib/masterfiles/git-failsafe.cf +++ b/contrib/masterfiles/git-failsafe.cf @@ -3,79 +3,88 @@ # failsafe.cf - Basic Failsafe Policy for Community # ############################################################################### - body common control { - bundlesequence => { "git_update" }; - version => "git-failsafe.cf 1.0.0"; + bundlesequence => { "git_update" }; + version => "git-failsafe.cf 1.0.0"; } ############################################################################# - body agent control { - ifelapsed => "1"; - skipidentify => "true"; + ifelapsed => "1"; + skipidentify => "true"; } ############################################################################# - bundle agent git_update { - vars: - - "inputs_dir" string => translatepath("$(sys.workdir)/inputs"), - comment => "Directory containing Cfengine policies", - handle => "update_vars_inputs_dir"; - - "ppkeys_file" string => translatepath("$(sys.workdir)/ppkeys/localhost.pub"), - comment => "Path to public key file", - handle => "update_vars_ppkeys_file"; - - "file_check" string => translatepath("$(inputs_dir)/promises.cf"), - comment => "Path to a policy file", - handle => "update_vars_file_check"; - - "master_location" string => "/var/cfengine/masterfiles", - comment => "The master cfengine policy directory on the policy host", - handle => "update_vars_master_location"; - - "git_origin" string => "http://myserver.com/myrepo.git", + vars: + "inputs_dir" + string => translatepath("$(sys.workdir)/inputs"), + comment => "Directory containing Cfengine policies", + handle => "update_vars_inputs_dir"; + + "ppkeys_file" + string => translatepath("$(sys.workdir)/ppkeys/localhost.pub"), + comment => "Path to public key file", + handle => "update_vars_ppkeys_file"; + + "file_check" + string => translatepath("$(inputs_dir)/promises.cf"), + comment => "Path to a policy file", + handle => "update_vars_file_check"; + + "master_location" + string => "/var/cfengine/masterfiles", + comment => "The master cfengine policy directory on the policy host", + handle => "update_vars_master_location"; + + "git_origin" + string => "http://myserver.com/myrepo.git", comment => "The URL to the Git repository.", handle => "update_vars_git_origin"; - "git_checkout_location" string => "$(master_location)/myrepo", + "git_checkout_location" + string => "$(master_location)/myrepo", comment => "The desired checkout location of the Git repository", handle => "update_vars_git_checkout_location"; - "git_checkout_umask" string => "077", + "git_checkout_umask" + string => "077", comment => "The desired umask while checking out the Git repository", handle => "update_vars_git_checkout_umask"; - "git_configfile" string => "$(git_checkout_location)/.git/config", + "git_configfile" + string => "$(git_checkout_location)/.git/config", comment => "The .git/config file location", handle => "update_vars_git_configfile"; - "git_branch" string => "master", + "git_branch" + string => "master", comment => "The desired branch of the Git repository", handle => "update_vars_git_branch"; - "git_binary" string => "/usr/bin/git", + "git_binary" + string => "/usr/bin/git", comment => "The location of the Git executable. Leave blank if you don't want to use Git.", handle => "update_vars_git_binary"; - # 1. Remove untracked files, 2. Discard changes to index and working tree since HEAD - # 3. Check out the proper branch; 4. Pull the latest changes from origin; - # 5. Reset the working tree to the latest updates - "git_update_command" string => "$(git_binary) clean -f -f -x -q -d .; $(git_binary) reset -q --hard HEAD; $(git_binary) checkout -q $(git_branch); $(git_binary) pull -q -s recursive -Xtheirs origin $(git_branch); $(git_binary) reset -q --hard HEAD", + # 1. Remove untracked files, 2. Discard changes to index and working tree since HEAD + # 3. Check out the proper branch; 4. Pull the latest changes from origin; + # 5. Reset the working tree to the latest updates + "git_update_command" + string => "$(git_binary) clean -f -f -x -q -d .; $(git_binary) reset -q --hard HEAD; $(git_binary) checkout -q $(git_branch); $(git_binary) pull -q -s recursive -Xtheirs origin $(git_branch); $(git_binary) reset -q --hard HEAD", comment => "The commands to do a Git update. They should DTRT for your environment (checkout/reset/clean/etc).", handle => "update_vars_git_update_command"; - "git_clone_command" string => "$(git_binary) clone -b $(git_branch) $(git_origin) $(git_checkout_location)", + "git_clone_command" + string => "$(git_binary) clone -b $(git_branch) $(git_origin) $(git_checkout_location)", comment => "The commands to do a Git update. They should DTRT for your environment (checkout/reset/clean/etc).", handle => "update_vars_git_clone_command"; - "git_config_template" string => " + "git_config_template" + string => " [core] repositoryformatversion = 0 filemode = true @@ -94,184 +103,176 @@ bundle agent git_update comment => "The $(git_configfile) template.", handle => "update_vars_git_config_template"; -# - - classes: - - # Set this to "any" or a machine class to have those machines do - # the Git checkouts locally, effectively replacing the policy - # hub model for a "just checkout from Git everywhere" model. + classes: + # Set this to "any" or a machine class to have those machines do + # the Git checkouts locally, effectively replacing the policy + # hub model for a "just checkout from Git everywhere" model. + # This is not recommended unless you are testing in isolation or + # have other very specific requirements. + "idempotent_git" expression => "!any"; - # This is not recommended unless you are testing in isolation or - # have other very specific requirements. - "idempotent_git" expression => "!any"; - - "use_git" expression => fileexists("$(git_binary)"), + "use_git" + expression => fileexists("$(git_binary)"), comment => "True if we found the Git binary and should use Git.", handle => "update_classes_use_git"; - "git_checkout_location_exists" expression => fileexists("$(git_checkout_location)"), + "git_checkout_location_exists" + expression => fileexists("$(git_checkout_location)"), comment => "True if the Git checkout location exists.", handle => "update_classes_git_checkout_location_exists"; - "git_checkout_exists" expression => fileexists("$(git_checkout_location)/.git"), + "git_checkout_exists" + expression => fileexists("$(git_checkout_location)/.git"), comment => "True if the Git checkout has already happened.", handle => "update_classes_git_checkout_exists"; - - "have_ppkeys" expression => fileexists("$(ppkeys_file)"), - comment => "Check for /var/cfengine/ppkeys/localhost.pub", - handle => "update_classes_have_ppkeys"; - - "files_ok" expression => fileexists("$(file_check)"), - comment => "Check for /var/cfengine/masterfiles/promises.cf", - handle => "update_classes_files_ok"; - -# - - processes: - - files_ok:: - - "cf-serverd" restart_class => "start_server", - comment => "Monitor cf-serverd process", - handle => "update_processes_cf_serverd"; - - "cf-monitord" restart_class => "start_monitor", - comment => "Monitor cf-monitord process", - handle => "update_processes_cf_monitord"; - - files_ok.!windows:: - - "cf-execd" restart_class => "start_exec", - comment => "Monitor cf-execd process", - handle => "update_processes_cf_execd"; - -# - - commands: - - start_server:: - - "$(sys.cf_serverd)" - comment => "Start cf-serverd process", - handle => "update_commands_start_cf_serverd"; - - start_monitor:: - - "$(sys.cf_monitord)" - comment => "Start cf-monitord process", - handle => "update_commands_start_cf_monitord"; - - !windows.start_exec:: - - "$(sys.cf_execd)" - comment => "Start cf-execd process", - handle => "update_commands_start_cf_execd_not_windows"; - - !have_ppkeys:: - - "$(sys.cf_key)", - comment => "Generate cfengine encryption keys if necessary", - handle => "update_commands_generate_keys"; + "have_ppkeys" + expression => fileexists("$(ppkeys_file)"), + comment => "Check for /var/cfengine/ppkeys/localhost.pub", + handle => "update_classes_have_ppkeys"; + + "files_ok" + expression => fileexists("$(file_check)"), + comment => "Check for /var/cfengine/masterfiles/promises.cf", + handle => "update_classes_files_ok"; + + processes: + files_ok:: + "cf-serverd" + restart_class => "start_server", + comment => "Monitor cf-serverd process", + handle => "update_processes_cf_serverd"; + + "cf-monitord" + restart_class => "start_monitor", + comment => "Monitor cf-monitord process", + handle => "update_processes_cf_monitord"; + + files_ok.!windows:: + "cf-execd" + restart_class => "start_exec", + comment => "Monitor cf-execd process", + handle => "update_processes_cf_execd"; + + commands: + start_server:: + "$(sys.cf_serverd)" + comment => "Start cf-serverd process", + handle => "update_commands_start_cf_serverd"; + + start_monitor:: + "$(sys.cf_monitord)" + comment => "Start cf-monitord process", + handle => "update_commands_start_cf_monitord"; + + !windows.start_exec:: + "$(sys.cf_execd)" + comment => "Start cf-execd process", + handle => "update_commands_start_cf_execd_not_windows"; + + !have_ppkeys:: + "$(sys.cf_key)", + comment => "Generate cfengine encryption keys if necessary", + handle => "update_commands_generate_keys"; (idempotent_git||am_policy_hub).use_git.git_config_ok:: "$(git_update_command)" - contain => u_in_dir_umask_shell("$(git_checkout_location)", "$(git_checkout_umask)"), - comment => "Update the Git repository.", - handle => "update_commands_git_update", - classes => u_if_repaired("git_resolve_updated"); + contain => u_in_dir_umask_shell( + "$(git_checkout_location)", "$(git_checkout_umask)" + ), + comment => "Update the Git repository.", + handle => "update_commands_git_update", + classes => u_if_repaired("git_resolve_updated"); (idempotent_git||am_policy_hub).use_git.!git_checkout_location_exists.!git_checkout_exists:: "$(git_clone_command)" - contain => u_in_dir_umask_shell("/", "$(git_checkout_umask)"), - comment => "Clone the Git repository.", - handle => "update_commands_git_clone", - classes => u_if_repaired("git_resolve_cloned"); - -# - - files: - - !(idempotent_git||am_policy_hub):: # policy hub should not alter inputs/ uneccessary - - "$(inputs_dir)/cf_promises_validated" + contain => u_in_dir_umask_shell("/", "$(git_checkout_umask)"), + comment => "Clone the Git repository.", + handle => "update_commands_git_clone", + classes => u_if_repaired("git_resolve_cloned"); + + files: + !(idempotent_git||am_policy_hub):: + # policy hub should not alter inputs/ uneccessary + "$(inputs_dir)/cf_promises_validated" comment => "Check whether a validation stamp is available for a new policy update to reduce the distributed load", - handle => "check_valid_update", - copy_from => u_rcp("$(master_location)/cf_promises_validated","$(sys.policy_hub)"), - action => u_immediate, + handle => "check_valid_update", + copy_from => u_rcp( + "$(master_location)/cf_promises_validated", "$(sys.policy_hub)" + ), + action => u_immediate, classes => u_if_repaired("validated_updates_ready"); - # on the policy hub, converge the .git/config file iff the checkout has been made already + # on the policy hub, converge the .git/config file iff the checkout has been made already (idempotent_git||am_policy_hub).use_git.git_checkout_location_exists.git_checkout_exists:: "$(git_configfile)" - handle => "converge_git_configfile", - classes => u_if_ok("git_config_ok"), - perms => u_m("600"), - edit_defaults => u_empty, - edit_line => u_insert_lines("$(git_config_template)"); - - # on the policy hub, do the following if a) we don't use Git - # or b) the Git update succeeded - !idempotent_git.(am_policy_hub.((git_resolve_cloned||git_resolve_updated)|!use_git))|validated_updates_ready:: # policy hub should always put masterfiles in inputs in order to check new policy - - "$(inputs_dir)" - comment => "Copy policy updates from master source on policy server if a new validation was acquired", - handle => "update_files_inputs_dir", - copy_from => u_rcp("$(master_location)","$(sys.policy_hub)"), - depth_search => u_recurse("inf"), - file_select => u_input_files, + handle => "converge_git_configfile", + classes => u_if_ok("git_config_ok"), + perms => u_m("600"), + edit_defaults => u_empty, + edit_line => u_insert_lines("$(git_config_template)"); + + # on the policy hub, do the following if a) we don't use Git + # or b) the Git update succeeded + !idempotent_git.(am_policy_hub.((git_resolve_cloned||git_resolve_updated)|!use_git))|validated_updates_ready:: + # policy hub should always put masterfiles in inputs in order to check new policy + "$(inputs_dir)" + comment => "Copy policy updates from master source on policy server if a new validation was acquired", + handle => "update_files_inputs_dir", + copy_from => u_rcp("$(master_location)", "$(sys.policy_hub)"), + depth_search => u_recurse("inf"), + file_select => u_input_files, depends_on => { "check_valid_update" }, - action => u_immediate, - classes => u_if_repaired("update_report"); - - !windows:: - - "$(sys.workdir)/bin" - comment => "Make sure cfengine binaries have right file permissions", - handle => "update_files_sys_workdir_bin", - perms => u_m("755"), - depth_search => u_recurse_basedir("inf"), - action => u_immediate; - - "$(sys.workdir)/lib" - comment => "Make sure cfengine libraries have right file permissions", - handle => "update_files_sys_workdir_lib", - perms => u_m("644"), - depth_search => u_recurse_basedir("inf"), - action => u_immediate; - - "$(sys.workdir)/lib" - comment => "Make sure cfengine libraries have right file permissions for only HP-UX", - handle => "update_files_sys_workdir_lib_hpux", - perms => u_m("755"), - depth_search => u_recurse_basedir("inf"), - action => u_immediate, - if => "hpux"; - - "/usr/local/bin" - comment => "Ensure cfengine binaries were copied to /usr/local/bin", - handle => "update_files_usr_local_bin", - perms => u_m("755"), - copy_from => u_cp_nobck("$(sys.workdir)/bin"), - file_select => u_cf3_files, - depth_search => u_recurse("1"), - action => u_immediate; - - am_policy_hub:: - - "$(master_location)/." - comment => "Make sure masterfiles folder has right file permissions", - handle => "update_files_sys_workdir_masterfiles", - perms => u_m("644"), - depth_search => u_recurse_basedir("inf"), - action => u_immediate; + action => u_immediate, + classes => u_if_repaired("update_report"); + + !windows:: + "$(sys.workdir)/bin" + comment => "Make sure cfengine binaries have right file permissions", + handle => "update_files_sys_workdir_bin", + perms => u_m("755"), + depth_search => u_recurse_basedir("inf"), + action => u_immediate; + + "$(sys.workdir)/lib" + comment => "Make sure cfengine libraries have right file permissions", + handle => "update_files_sys_workdir_lib", + perms => u_m("644"), + depth_search => u_recurse_basedir("inf"), + action => u_immediate; + + "$(sys.workdir)/lib" + comment => "Make sure cfengine libraries have right file permissions for only HP-UX", + handle => "update_files_sys_workdir_lib_hpux", + perms => u_m("755"), + depth_search => u_recurse_basedir("inf"), + action => u_immediate, + if => "hpux"; + + "/usr/local/bin" + comment => "Ensure cfengine binaries were copied to /usr/local/bin", + handle => "update_files_usr_local_bin", + perms => u_m("755"), + copy_from => u_cp_nobck("$(sys.workdir)/bin"), + file_select => u_cf3_files, + depth_search => u_recurse("1"), + action => u_immediate; + + am_policy_hub:: + "$(master_location)/." + comment => "Make sure masterfiles folder has right file permissions", + handle => "update_files_sys_workdir_masterfiles", + perms => u_m("644"), + depth_search => u_recurse_basedir("inf"), + action => u_immediate; reports: git_checkout_location_exists.!git_checkout_exists:: "$(git_checkout_location) already exists but it's not a Git checkout location. Aborting."; + git_resolve_updated:: "Updated $(git_origin) (branch $(git_branch), umask $(git_checkout_umask)) into $(git_checkout_location)"; + git_resolve_cloned:: "Cloned $(git_origin) (branch $(git_branch), umask $(git_checkout_umask)) into $(git_checkout_location)"; } @@ -279,112 +280,97 @@ bundle agent git_update ######################################################### # Self-contained bodies from the lib to avoid dependencies ######################################################### - body perms u_m(p) { - mode => "$(p)"; + mode => "$(p)"; } ######################################################### - body file_select u_cf3_files { - leaf_name => { "cf-.*" }; - file_result => "leaf_name"; + leaf_name => { "cf-.*" }; + file_result => "leaf_name"; } ######################################################### - body file_select u_input_files { - leaf_name => { ".*.cf",".*.dat",".*.txt" }; - file_result => "leaf_name"; + leaf_name => { ".*.cf", ".*.dat", ".*.txt" }; + file_result => "leaf_name"; } ######################################################### - -body copy_from u_rcp(from,server) +body copy_from u_rcp(from, server) { - source => "$(from)"; - compare => "digest"; - trustkey => "false"; - -!am_policy_hub:: + source => "$(from)"; + compare => "digest"; + trustkey => "false"; - servers => { "$(server)" }; + !am_policy_hub:: + servers => { "$(server)" }; } ######################################################### - body copy_from u_cp_nobck(from) { - source => "$(from)"; - compare => "digest"; - copy_backup => "false"; + source => "$(from)"; + compare => "digest"; + copy_backup => "false"; } ######################################################### - body action u_immediate { - ifelapsed => "0"; + ifelapsed => "0"; } ######################################################### - body depth_search u_recurse(d) { - depth => "$(d)"; - exclude_dirs => { "\.svn", "\.git", "\.hg", "\.bzr" }; + depth => "$(d)"; + exclude_dirs => { "\.svn", "\.git", "\.hg", "\.bzr" }; } ######################################################### - body depth_search u_recurse_basedir(d) { - include_basedir => "true"; - depth => "$(d)"; - exclude_dirs => { "\.svn", "\.git", "\.hg", "\.bzr" }; + include_basedir => "true"; + depth => "$(d)"; + exclude_dirs => { "\.svn", "\.git", "\.hg", "\.bzr" }; } ######################################################### - body classes u_if_repaired(x) { - promise_repaired => { "$(x)" }; + promise_repaired => { "$(x)" }; } body classes u_if_ok(x) { - promise_repaired => { "$(x)" }; - promise_kept => { "$(x)" }; + promise_repaired => { "$(x)" }; + promise_kept => { "$(x)" }; } ######################################################### - body contain u_in_dir_umask_shell(dir, umask) { - chdir => "$(dir)"; - useshell => "true"; - umask => "$(umask)"; + chdir => "$(dir)"; + useshell => "true"; + umask => "$(umask)"; } ######################################################### - bundle edit_line u_insert_lines(lines) { insert_lines: - - "$(lines)" - comment => "Append lines if they don't exist"; + "$(lines)" comment => "Append lines if they don't exist"; } ######################################################### - body edit_defaults u_empty { - empty_file_before_editing => "true"; - edit_backup => "false"; + empty_file_before_editing => "true"; + edit_backup => "false"; } ######################################################### diff --git a/libpromises/failsafe.cf b/libpromises/failsafe.cf index 946e53a049..4a982176ce 100644 --- a/libpromises/failsafe.cf +++ b/libpromises/failsafe.cf @@ -1,4 +1,3 @@ -# # Copyright 2021 Northern.tech AS # # This file is part of CFEngine 3 - written and maintained by Northern.tech AS. @@ -20,7 +19,6 @@ # versions of CFEngine, the applicable Commercial Open Source License # (COSL) may apply to this file if you as a licensee so wish it. See # included file COSL.txt. - ########## CFEngine Bootstrap / Failsafe Policy ############################## # This file (failsafe.cf) is re-generated inside "inputs" directory every time # you bootstrap. This means that custom changes will be overwritten. @@ -29,74 +27,74 @@ # the policy hub for the first time when bootstrapping, and to recover the # system by fetching policies in case the standard agent run fails. ############################################################################## - body agent control { - # Bootstrapping can't continue without keys - abortclasses => { "no_ppkeys_ABORT_kept" }; - # Make sure that running failsafe many times in a row does not - # change functionality - ifelapsed => "0"; + # Bootstrapping can't continue without keys + abortclasses => { "no_ppkeys_ABORT_kept" }; + + # Make sure that running failsafe many times in a row does not + # change functionality + ifelapsed => "0"; } ################################################################################ - bundle agent main { meta: - - "description" - string => "Perform bootstrap or failsafe recovery operations."; + "description" + string => "Perform bootstrap or failsafe recovery operations."; vars: - # In order to preserve the log level used during bootstrap we build the - # string to set log level on any direct sub-agent calls based on classes - # that are defined when the options are set. - - # --log-level, -g value - Specify how detailed logs should be. - # Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug' - - "log_level" - string => ifelse("debug_mode", "--log-level=debug", - "verbose_mode", "--log-level=verbose", - "info_mode", "--log-level=info", - # CFE-4121 - Not yet implemented - # "notice_mode", "--log-level=notice", - # "warning_mode", "--log-level=warning", - # "error_mode", "--log-level=error", - ""); + # In order to preserve the log level used during bootstrap we build the + # string to set log level on any direct sub-agent calls based on classes + # that are defined when the options are set. + # --log-level, -g value - Specify how detailed logs should be. + # Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug' + "log_level" + string => ifelse( + "debug_mode", + "--log-level=debug", + "verbose_mode", + "--log-level=verbose", + "info_mode", + "--log-level=info", + # CFE-4121 - Not yet implemented + # "notice_mode", "--log-level=notice", + # "warning_mode", "--log-level=warning", + # "error_mode", "--log-level=error", + "" + ); methods: - - "Check Keys" - usebundle => failsafe_cfe_internal_checkkeys, - comment => "Without a valid keypair we aren't going to be able + "Check Keys" + usebundle => failsafe_cfe_internal_checkkeys, + comment => "Without a valid keypair we aren't going to be able to establish trust"; - "Fetch Inputs" - usebundle => failsafe_cfe_internal_update, - comment => "We need to fetch policy from upstream if we are + "Fetch Inputs" + usebundle => failsafe_cfe_internal_update, + comment => "We need to fetch policy from upstream if we are bootstrapping or if we are performing failsafe recovery."; - "Actuate Update Policy" - usebundle => failsafe_cfe_internal_call_update, - comment => "In order to speed up convergence and reporting we + "Actuate Update Policy" + usebundle => failsafe_cfe_internal_call_update, + comment => "In order to speed up convergence and reporting we trigger the update policy right after initial bootstrap. This allows the first scheduled run to happen with the most up to date and complete information."; - "Trigger Policy" - usebundle => failsafe_cfe_internal_trigger_policy, - comment => "In order to speed up convergence and reporting we + "Trigger Policy" + usebundle => failsafe_cfe_internal_trigger_policy, + comment => "In order to speed up convergence and reporting we trigger the whole policy right after initial bootstrap. This allows the first report to provide more complete data."; - "Report" - usebundle => failsafe_cfe_internal_report, - comment => "It's important to let the user know what happened + "Report" + usebundle => failsafe_cfe_internal_report, + comment => "It's important to let the user know what happened as the result of the bootstrap or failsafe operation."; } @@ -104,9 +102,9 @@ bundle agent main bundle agent failsafe_cfe_internal_checkkeys { classes: - "have_ppkeys" - expression => fileexists("$(sys.workdir)/ppkeys/localhost.pub"), - handle => "failsafe_cfe_internal_bootstrap_checkkeys_classes_have_ppkeys"; + "have_ppkeys" + expression => fileexists("$(sys.workdir)/ppkeys/localhost.pub"), + handle => "failsafe_cfe_internal_bootstrap_checkkeys_classes_have_ppkeys"; reports: !have_ppkeys:: @@ -115,36 +113,29 @@ bundle agent failsafe_cfe_internal_checkkeys } ################################################################################ - bundle agent failsafe_cfe_internal_update { vars: - - # A policy server cannot use the shortcut feature to resolve - # masterfiles since cf-serverd is potentially not yet up and - # running. - - # The unqualified path is used for non policy servers so that - # the policy server can use a shortcut to decide on behalf of - # the client which policy to serve by default. This is useful - # when running binaires from mixed sources (for example CFEngine - # produced binaries vs packages from the debian repository). - - "masterfiles_dir_remote" - string => ifelse( "policy_server", $(sys.masterdir), - "masterfiles" ); + # A policy server cannot use the shortcut feature to resolve + # masterfiles since cf-serverd is potentially not yet up and + # running. + # The unqualified path is used for non policy servers so that + # the policy server can use a shortcut to decide on behalf of + # the client which policy to serve by default. This is useful + # when running binaires from mixed sources (for example CFEngine + # produced binaries vs packages from the debian repository). + "masterfiles_dir_remote" + string => ifelse("policy_server", $(sys.masterdir), "masterfiles"); files: - - "$(sys.inputdir)" - handle => "failsafe_cfe_internal_bootstrap_update_files_sys_workdir_inputs_shortcut", - copy_from => failsafe_scp("$(masterfiles_dir_remote)"), - depth_search => failsafe_u_infinite_client_policy, - file_select => failsafe_exclude_vcs_files, - classes => failsafe_results("namespace", "inputdir_update"); + "$(sys.inputdir)" + handle => "failsafe_cfe_internal_bootstrap_update_files_sys_workdir_inputs_shortcut", + copy_from => failsafe_scp("$(masterfiles_dir_remote)"), + depth_search => failsafe_u_infinite_client_policy, + file_select => failsafe_exclude_vcs_files, + classes => failsafe_results("namespace", "inputdir_update"); !policy_server:: - "$(sys.workdir)/modules" handle => "failsafe_cfe_internal_bootstrap_update_files_sys_workdir_modules_shortcut", copy_from => failsafe_scp("modules"), @@ -153,7 +144,6 @@ bundle agent failsafe_cfe_internal_update classes => failsafe_results("namespace", "modulesdir_update"); !windows.inputdir_update_error:: - # When running on a *nix platform with homogeneous packages # $(sys.masterdir) is a good guess. This is never the case for # windows, and might be a poor guess if mixing packages from @@ -171,7 +161,6 @@ bundle agent failsafe_cfe_internal_update shortcut."; windows.inputdir_update_error:: - # Note: Windows can't use $(sys.masterdir) because no one runs a # hub on windows and the copy_from needs the remote path. "$(sys.inputdir)" @@ -186,7 +175,6 @@ bundle agent failsafe_cfe_internal_update shortcut."; windows:: - # TODO: Remove the use of bin-twin ref: Redmine #7364 "$(sys.workdir)\\bin-twin\\." handle => "failsafe_cfe_internal_bootstrap_update_files_sys_workdir_bin_twin_windows", @@ -196,45 +184,36 @@ bundle agent failsafe_cfe_internal_update comment => "Make sure we maintain a clone of the binaries and libraries for updating"; - processes: - - # TODO: Decide if this class guard is appropriate. Should we - # guard checking of cf-execd process running to when inputs are - # repaired + # TODO: Decide if this class guard is appropriate. Should we + # guard checking of cf-execd process running to when inputs are + # repaired !windows.inputdir_update_repaired:: - # We need to know when cf-execd is not running so that we can # start it when necessary. Windows and systemd hosts uses the service # manager instead of keying on individual processes. - - "cf-execd" restart_class => "cf_execd_not_running", + "cf-execd" + restart_class => "cf_execd_not_running", handle => "failsafe_cfe_internal_bootstrap_update_processes_start_cf_execd"; any:: - # We need to know if cf-serverd isn't running so that we can # start it when necessary. - - "cf-serverd" restart_class => "cf_serverd_not_running", + "cf-serverd" + restart_class => "cf_serverd_not_running", handle => "failsafe_cfe_internal_bootstrap_update_processes_start_cf_serverd"; commands: - cf_execd_not_running.!(windows|systemd|bootstrap_skip_services):: - # Windows and systemd do not launch cf-execd directly and are # handeled separately. - "$(sys.cf_execd)" handle => "failsafe_cfe_internal_bootstrap_update_commands_check_sys_cf_execd_start", classes => failsafe_results("namespace", "cf_execd_running"); cf_serverd_not_running.!(windows|systemd|bootstrap_skip_services):: - # cf-serverd is not launched directly on Windows and systemd and is # handled separately. - "$(sys.cf_serverd)" handle => "failsafe_cfe_internal_bootstrap_update_commands_check_sys_cf_serverd_start", action => failsafe_ifwin_bg, @@ -246,23 +225,19 @@ bundle agent failsafe_cfe_internal_update started by a separate policy."; cf_execd_not_running.systemd.!bootstrap_skip_services:: - # We explicitly use "restart", because it is possible that cf-serverd # is running, even if cf-execd isn't, for example. Here we want to be # sure we relaunch everything. - "/bin/systemctl restart cfengine3" -> { "CFE-1459" } handle => "failsafe_cfe_internal_bootstrap_update_commands_systemd_cfe_start", contain => bootstrap_command_silent, classes => failsafe_results("namespace", "systemctl_restart_cfengine3"); services: - - # TODO: Is this restriction to only promise the service running - # when inputs are repaired appropriate? Perhaps it should always - # be checked. + # TODO: Is this restriction to only promise the service running + # when inputs are repaired appropriate? Perhaps it should always + # be checked. windows.inputdir_update_repaired.!bootstrap_skip_services:: - "CfengineNovaExec" handle => "failsafe_cfe_internal_bootstrap_update_services_windows_executor", service_policy => "start", @@ -271,35 +246,29 @@ bundle agent failsafe_cfe_internal_update } ################################################################################ - bundle agent failsafe_cfe_internal_report { meta: - - "description" - string => "Report the outcome of the embedded + "description" + string => "Report the outcome of the embedded bootstrap/failsafe operation."; classes: - - # TODO: Determine if this is necessary and/or useful. Pre-eval - # might resolve this before policy update occurs, and this is - # probably most useful after policy update has been attempted. - - "have_promises_cf" - scope => "bundle", - expression => fileexists("$(sys.inputdir)/promises.cf"), - handle => "failsafe_cfe_internal_bootstrap_update_classes_have_promises_cf", - comment => "We expect to find promises.cf after policy has + # TODO: Determine if this is necessary and/or useful. Pre-eval + # might resolve this before policy update occurs, and this is + # probably most useful after policy update has been attempted. + "have_promises_cf" + scope => "bundle", + expression => fileexists("$(sys.inputdir)/promises.cf"), + handle => "failsafe_cfe_internal_bootstrap_update_classes_have_promises_cf", + comment => "We expect to find promises.cf after policy has been successfully copied from the policy server. If promises.cf is missing, then the bootstrap or failsafe recovery has likely failed."; reports: - !bootstrap_mode:: - "Built-in failsafe policy triggered" handle => "failsafe_cfe_internal_bootstrap_update_reports_failsafe_notification", comment => "Be sure to inform the user that the failsafe policy has @@ -308,29 +277,23 @@ bundle agent failsafe_cfe_internal_report configuration in body executor control."; bootstrap_mode:: - "Bootstrapping from host '$(sys.policy_hub)' via built-in policy '$(this.promise_filename)'" handle => "failsafe_cfe_internal_bootstrap_update_reports_bootstrap_notification", comment => "Be sure to inform the user that they have triggerd a bootstrap."; bootstrap_mode.policy_server:: - "This host assumes the role of policy server" - handle => "failsafe_cfe_internal_bootstrap_update_reports_assume_policy_hub"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_assume_policy_hub"; bootstrap_mode.!policy_server:: - "This autonomous node assumes the role of voluntary client" - handle => "failsafe_cfe_internal_bootstrap_update_reports_assume_voluntary_client"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_assume_voluntary_client"; inputdir_update_repaired:: - "Updated local policy from policy server" - handle => "failsafe_cfe_internal_bootstrap_update_reports_inputdir_update_repaired"; - + handle => "failsafe_cfe_internal_bootstrap_update_reports_inputdir_update_repaired"; inputdir_update_repaired.!have_promises_cf:: - # We used to display this report when we have fetched new # policy, but still can not find promises.cf in # sys.inputdir. However if the hub being bootstrapped to is down @@ -338,7 +301,6 @@ bundle agent failsafe_cfe_internal_report # # TODO: Come up with better conditions. These seem weak. # - Potentially use returnszero() with cf-promises? - "Failed to copy policy from policy server at $(sys.policy_hub):$(sys.masterdir) Please check * cf-serverd is running on $(sys.policy_hub) @@ -348,7 +310,7 @@ bundle agent failsafe_cfe_internal_report * masterfiles 'bundle server' -> access: -> masterfiles -> admit/deny It is often useful to restart cf-serverd in verbose mode (cf-serverd -v) on $(sys.policy_hub) to diagnose connection issues. When updating masterfiles, wait (usually 5 minutes) for files to propagate to inputs on $(sys.policy_hub) before retrying." - handle => "failsafe_cfe_internal_bootstrap_update_reports_did_not_get_policy"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_did_not_get_policy"; trigger_policy_repaired:: "Triggered an initial run of the policy" @@ -359,66 +321,56 @@ bundle agent failsafe_cfe_internal_report handle => "failsafe_cfe_internal_bootstrap_trigger_policy_failed"; systemctl_restart_cfengine3_repaired:: - "Restarted systemd unit cfengine3" handle => "failsafe_cfe_intrnal_bootstrap_update_reports_systemd_unit_restarted"; systemctl_restart_cfengine3_error:: - "Error restarting systemd unit cfengine3" handle => "failsafe_cfe_intrnal_bootstrap_update_reports_systemd_unit_restarted"; cf_serverd_running_repaired:: - "Started the server" - handle => "failsafe_cfe_internal_bootstrap_update_reports_started_serverd"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_started_serverd"; cf_serverd_running_failed:: - "Failed to start the server" - handle => "failsafe_cfe_internal_bootstrap_update_reports_failed_to_start_serverd"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_failed_to_start_serverd"; cf_execd_running_repaired:: - "Started the scheduler" - handle => "failsafe_cfe_internal_bootstrap_update_reports_started_execd"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_started_execd"; cf_execd_running_failed:: - "Failed to start the scheduler" - handle => "failsafe_cfe_internal_bootstrap_update_reports_failed_to_start_execd"; + handle => "failsafe_cfe_internal_bootstrap_update_reports_failed_to_start_execd"; } ################################################################################ - bundle agent failsafe_cfe_internal_call_update { vars: - - "mode" string => ifelse("bootstrap_mode", "bootstrap_mode", "failsafe_mode"); + "mode" + string => ifelse("bootstrap_mode", "bootstrap_mode", "failsafe_mode"); commands: - # On Windows we need cf-execd to call update.cf, otherwise the daemons will # not run under the SYSTEM account. !windows.!skip_policy_on_bootstrap:: "$(sys.cf_agent) -f $(sys.update_policy_path) --define $(mode) $(main.log_level)" handle => "failsafe_cfe_internal_call_update_commands_call_update_cf", - if => fileexists( $(sys.update_policy_path) ), + if => fileexists($(sys.update_policy_path)), comment => "We run update.cf in order to prepare system information for collection into CFEngine Enterprise more quickly."; } ################################################################################ - bundle agent failsafe_cfe_internal_trigger_policy { commands: - bootstrap_mode.!skip_policy_on_bootstrap:: "$(sys.cf_agent) --define bootstrap_mode $(main.log_level)" handle => "failsafe_cfe_internal_trigger_policy_commands_call_promises_cf", - if => fileexists( $(sys.default_policy_path) ), + if => fileexists($(sys.default_policy_path)), classes => failsafe_results("namespace", "trigger_policy"), comment => "We run promises.cf in order to prepare system information for collection into CFEngine Enterprise more quickly."; @@ -427,54 +379,62 @@ bundle agent failsafe_cfe_internal_trigger_policy ############################################ body copy_from failsafe_scp(from) { - source => "$(from)"; - compare => "digest"; - # This class is always set when bootstrapping. You can deactivate - # this class with --trust-server=no when bootstrapping - trust_server:: - trustkey => "true"; - !policy_server:: - servers => { "$(sys.policy_hub)" }; - portnumber => "$(sys.policy_hub_port)"; + source => "$(from)"; + compare => "digest"; + + # This class is always set when bootstrapping. You can deactivate + # this class with --trust-server=no when bootstrapping + trust_server:: + trustkey => "true"; + + !policy_server:: + servers => { "$(sys.policy_hub)" }; + portnumber => "$(sys.policy_hub_port)"; } + ############################################ body depth_search failsafe_u_infinite_client_policy # @brief Search recursively for files excluding vcs related files and .no-distrib directories # @param d Maximum depth to search recursively # Duplicated in update policy { - depth => "inf"; - exclude_dirs => { "\.svn", "\.git", "git-core", "\.no-distrib" }; + depth => "inf"; + exclude_dirs => { "\.svn", "\.git", "git-core", "\.no-distrib" }; } + ############################################ body depth_search failsafe_recurse(d) { - depth => "$(d)"; - exclude_dirs => { "\.svn", "\.git" }; + depth => "$(d)"; + exclude_dirs => { "\.svn", "\.git" }; } + ############################################ body file_select failsafe_exclude_vcs_files { - leaf_name => { "\.git.*", "\.mailmap" }; - file_result => "!leaf_name"; + leaf_name => { "\.git.*", "\.mailmap" }; + file_result => "!leaf_name"; } + ############################################ body service_method failsafe_bootstart { - service_autostart_policy => "boot_time"; + service_autostart_policy => "boot_time"; } + ############################################ body action failsafe_ifwin_bg { - windows:: - background => "true"; + windows:: + background => "true"; } + ############################################ body copy_from failsafe_cp(from) { - source => "$(from)"; - compare => "digest"; - copy_backup => "false"; + source => "$(from)"; + compare => "digest"; + copy_backup => "false"; } ############################################ @@ -487,31 +447,30 @@ body classes failsafe_results(scope, class_prefix) # @param class_prefix The prefix for the classes defined { scope => "$(scope)"; - - promise_kept => { "$(class_prefix)_reached", - "$(class_prefix)_kept" }; - - promise_repaired => { "$(class_prefix)_reached", - "$(class_prefix)_repaired" }; - - repair_failed => { "$(class_prefix)_reached", - "$(class_prefix)_error", - "$(class_prefix)_not_kept", - "$(class_prefix)_failed" }; - - repair_denied => { "$(class_prefix)_reached", - "$(class_prefix)_error", - "$(class_prefix)_not_kept", - "$(class_prefix)_denied" }; - - repair_timeout => { "$(class_prefix)_reached", - "$(class_prefix)_error", - "$(class_prefix)_not_kept", - "$(class_prefix)_timeout" }; + promise_kept => { "$(class_prefix)_reached", "$(class_prefix)_kept" }; + promise_repaired => { "$(class_prefix)_reached", "$(class_prefix)_repaired" }; + repair_failed => { + "$(class_prefix)_reached", + "$(class_prefix)_error", + "$(class_prefix)_not_kept", + "$(class_prefix)_failed", + }; + repair_denied => { + "$(class_prefix)_reached", + "$(class_prefix)_error", + "$(class_prefix)_not_kept", + "$(class_prefix)_denied", + }; + repair_timeout => { + "$(class_prefix)_reached", + "$(class_prefix)_error", + "$(class_prefix)_not_kept", + "$(class_prefix)_timeout", + }; } body contain bootstrap_command_silent # @brief Suppress command output { - no_output => "true"; + no_output => "true"; } diff --git a/tests/acceptance/19_security/other_writeable/group_write.cf b/tests/acceptance/19_security/other_writeable/group_write.cf index 73786f129c..95fbc2b014 100644 --- a/tests/acceptance/19_security/other_writeable/group_write.cf +++ b/tests/acceptance/19_security/other_writeable/group_write.cf @@ -3,19 +3,19 @@ # Create a file, check defaults # ####################################################### - body common control { - inputs => { "../../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle common g { vars: - # This extracts the octal mode, and decimal nlink, uid, gid, size - "policy_file" string => ' + # This extracts the octal mode, and decimal nlink, uid, gid, size + "policy_file" + string => ' body common control { @@ -29,11 +29,9 @@ bundle agent test "Scarecrow: Well, some people without brains do an awful lot of talking don\'t they?"; }'; - } ####################################################### - bundle agent init { files: @@ -45,30 +43,31 @@ bundle agent init } ####################################################### - bundle agent test { - vars: - "agent_output" string => execresult("$(sys.cf_agent) -f $(G.testfile)", "noshell"), + "agent_output" + string => execresult("$(sys.cf_agent) -f $(G.testfile)", "noshell"), if => fileexists("$(G.testfile)"); classes: "security_exception" - expression => regcmp(".*is writable by others (security exception).*", "$(agent_output)"), + expression => regcmp( + ".*is writable by others (security exception).*", "$(agent_output)" + ), comment => "It's a security risk to evaluate policy that is writeable by users other than the owner"; } ####################################################### - bundle agent check { classes: - "ok" expression => "!security_exception"; + "ok" expression => "!security_exception"; reports: ok:: "$(this.promise_filename) Pass"; + !ok:: "$(this.promise_filename) FAIL"; } diff --git a/tests/acceptance/19_security/other_writeable/other_write.cf b/tests/acceptance/19_security/other_writeable/other_write.cf index 48ec961be3..24804a3a0f 100644 --- a/tests/acceptance/19_security/other_writeable/other_write.cf +++ b/tests/acceptance/19_security/other_writeable/other_write.cf @@ -3,19 +3,19 @@ # Create a file, check defaults # ####################################################### - body common control { - inputs => { "../../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle common g { vars: - # This extracts the octal mode, and decimal nlink, uid, gid, size - "policy_file" string => ' + # This extracts the octal mode, and decimal nlink, uid, gid, size + "policy_file" + string => ' body common control { @@ -28,11 +28,9 @@ bundle agent test "We\'re not in KS anymore toto."; }'; - } ####################################################### - bundle agent init { files: @@ -44,31 +42,31 @@ bundle agent init } ####################################################### - bundle agent test { - vars: - "agent_output" string => execresult("$(sys.cf_agent) -f $(G.testfile)", "noshell"), + "agent_output" + string => execresult("$(sys.cf_agent) -f $(G.testfile)", "noshell"), if => fileexists("$(G.testfile)"); - classes: "security_exception" - expression => regcmp(".*is writable by others (security exception).*", "$(agent_output)"), + expression => regcmp( + ".*is writable by others (security exception).*", "$(agent_output)" + ), comment => "It's a security risk to evaluate policy that is writeable by users other than the owner"; } ####################################################### - bundle agent check { classes: - "ok" expression => "!security_exception"; + "ok" expression => "!security_exception"; reports: ok:: "$(this.promise_filename) Pass"; + !ok:: "$(this.promise_filename) FAIL"; } diff --git a/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf b/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf index 7835fb6591..48a46fea2e 100644 --- a/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf +++ b/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf @@ -1,41 +1,45 @@ # Tests that the '-p' option works properly in all its modes. - body common control { - inputs => { "../default.sub.cf" }; - bundlesequence => { default($(this.promise_filename)) }; + inputs => { "../default.sub.cf" }; + bundlesequence => { default($(this.promise_filename)) }; } bundle agent init { vars: - "testdir" data => '{ testdir: "$(this.promise_dirname)" }'; + "testdir" data => '{ testdir: "$(this.promise_dirname)" }'; files: - "$(G.testdir)/output.expected" - create => "true", - edit_template => "$(this.promise_filename).template", - template_method => "mustache", - template_data => @(testdir); + "$(G.testdir)/output.expected" + create => "true", + edit_template => "$(this.promise_filename).template", + template_method => "mustache", + template_data => @(testdir); } bundle agent test { meta: - "test_soft_fail" string => "windows", - meta => { "ENT-10254" }; + "test_soft_fail" + string => "windows", + meta => { "ENT-10254" }; + vars: - "arg_list" slist => { "none", "cf", "json", "cf-full", "json-full" }; + "arg_list" slist => { "none", "cf", "json", "cf-full", "json-full" }; commands: - "$(sys.cf_promises) -f $(this.promise_filename).sub -p $(arg_list) >> $(G.testdir)/output.actual" - contain => in_shell; + "$(sys.cf_promises) -f $(this.promise_filename).sub -p $(arg_list) >> $(G.testdir)/output.actual" + contain => in_shell; } bundle agent check { methods: - "check" usebundle => dcs_check_diff("$(G.testdir)/output.actual", - "$(G.testdir)/output.expected", - $(this.promise_filename)); + "check" + usebundle => dcs_check_diff( + "$(G.testdir)/output.actual", + "$(G.testdir)/output.expected", + $(this.promise_filename) + ); } diff --git a/tests/acceptance/selftest/flakey_meta_fail.cf b/tests/acceptance/selftest/flakey_meta_fail.cf index 9f1d6a4b56..163e123fcb 100644 --- a/tests/acceptance/selftest/flakey_meta_fail.cf +++ b/tests/acceptance/selftest/flakey_meta_fail.cf @@ -1,14 +1,15 @@ body common control { - inputs => { "../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle agent test { meta: - "test_flakey_fail" string => "any", + "test_flakey_fail" + string => "any", meta => { "ENT-6947" }; } diff --git a/tests/acceptance/selftest/flakey_meta_no_ticket.cf b/tests/acceptance/selftest/flakey_meta_no_ticket.cf index 084c77419b..12583441c3 100644 --- a/tests/acceptance/selftest/flakey_meta_no_ticket.cf +++ b/tests/acceptance/selftest/flakey_meta_no_ticket.cf @@ -1,8 +1,8 @@ body common control { - inputs => { "../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle agent test diff --git a/tests/acceptance/selftest/skipped.cf b/tests/acceptance/selftest/skipped.cf index a90caa0848..00d9315850 100644 --- a/tests/acceptance/selftest/skipped.cf +++ b/tests/acceptance/selftest/skipped.cf @@ -1,8 +1,8 @@ body common control { - inputs => { "../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle agent test diff --git a/tests/acceptance/selftest/soft_no_ticket.cf b/tests/acceptance/selftest/soft_no_ticket.cf index 6e5ce0f3ea..4e49dc4feb 100644 --- a/tests/acceptance/selftest/soft_no_ticket.cf +++ b/tests/acceptance/selftest/soft_no_ticket.cf @@ -1,8 +1,8 @@ body common control { - inputs => { "../default.sub.cf" }; - bundlesequence => { default("$(this.promise_filename)") }; - version => "1.0"; + inputs => { "../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; } bundle agent test diff --git a/tests/stress/american_psycho.cf b/tests/stress/american_psycho.cf index 7f83abc518..3493b2a25a 100644 --- a/tests/stress/american_psycho.cf +++ b/tests/stress/american_psycho.cf @@ -1,47 +1,36 @@ # It might be interesting to run some of these with action => background to test parralism # might be good to have a test to create large files (over 100M) and very large files (over 1G) and copy_from on those, especially when using compare digest, encrypt and verify - bundle agent stress { - vars: "large_number" string => "1834", comment => "values larger than this generate a segfault in execresult for seq"; methods: - STRESS_DEFINE_CLASSES:: - "define_classes" - usebundle => define_classes; + "define_classes" usebundle => define_classes; STRESS_COMMANDS_ECHO:: - "commands_echo" - usebundle => commands_echo; + "commands_echo" usebundle => commands_echo; STRESS_DEFINE_VARIABLES:: - "define_variables" - usebundle => define_variables; + "define_variables" usebundle => define_variables; STRESS_EXECRESULT_ECHO_NOSHELL:: - "execresult_echo_noshell" - usebundle => execresult_echo_noshell; + "execresult_echo_noshell" usebundle => execresult_echo_noshell; STRESS_EXECRESULT_ECHO_USESHELL:: - "execresult_echo_useshell" - usebundle => execresult_echo_useshell; + "execresult_echo_useshell" usebundle => execresult_echo_useshell; STRESS_DEFINE_PERSISTENT_CLASSES:: - "define_persistent_classes" - usebundle => define_persistent_classes; + "define_persistent_classes" usebundle => define_persistent_classes; STRESS_NESTED_USEBUNDLE:: - "nested_usebundle" - usebundle => nested_usebundle; + "nested_usebundle" usebundle => nested_usebundle; STRESS_COPY_FROM_INIT:: - "copy_from_init" - usebundle => copy_from_init; + "copy_from_init" usebundle => copy_from_init; STRESS_COPY_MANY_FILES_TO_MANY_FILES:: "copy_many_files_to_many_files" @@ -55,30 +44,33 @@ bundle common knowledge "echo" string => "/bin/echo"; "bc" string => "/usr/bin/bc -l"; - "range" string => execresult("$(knowledge.seq) $(stress.large_number)", "noshell"); - "range_list" slist => splitstring("$(range)", $(const.n), "$(stress.large_number)"); + "range" + string => execresult( + "$(knowledge.seq) $(stress.large_number)", "noshell" + ); + + "range_list" + slist => splitstring("$(range)", $(const.n), "$(stress.large_number)"); } bundle agent define_classes { - classes: + classes: "$(this.bundle)_class_$(knowledge.range_list)" expression => "any"; reports: DEBUG:: - "$(this.bundle): Defined $(this.bundle)_class_$(knowledge.range_list)"; + "$(this.bundle): Defined $(this.bundle)_class_$(knowledge.range_list)"; } bundle agent commands_echo { commands: - "$(knowledge.echo)" - args => "$(this.bundle): $(knowledge.range_list)"; + "$(knowledge.echo)" args => "$(this.bundle): $(knowledge.range_list)"; reports: DEBUG:: - "$(this.bundle) Called $(knowledge.echo) $(this.bundle): $(knowledge.range_list)"; - + "$(this.bundle) Called $(knowledge.echo) $(this.bundle): $(knowledge.range_list)"; } bundle agent define_variables @@ -88,29 +80,33 @@ bundle agent define_variables reports: DEBUG:: - "$(this.bundle) Defined var_$(knowledge.range_list)"; + "$(this.bundle) Defined var_$(knowledge.range_list)"; } bundle agent execresult_echo_noshell { vars: "var_$(knowledge.range_list)" - string => execresult("$(knowledge.echo) $(this.bundle): $(knowledge.range_list)", "noshell"); + string => execresult( + "$(knowledge.echo) $(this.bundle): $(knowledge.range_list)", "noshell" + ); reports: DEBUG:: - "$(this.bundle) Defined var_$(knowledge.echo) with value $(var_$(knowledge.range_list))"; + "$(this.bundle) Defined var_$(knowledge.echo) with value $(var_$(knowledge.range_list))"; } bundle agent execresult_echo_useshell { vars: "var_$(knowledge.range_list)" - string => execresult("$(knowledge.echo) $(this.bundle): $(knowledge.range_list)", "useshell"); + string => execresult( + "$(knowledge.echo) $(this.bundle): $(knowledge.range_list)", "useshell" + ); reports: DEBUG:: - "$(this.bundle) Defined var_$(knowledge.echo) with value $(var_$(knowledge.range_list))"; + "$(this.bundle) Defined var_$(knowledge.echo) with value $(var_$(knowledge.range_list))"; } bundle agent define_persistent_classes @@ -122,8 +118,7 @@ bundle agent define_persistent_classes reports: DEBUG:: - "$(this.bundle): Defined persistent_$(this.bundle)_class_$(knowledge.range_list)"; - + "$(this.bundle): Defined persistent_$(this.bundle)_class_$(knowledge.range_list)"; } bundle agent nested_usebundle @@ -138,14 +133,17 @@ bundle agent nested_usebundle_param(number) # called with this bundle call will generate a segfault relatively quickly { vars: - "half" string => execresult("$(knowledge.echo) $(number)/1.2 | $(knowledge.bc)", "useshell"); + "half" + string => execresult( + "$(knowledge.echo) $(number)/1.2 | $(knowledge.bc)", "useshell" + ); methods: - "call myself inderectly" - usebundle => nested_usebundle_param($(half)); + "call myself inderectly" usebundle => nested_usebundle_param($(half)); reports: "$(knowledge.echo) $(number)/1.2 | $(knowledge.bc)"; + "$(this.bundle): '$(knowledge.echo) $(number)/1.2 | $(knowledge.bc)' = $(half)" action => immediate; } @@ -156,24 +154,24 @@ bundle agent copy_from_init "copy_from_source_path" string => "/var/cfengine/masterfiles/"; "copy_from_single_file" string => "$(copy_from_source_path)/promises.cf"; - files: "$(copy_from_source_path)/$(knowledge.range_list).txt" copy_from => local_cp("$(copy_from_single_file)"); - "/tmp/$(knowledge.range_list)" - delete => tidy; - + "/tmp/$(knowledge.range_list)" delete => tidy; } bundle agent copy_many_files_to_many_files { files: "/tmp/$(knowledge.range_list)" - copy_from => secure_cp("$(copy_from_init.copy_from_source_path)/$(knowledge.range_list).txt", $(sys.uqhost)), + copy_from => secure_cp( + "$(copy_from_init.copy_from_source_path)/$(knowledge.range_list).txt", + $(sys.uqhost) + ), classes => classes_generic("$(this.bundle)_tmp_$(knowledge.range_list)"); - # if you use 'generic' as the classes body here the agent aborts, need acceptance test. + # if you use 'generic' as the classes body here the agent aborts, need acceptance test. reports: DEBUG:: "$(this.bundle): copied $(copy_from_init.copy_from_source_path)/$(knowledge.range_list).txt to /tmp/$(knowledge.range_list)" @@ -183,45 +181,70 @@ bundle agent copy_many_files_to_many_files body classes classes_generic(x) # Define x prefixed/suffixed with promise outcome { - promise_repaired => { "promise_repaired_$(x)", "$(x)_repaired", "$(x)_ok", "$(x)_reached" }; - repair_failed => { "repair_failed_$(x)", "$(x)_failed", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" }; - repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" }; - repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" }; - promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(x)_not_repaired", "$(x)_reached" }; + promise_repaired => { + "promise_repaired_$(x)", "$(x)_repaired", "$(x)_ok", "$(x)_reached" + }; + repair_failed => { + "repair_failed_$(x)", + "$(x)_failed", + "$(x)_not_ok", + "$(x)_not_kept", + "$(x)_not_repaired", + "$(x)_reached", + }; + repair_denied => { + "repair_denied_$(x)", + "$(x)_denied", + "$(x)_not_ok", + "$(x)_not_kept", + "$(x)_not_repaired", + "$(x)_reached", + }; + repair_timeout => { + "repair_timeout_$(x)", + "$(x)_timeout", + "$(x)_not_ok", + "$(x)_not_kept", + "$(x)_not_repaired", + "$(x)_reached", + }; + promise_kept => { + "promise_kept_$(x)", + "$(x)_kept", + "$(x)_ok", + "$(x)_not_repaired", + "$(x)_reached", + }; } body copy_from local_cp(from) { -source => "$(from)"; + source => "$(from)"; } body delete tidy { -dirlinks => "delete"; -rmdirs => "true"; + dirlinks => "delete"; + rmdirs => "true"; } -body copy_from secure_cp(from,server) +body copy_from secure_cp(from, server) { -source => "$(from)"; -servers => { "$(server)" }; -compare => "digest"; -encrypt => "true"; -verify => "true"; + source => "$(from)"; + servers => { "$(server)" }; + compare => "digest"; + encrypt => "true"; + verify => "true"; } - - body action immediate { -ifelapsed => "0"; + ifelapsed => "0"; } - body classes persist(class, minutes) { promise_kept => { @(class) }; promise_repaired => { @(class) }; persist_time => "$(minutes)"; } - diff --git a/tests/unit/data/benchmark.cf b/tests/unit/data/benchmark.cf index 52c8d66921..0d455a1e78 100644 --- a/tests/unit/data/benchmark.cf +++ b/tests/unit/data/benchmark.cf @@ -1,38 +1,36 @@ # This file is intended to have as much feature variation as possible, # while still being correct. - body common control { - bundlesequence => { "main" }; + bundlesequence => { "main" }; } bundle agent main { reports: cfengine:: - "Hello, CFEngine" - friend_pattern => hash("abc", "md5"); + "Hello, CFEngine" friend_pattern => hash("abc", "md5"); + any:: - "Hello, world" - friend_pattern => hash("abc", "md5"); + "Hello, world" friend_pattern => hash("abc", "md5"); files: - "/tmp/stuff" -> { "stakeholder" } + "/tmp/stuff" -> { "stakeholder" } create => "true", perms => myperms; processes: - "/bin/stuff" -> { "stakeholder" } + "/bin/stuff" -> { "stakeholder" } process_count => any_count("stuff_running"); } body process_count any_count(cl) { - match_range => "0,0"; - out_of_range_define => { "$(cl)" }; + match_range => "0,0"; + out_of_range_define => { "$(cl)" }; } body perms myperms { - mode => "555"; + mode => "555"; } diff --git a/tests/unit/data/benchmark.json b/tests/unit/data/benchmark.json index 8d0917fb2f..5f6280456a 100644 --- a/tests/unit/data/benchmark.json +++ b/tests/unit/data/benchmark.json @@ -1,186 +1,156 @@ { - "bundles": [ + "bundles": [ + { + "sourcePath": "tests/unit/data/benchmark.cf", + "line": 9, + "namespace": "default", + "name": "main", + "bundleType": "agent", + "arguments": [], + "promiseTypes": [ { - "sourcePath": "tests/unit/data/benchmark.cf", - "line": 9, - "namespace": "default", - "name": "main", - "bundleType": "agent", - "arguments": [], - "promiseTypes": [ + "line": 11, + "name": "reports", + "contexts": [ + { + "name": "cfengine", + "promises": [ { - "line": 11, - "name": "reports", - "contexts": [ - { - "name": "cfengine", - "promises": [ - { - "line": 13, - "promiser": "Hello, CFEngine", - "attributes": [ - { - "line": 14, - "lval": "friend_pattern", - "rval": { - "type": "functionCall", - "name": "hash", - "arguments": [ - { - "type": "string", - "value": "abc" - }, - { - "type": "string", - "value": "md5" - } - ] - } - } - ] - } - ] - }, - { - "name": "any", - "promises": [ - { - "line": 16, - "promiser": "Hello, world", - "attributes": [ - { - "line": 17, - "lval": "friend_pattern", - "rval": { - "type": "functionCall", - "name": "hash", - "arguments": [ - { - "type": "string", - "value": "abc" - }, - { - "type": "string", - "value": "md5" - } - ] - } - } - ] - } - ] - } - ] - }, - { - "line": 19, - "name": "files", - "contexts": [ - { - "name": "any", - "promises": [ - { - "line": 20, - "promiser": "/tmp/stuff", - "promisee": [ - "stakeholder" - ], - "attributes": [ - { - "line": 21, - "lval": "create", - "rval": { - "type": "string", - "value": "true" - } - }, - { - "line": 22, - "lval": "perms", - "rval": { - "type": "symbol", - "value": "myperms" - } - } - ] - } - ] - } - ] - }, + "line": 13, + "promiser": "Hello, CFEngine", + "attributes": [ + { + "line": 14, + "lval": "friend_pattern", + "rval": { + "type": "functionCall", + "name": "hash", + "arguments": [ + { "type": "string", "value": "abc" }, + { "type": "string", "value": "md5" } + ] + } + } + ] + } + ] + }, + { + "name": "any", + "promises": [ { - "line": 24, - "name": "processes", - "contexts": [ - { - "name": "any", - "promises": [ - { - "line": 25, - "promiser": "/bin/stuff", - "promisee": [ - "stakeholder" - ], - "attributes": [] - } - ] - } - ] + "line": 16, + "promiser": "Hello, world", + "attributes": [ + { + "line": 17, + "lval": "friend_pattern", + "rval": { + "type": "functionCall", + "name": "hash", + "arguments": [ + { "type": "string", "value": "abc" }, + { "type": "string", "value": "md5" } + ] + } + } + ] } - ] - } - ], - "bodies": [ + ] + } + ] + }, { - "sourcePath": "tests/unit/data/benchmark.cf", - "line": 4, - "namespace": "default", - "name": "control", - "bodyType": "common", - "arguments": [], - "contexts": [ + "line": 19, + "name": "files", + "contexts": [ + { + "name": "any", + "promises": [ { - "name": "any", - "attributes": [ - { - "line": 6, - "lval": "bundlesequence", - "rval": { - "type": "list", - "value": [ - { - "type": "string", - "value": "main" - } - ] - } - } - ] + "line": 20, + "promiser": "/tmp/stuff", + "promisee": ["stakeholder"], + "attributes": [ + { + "line": 21, + "lval": "create", + "rval": { "type": "string", "value": "true" } + }, + { + "line": 22, + "lval": "perms", + "rval": { "type": "symbol", "value": "myperms" } + } + ] } - ] + ] + } + ] }, { - "sourcePath": "tests/unit/data/benchmark.cf", - "line": 28, - "namespace": "default", - "name": "myperms", - "bodyType": "perms", - "arguments": [], - "contexts": [ + "line": 24, + "name": "processes", + "contexts": [ + { + "name": "any", + "promises": [ { - "name": "any", - "attributes": [ - { - "line": 30, - "lval": "mode", - "rval": { - "type": "string", - "value": "555" - } - } - ] + "line": 25, + "promiser": "/bin/stuff", + "promisee": ["stakeholder"], + "attributes": [] } - ] + ] + } + ] + } + ] + } + ], + "bodies": [ + { + "sourcePath": "tests/unit/data/benchmark.cf", + "line": 4, + "namespace": "default", + "name": "control", + "bodyType": "common", + "arguments": [], + "contexts": [ + { + "name": "any", + "attributes": [ + { + "line": 6, + "lval": "bundlesequence", + "rval": { + "type": "list", + "value": [{ "type": "string", "value": "main" }] + } + } + ] + } + ] + }, + { + "sourcePath": "tests/unit/data/benchmark.cf", + "line": 28, + "namespace": "default", + "name": "myperms", + "bodyType": "perms", + "arguments": [], + "contexts": [ + { + "name": "any", + "attributes": [ + { + "line": 30, + "lval": "mode", + "rval": { "type": "string", "value": "555" } + } + ] } - ] + ] + } + ] } diff --git a/tests/unit/data/body_edit_line_common_constraints.cf b/tests/unit/data/body_edit_line_common_constraints.cf index 398d14645b..23c67e749e 100644 --- a/tests/unit/data/body_edit_line_common_constraints.cf +++ b/tests/unit/data/body_edit_line_common_constraints.cf @@ -1,6 +1,5 @@ bundle edit_line test { delete_lines: - "abc" - select_region => test_select; + "abc" select_region => test_select; } diff --git a/tests/unit/data/body_edit_xml_common_constraints.cf b/tests/unit/data/body_edit_xml_common_constraints.cf index 58bff998c5..fcfbbe6ab1 100644 --- a/tests/unit/data/body_edit_xml_common_constraints.cf +++ b/tests/unit/data/body_edit_xml_common_constraints.cf @@ -1,6 +1,5 @@ bundle edit_xml test { build_xpath: - "foo" - select_xpath => "abc"; + "foo" select_xpath => "abc"; } diff --git a/tests/unit/data/body_executor_control_agent_expireafter_only.cf b/tests/unit/data/body_executor_control_agent_expireafter_only.cf index f77b10500d..cc82641905 100644 --- a/tests/unit/data/body_executor_control_agent_expireafter_only.cf +++ b/tests/unit/data/body_executor_control_agent_expireafter_only.cf @@ -1,4 +1,4 @@ body executor control { - agent_expireafter => "121"; + agent_expireafter => "121"; } diff --git a/tests/unit/data/body_executor_control_full.cf b/tests/unit/data/body_executor_control_full.cf index 66fad8cdb3..74ca1957ed 100644 --- a/tests/unit/data/body_executor_control_full.cf +++ b/tests/unit/data/body_executor_control_full.cf @@ -1,13 +1,13 @@ body executor control { - splaytime => "1"; - mailto => "cfengine_mail@example.org"; - mailfrom => "cfengine@example.org"; - mailsubject => "Test [localhost/127.0.0.1]"; - smtpserver => "localhost"; - mailmaxlines => "50"; - schedule => { "Min00_05", "Min05_10" }; - executorfacility => "LOG_LOCAL6"; - agent_expireafter => "120"; - exec_command => "/bin/echo"; + splaytime => "1"; + mailto => "cfengine_mail@example.org"; + mailfrom => "cfengine@example.org"; + mailsubject => "Test [localhost/127.0.0.1]"; + smtpserver => "localhost"; + mailmaxlines => "50"; + schedule => { "Min00_05", "Min05_10" }; + executorfacility => "LOG_LOCAL6"; + agent_expireafter => "120"; + exec_command => "/bin/echo"; } diff --git a/tests/unit/data/body_redefinition.cf b/tests/unit/data/body_redefinition.cf index bf1fe68734..f06fbe8d30 100644 --- a/tests/unit/data/body_redefinition.cf +++ b/tests/unit/data/body_redefinition.cf @@ -1,9 +1,9 @@ body agent control { - ifelapsed => "60"; + ifelapsed => "60"; } body agent control { - ifelapsed => "120"; + ifelapsed => "120"; } diff --git a/tests/unit/data/bundle_custom_promise_type.cf b/tests/unit/data/bundle_custom_promise_type.cf index 2ab37741d7..ca278c36c2 100644 --- a/tests/unit/data/bundle_custom_promise_type.cf +++ b/tests/unit/data/bundle_custom_promise_type.cf @@ -2,6 +2,6 @@ # defined in another policy file bundle agent foo { -custom_promise_type: - "var" string => "test"; + custom_promise_type: + "var" string => "test"; } diff --git a/tests/unit/data/constraint_comment_nonscalar.cf b/tests/unit/data/constraint_comment_nonscalar.cf index e935dc2e7d..da7243eb4a 100644 --- a/tests/unit/data/constraint_comment_nonscalar.cf +++ b/tests/unit/data/constraint_comment_nonscalar.cf @@ -1,6 +1,5 @@ bundle agent foo { reports: - "hello" - comment => { "a" }; + "hello" comment => { "a" }; } diff --git a/tests/unit/data/methods_invalid_arity.cf b/tests/unit/data/methods_invalid_arity.cf index 2b1188cb0f..aae9ad1791 100644 --- a/tests/unit/data/methods_invalid_arity.cf +++ b/tests/unit/data/methods_invalid_arity.cf @@ -8,6 +8,5 @@ bundle agent foo(one, two) bundle agent bar { methods: - "any" - usebundle => foo("snookie"); + "any" usebundle => foo("snookie"); } diff --git a/tests/unit/data/mustache_comments.json b/tests/unit/data/mustache_comments.json index eb2fc158f9..6cd078f7c1 100644 --- a/tests/unit/data/mustache_comments.json +++ b/tests/unit/data/mustache_comments.json @@ -1,83 +1,83 @@ { - "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", - "overview": "Comment tags represent content that should never appear in the resulting\noutput.\n\nThe tag's content may contain any substring (including newlines) EXCEPT the\nclosing delimiter.\n\nComment tags SHOULD be treated as standalone when appropriate.\n", - "tests": [ - { - "name": "Inline", - "data": {}, - "expected": "1234567890", - "template": "12345{{! Comment Block! }}67890", - "desc": "Comment blocks should be removed from the template." - }, - { - "name": "Multiline", - "data": {}, - "expected": "1234567890\n", - "template": "12345{{!\n This is a\n multi-line comment...\n}}67890\n", - "desc": "Multiline comments should be permitted." - }, - { - "name": "Standalone", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n{{! Comment Block! }}\nEnd.\n", - "desc": "All standalone comment lines should be removed." - }, - { - "name": "Indented Standalone", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n {{! Indented Comment Block! }}\nEnd.\n", - "desc": "All standalone comment lines should be removed." - }, - { - "name": "Standalone Line Endings", - "data": {}, - "expected": "|\r\n|", - "template": "|\r\n{{! Standalone Comment }}\r\n|", - "desc": "\"\\r\\n\" should be considered a newline for standalone tags." - }, - { - "name": "Standalone Without Previous Line", - "data": {}, - "expected": "!", - "template": " {{! I'm Still Standalone }}\n!", - "desc": "Standalone tags should not require a newline to precede them." - }, - { - "name": "Standalone Without Newline", - "data": {}, - "expected": "!\n", - "template": "!\n {{! I'm Still Standalone }}", - "desc": "Standalone tags should not require a newline to follow them." - }, - { - "name": "Multiline Standalone", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n", - "desc": "All standalone comment lines should be removed." - }, - { - "name": "Indented Multiline Standalone", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n", - "desc": "All standalone comment lines should be removed." - }, - { - "name": "Indented Inline", - "data": {}, - "expected": " 12 \n", - "template": " 12 {{! 34 }}\n", - "desc": "Inline comments should not strip whitespace" - }, - { - "name": "Surrounding Whitespace", - "data": {}, - "expected": "12345 67890", - "template": "12345 {{! Comment Block! }} 67890", - "desc": "Comment removal should preserve surrounding whitespace." - } - ] + "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", + "overview": "Comment tags represent content that should never appear in the resulting\noutput.\n\nThe tag's content may contain any substring (including newlines) EXCEPT the\nclosing delimiter.\n\nComment tags SHOULD be treated as standalone when appropriate.\n", + "tests": [ + { + "name": "Inline", + "data": {}, + "expected": "1234567890", + "template": "12345{{! Comment Block! }}67890", + "desc": "Comment blocks should be removed from the template." + }, + { + "name": "Multiline", + "data": {}, + "expected": "1234567890\n", + "template": "12345{{!\n This is a\n multi-line comment...\n}}67890\n", + "desc": "Multiline comments should be permitted." + }, + { + "name": "Standalone", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n{{! Comment Block! }}\nEnd.\n", + "desc": "All standalone comment lines should be removed." + }, + { + "name": "Indented Standalone", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n {{! Indented Comment Block! }}\nEnd.\n", + "desc": "All standalone comment lines should be removed." + }, + { + "name": "Standalone Line Endings", + "data": {}, + "expected": "|\r\n|", + "template": "|\r\n{{! Standalone Comment }}\r\n|", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags." + }, + { + "name": "Standalone Without Previous Line", + "data": {}, + "expected": "!", + "template": " {{! I'm Still Standalone }}\n!", + "desc": "Standalone tags should not require a newline to precede them." + }, + { + "name": "Standalone Without Newline", + "data": {}, + "expected": "!\n", + "template": "!\n {{! I'm Still Standalone }}", + "desc": "Standalone tags should not require a newline to follow them." + }, + { + "name": "Multiline Standalone", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n", + "desc": "All standalone comment lines should be removed." + }, + { + "name": "Indented Multiline Standalone", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n", + "desc": "All standalone comment lines should be removed." + }, + { + "name": "Indented Inline", + "data": {}, + "expected": " 12 \n", + "template": " 12 {{! 34 }}\n", + "desc": "Inline comments should not strip whitespace" + }, + { + "name": "Surrounding Whitespace", + "data": {}, + "expected": "12345 67890", + "template": "12345 {{! Comment Block! }} 67890", + "desc": "Comment removal should preserve surrounding whitespace." + } + ] } diff --git a/tests/unit/data/mustache_delimiters.json b/tests/unit/data/mustache_delimiters.json index de1c22ba0a..803f1795f6 100644 --- a/tests/unit/data/mustache_delimiters.json +++ b/tests/unit/data/mustache_delimiters.json @@ -1,77 +1,67 @@ { - "tests": [ - { - "name": "Pair Behavior", - "data": { - "text": "Hey!" - }, - "expected": "(Hey!)", - "template": "{{=<% %>=}}(<%text%>)", - "desc": "The equals sign (used on both sides) should permit delimiter changes." - }, - { - "name": "Special Characters", - "data": { - "text": "It worked!" - }, - "expected": "(It worked!)", - "template": "({{=[ ]=}}[text])", - "desc": "Characters with special meaning regexen should be valid delimiters." - }, - { - "name": "Sections", - "data": { - "section": true, - "data": "I got interpolated." - }, - "expected": "[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n", - "template": "[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n", - "desc": "Delimiters set outside sections should persist." - }, - { - "name": "Inverted Sections", - "data": { - "section": false, - "data": "I got interpolated." - }, - "expected": "[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n", - "template": "[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n", - "desc": "Delimiters set outside inverted sections should persist." - }, - { - "name": "Surrounding Whitespace", - "data": {}, - "expected": "| |", - "template": "| {{=@ @=}} |", - "desc": "Surrounding whitespace should be left untouched." - }, - { - "name": "Outlying Whitespace (Inline)", - "data": {}, - "expected": " | \n", - "template": " | {{=@ @=}}\n", - "desc": "Whitespace should be left untouched." - }, - { - "name": "Standalone Tag", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n{{=@ @=}}\nEnd.\n", - "desc": "Standalone lines should be removed from the template." - }, - { - "name": "Indented Standalone Tag", - "data": {}, - "expected": "Begin.\nEnd.\n", - "template": "Begin.\n {{=@ @=}}\nEnd.\n", - "desc": "Indented standalone lines should be removed from the template." - }, - { - "name": "Pair with Padding", - "data": {}, - "expected": "||", - "template": "|{{= @ @ =}}|", - "desc": "Superfluous in-tag whitespace should be ignored." - } - ] + "tests": [ + { + "name": "Pair Behavior", + "data": { "text": "Hey!" }, + "expected": "(Hey!)", + "template": "{{=<% %>=}}(<%text%>)", + "desc": "The equals sign (used on both sides) should permit delimiter changes." + }, + { + "name": "Special Characters", + "data": { "text": "It worked!" }, + "expected": "(It worked!)", + "template": "({{=[ ]=}}[text])", + "desc": "Characters with special meaning regexen should be valid delimiters." + }, + { + "name": "Sections", + "data": { "section": true, "data": "I got interpolated." }, + "expected": "[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n", + "template": "[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n", + "desc": "Delimiters set outside sections should persist." + }, + { + "name": "Inverted Sections", + "data": { "section": false, "data": "I got interpolated." }, + "expected": "[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n", + "template": "[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n", + "desc": "Delimiters set outside inverted sections should persist." + }, + { + "name": "Surrounding Whitespace", + "data": {}, + "expected": "| |", + "template": "| {{=@ @=}} |", + "desc": "Surrounding whitespace should be left untouched." + }, + { + "name": "Outlying Whitespace (Inline)", + "data": {}, + "expected": " | \n", + "template": " | {{=@ @=}}\n", + "desc": "Whitespace should be left untouched." + }, + { + "name": "Standalone Tag", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n{{=@ @=}}\nEnd.\n", + "desc": "Standalone lines should be removed from the template." + }, + { + "name": "Indented Standalone Tag", + "data": {}, + "expected": "Begin.\nEnd.\n", + "template": "Begin.\n {{=@ @=}}\nEnd.\n", + "desc": "Indented standalone lines should be removed from the template." + }, + { + "name": "Pair with Padding", + "data": {}, + "expected": "||", + "template": "|{{= @ @ =}}|", + "desc": "Superfluous in-tag whitespace should be ignored." + } + ] } diff --git a/tests/unit/data/mustache_extra.json b/tests/unit/data/mustache_extra.json index a69973b933..ac77a5a69f 100644 --- a/tests/unit/data/mustache_extra.json +++ b/tests/unit/data/mustache_extra.json @@ -1,66 +1,66 @@ { - "tests": [ - { - "name": "Demo", - "data": { - "header": "Colors", - "items": [ - {"name": "red", "first": true, "url": "#Red"}, - {"name": "green", "link": true, "url": "#Green"}, - {"name": "blue", "link": true, "url": "#Blue"} - ], - "empty": false - }, - "expected": "

Colors

\n\n
  • red
  • \n
  • green
  • \n
  • blue
  • \n\n", - "template": "

    {{header}}

    \n{{#bug}}\n{{/bug}}\n\n{{#items}}\n {{#first}}\n
  • {{name}}
  • \n {{/first}}\n {{#link}}\n
  • {{name}}
  • \n {{/link}}\n{{/items}}\n\n{{#empty}}\n

    The list is empty.

    \n{{/empty}}\n" - }, - { - "name": "Ted's Abusive Test 1", - "data": { "x": 123 }, - "template": "{{x}}", - "expected": "123" - }, - { - "name": "Ted's Abusive Test 2", - "data": { "x": 123, "y": 456 }, - "template": "{{x}} {{y}}", - "expected": "123 456" - }, - { - "name": "Ted's Abusive Test 3", - "data": [ null ], - "template": "{{null}}", - "expected": "" - }, - { - "name": "Ted's Abusive Test 4", - "data": { "x": 123, "y": 456 }, - "template": "{{}}", - "expected": "{{}}" - }, - { - "name": "Ted's Abusive Test 5", - "data": { "boolean": true}, - "template": "{{#boolean}}IT IS TRUE{{/boolean}}", - "expected": "IT IS TRUE" - }, - { - "name": "Ted's Abusive Test 6", - "data": { "boolean": false}, - "template": "{{^boolean}}IT IS FALSE{{/boolean}}", - "expected": "IT IS FALSE" - }, - { - "name": "Ted's Abusive Test 7", - "data": { "list": - [ - { "k": 789, "v": 0 }, - { "k": null, "v": true }, - { "k": -1, "v": -2 } - ] - }, - "template": "{{#list}}{{k}}={{v}}, {{/list}}", - "expected": "789=0, =true, -1=-2, " - } - ] + "tests": [ + { + "name": "Demo", + "data": { + "header": "Colors", + "items": [ + { "name": "red", "first": true, "url": "#Red" }, + { "name": "green", "link": true, "url": "#Green" }, + { "name": "blue", "link": true, "url": "#Blue" } + ], + "empty": false + }, + "expected": "

    Colors

    \n\n
  • red
  • \n
  • green
  • \n
  • blue
  • \n\n", + "template": "

    {{header}}

    \n{{#bug}}\n{{/bug}}\n\n{{#items}}\n {{#first}}\n
  • {{name}}
  • \n {{/first}}\n {{#link}}\n
  • {{name}}
  • \n {{/link}}\n{{/items}}\n\n{{#empty}}\n

    The list is empty.

    \n{{/empty}}\n" + }, + { + "name": "Ted's Abusive Test 1", + "data": { "x": 123 }, + "template": "{{x}}", + "expected": "123" + }, + { + "name": "Ted's Abusive Test 2", + "data": { "x": 123, "y": 456 }, + "template": "{{x}} {{y}}", + "expected": "123 456" + }, + { + "name": "Ted's Abusive Test 3", + "data": [null], + "template": "{{null}}", + "expected": "" + }, + { + "name": "Ted's Abusive Test 4", + "data": { "x": 123, "y": 456 }, + "template": "{{}}", + "expected": "{{}}" + }, + { + "name": "Ted's Abusive Test 5", + "data": { "boolean": true }, + "template": "{{#boolean}}IT IS TRUE{{/boolean}}", + "expected": "IT IS TRUE" + }, + { + "name": "Ted's Abusive Test 6", + "data": { "boolean": false }, + "template": "{{^boolean}}IT IS FALSE{{/boolean}}", + "expected": "IT IS FALSE" + }, + { + "name": "Ted's Abusive Test 7", + "data": { + "list": [ + { "k": 789, "v": 0 }, + { "k": null, "v": true }, + { "k": -1, "v": -2 } + ] + }, + "template": "{{#list}}{{k}}={{v}}, {{/list}}", + "expected": "789=0, =true, -1=-2, " + } + ] } diff --git a/tests/unit/data/mustache_interpolation.json b/tests/unit/data/mustache_interpolation.json index 0b2c03aa4c..a1fd4ec19f 100644 --- a/tests/unit/data/mustache_interpolation.json +++ b/tests/unit/data/mustache_interpolation.json @@ -1,182 +1,144 @@ { - "tests": [ - { - "name": "No Interpolation", - "data": {}, - "expected": "Hello from {Mustache}!\n", - "template": "Hello from {Mustache}!\n", - "desc": "Mustache-free templates should render as-is." - }, - { - "name": "Basic Interpolation", - "data": { - "subject": "world" - }, - "expected": "Hello, world!\n", - "template": "Hello, {{subject}}!\n", - "desc": "Unadorned tags should interpolate content into the template." - }, - { - "name": "HTML Escaping", - "data": { - "forbidden": "& \" < >" - }, - "expected": "These characters should be HTML escaped: & " < >\n", - "template": "These characters should be HTML escaped: {{forbidden}}\n", - "desc": "Basic interpolation should be HTML escaped." - }, - { - "name": "Triple Mustache", - "data": { - "forbidden": "& \" < >" - }, - "expected": "These characters should not be HTML escaped: & \" < >\n", - "template": "These characters should not be HTML escaped: {{{forbidden}}}\n", - "desc": "Triple mustaches should interpolate without HTML escaping." - }, - { - "name": "Ampersand", - "data": { - "forbidden": "& \" < >" - }, - "expected": "These characters should not be HTML escaped: & \" < >\n", - "template": "These characters should not be HTML escaped: {{&forbidden}}\n", - "desc": "Ampersand should interpolate without HTML escaping." - }, - { - "name": "Basic Integer Interpolation", - "data": { - "mph": 85 - }, - "expected": "\"85 miles an hour!\"", - "template": "\"{{mph}} miles an hour!\"", - "desc": "Integers should interpolate seamlessly." - }, - { - "name": "Triple Mustache Integer Interpolation", - "data": { - "mph": 85 - }, - "expected": "\"85 miles an hour!\"", - "template": "\"{{{mph}}} miles an hour!\"", - "desc": "Integers should interpolate seamlessly." - }, - { - "name": "Ampersand Integer Interpolation", - "data": { - "mph": 85 - }, - "expected": "\"85 miles an hour!\"", - "template": "\"{{&mph}} miles an hour!\"", - "desc": "Integers should interpolate seamlessly." - }, - { - "name": "Basic Decimal Interpolation", - "data": { - "power": 1.21 - }, - "expected": "\"1.21 jiggawatts!\"", - "template": "\"{{power}} jiggawatts!\"", - "desc": "Decimals should interpolate seamlessly with proper significance." - }, - { - "name": "Triple Mustache Decimal Interpolation", - "data": { - "power": 1.21 - }, - "expected": "\"1.21 jiggawatts!\"", - "template": "\"{{{power}}} jiggawatts!\"", - "desc": "Decimals should interpolate seamlessly with proper significance." - }, - { - "name": "Ampersand Decimal Interpolation", - "data": { - "power": 1.21 - }, - "expected": "\"1.21 jiggawatts!\"", - "template": "\"{{&power}} jiggawatts!\"", - "desc": "Decimals should interpolate seamlessly with proper significance." - }, - { - "name": "Interpolation - Surrounding Whitespace", - "data": { - "string": "---" - }, - "expected": "| --- |", - "template": "| {{string}} |", - "desc": "Interpolation should not alter surrounding whitespace." - }, - { - "name": "Triple Mustache - Surrounding Whitespace", - "data": { - "string": "---" - }, - "expected": "| --- |", - "template": "| {{{string}}} |", - "desc": "Interpolation should not alter surrounding whitespace." - }, - { - "name": "Ampersand - Surrounding Whitespace", - "data": { - "string": "---" - }, - "expected": "| --- |", - "template": "| {{&string}} |", - "desc": "Interpolation should not alter surrounding whitespace." - }, - { - "name": "Interpolation - Standalone", - "data": { - "string": "---" - }, - "expected": " ---\n", - "template": " {{string}}\n", - "desc": "Standalone interpolation should not alter surrounding whitespace." - }, - { - "name": "Triple Mustache - Standalone", - "data": { - "string": "---" - }, - "expected": " ---\n", - "template": " {{{string}}}\n", - "desc": "Standalone interpolation should not alter surrounding whitespace." - }, - { - "name": "Ampersand - Standalone", - "data": { - "string": "---" - }, - "expected": " ---\n", - "template": " {{&string}}\n", - "desc": "Standalone interpolation should not alter surrounding whitespace." - }, - { - "name": "Interpolation With Padding", - "data": { - "string": "---" - }, - "expected": "|---|", - "template": "|{{ string }}|", - "desc": "Superfluous in-tag whitespace should be ignored." - }, - { - "name": "Triple Mustache With Padding", - "data": { - "string": "---" - }, - "expected": "|---|", - "template": "|{{{ string }}}|", - "desc": "Superfluous in-tag whitespace should be ignored." - }, - { - "name": "Ampersand With Padding", - "data": { - "string": "---" - }, - "expected": "|---|", - "template": "|{{& string }}|", - "desc": "Superfluous in-tag whitespace should be ignored." - } - ] + "tests": [ + { + "name": "No Interpolation", + "data": {}, + "expected": "Hello from {Mustache}!\n", + "template": "Hello from {Mustache}!\n", + "desc": "Mustache-free templates should render as-is." + }, + { + "name": "Basic Interpolation", + "data": { "subject": "world" }, + "expected": "Hello, world!\n", + "template": "Hello, {{subject}}!\n", + "desc": "Unadorned tags should interpolate content into the template." + }, + { + "name": "HTML Escaping", + "data": { "forbidden": "& \" < >" }, + "expected": "These characters should be HTML escaped: & " < >\n", + "template": "These characters should be HTML escaped: {{forbidden}}\n", + "desc": "Basic interpolation should be HTML escaped." + }, + { + "name": "Triple Mustache", + "data": { "forbidden": "& \" < >" }, + "expected": "These characters should not be HTML escaped: & \" < >\n", + "template": "These characters should not be HTML escaped: {{{forbidden}}}\n", + "desc": "Triple mustaches should interpolate without HTML escaping." + }, + { + "name": "Ampersand", + "data": { "forbidden": "& \" < >" }, + "expected": "These characters should not be HTML escaped: & \" < >\n", + "template": "These characters should not be HTML escaped: {{&forbidden}}\n", + "desc": "Ampersand should interpolate without HTML escaping." + }, + { + "name": "Basic Integer Interpolation", + "data": { "mph": 85 }, + "expected": "\"85 miles an hour!\"", + "template": "\"{{mph}} miles an hour!\"", + "desc": "Integers should interpolate seamlessly." + }, + { + "name": "Triple Mustache Integer Interpolation", + "data": { "mph": 85 }, + "expected": "\"85 miles an hour!\"", + "template": "\"{{{mph}}} miles an hour!\"", + "desc": "Integers should interpolate seamlessly." + }, + { + "name": "Ampersand Integer Interpolation", + "data": { "mph": 85 }, + "expected": "\"85 miles an hour!\"", + "template": "\"{{&mph}} miles an hour!\"", + "desc": "Integers should interpolate seamlessly." + }, + { + "name": "Basic Decimal Interpolation", + "data": { "power": 1.21 }, + "expected": "\"1.21 jiggawatts!\"", + "template": "\"{{power}} jiggawatts!\"", + "desc": "Decimals should interpolate seamlessly with proper significance." + }, + { + "name": "Triple Mustache Decimal Interpolation", + "data": { "power": 1.21 }, + "expected": "\"1.21 jiggawatts!\"", + "template": "\"{{{power}}} jiggawatts!\"", + "desc": "Decimals should interpolate seamlessly with proper significance." + }, + { + "name": "Ampersand Decimal Interpolation", + "data": { "power": 1.21 }, + "expected": "\"1.21 jiggawatts!\"", + "template": "\"{{&power}} jiggawatts!\"", + "desc": "Decimals should interpolate seamlessly with proper significance." + }, + { + "name": "Interpolation - Surrounding Whitespace", + "data": { "string": "---" }, + "expected": "| --- |", + "template": "| {{string}} |", + "desc": "Interpolation should not alter surrounding whitespace." + }, + { + "name": "Triple Mustache - Surrounding Whitespace", + "data": { "string": "---" }, + "expected": "| --- |", + "template": "| {{{string}}} |", + "desc": "Interpolation should not alter surrounding whitespace." + }, + { + "name": "Ampersand - Surrounding Whitespace", + "data": { "string": "---" }, + "expected": "| --- |", + "template": "| {{&string}} |", + "desc": "Interpolation should not alter surrounding whitespace." + }, + { + "name": "Interpolation - Standalone", + "data": { "string": "---" }, + "expected": " ---\n", + "template": " {{string}}\n", + "desc": "Standalone interpolation should not alter surrounding whitespace." + }, + { + "name": "Triple Mustache - Standalone", + "data": { "string": "---" }, + "expected": " ---\n", + "template": " {{{string}}}\n", + "desc": "Standalone interpolation should not alter surrounding whitespace." + }, + { + "name": "Ampersand - Standalone", + "data": { "string": "---" }, + "expected": " ---\n", + "template": " {{&string}}\n", + "desc": "Standalone interpolation should not alter surrounding whitespace." + }, + { + "name": "Interpolation With Padding", + "data": { "string": "---" }, + "expected": "|---|", + "template": "|{{ string }}|", + "desc": "Superfluous in-tag whitespace should be ignored." + }, + { + "name": "Triple Mustache With Padding", + "data": { "string": "---" }, + "expected": "|---|", + "template": "|{{{ string }}}|", + "desc": "Superfluous in-tag whitespace should be ignored." + }, + { + "name": "Ampersand With Padding", + "data": { "string": "---" }, + "expected": "|---|", + "template": "|{{& string }}|", + "desc": "Superfluous in-tag whitespace should be ignored." + } + ] } diff --git a/tests/unit/data/mustache_inverted.json b/tests/unit/data/mustache_inverted.json index aae4cc3f8f..9a7ddac3cc 100644 --- a/tests/unit/data/mustache_inverted.json +++ b/tests/unit/data/mustache_inverted.json @@ -1,214 +1,153 @@ { - "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", - "overview": "Inverted Section tags and End Section tags are used in combination to wrap a\nsection of the template.\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Inverted Section tag MUST be\nfollowed by an End Section tag with the same content within the same\nsection.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nThis section MUST NOT be rendered unless the data list is empty.\n\nInverted Section and End Section tags SHOULD be treated as standalone when\nappropriate.\n", - "tests": [ - { - "name": "Falsey", - "data": { - "boolean": false - }, - "expected": "\"This should be rendered.\"", - "template": "\"{{^boolean}}This should be rendered.{{/boolean}}\"", - "desc": "Falsey sections should have their contents rendered." - }, - { - "name": "Truthy", - "data": { - "boolean": true - }, - "expected": "\"\"", - "template": "\"{{^boolean}}This should not be rendered.{{/boolean}}\"", - "desc": "Truthy sections should have their contents omitted." - }, - { - "name": "Context", - "data": { - "context": { - "name": "Joe" - } - }, - "expected": "\"\"", - "template": "\"{{^context}}Hi {{name}}.{{/context}}\"", - "desc": "Objects and hashes should behave like truthy values." - }, - { - "name": "List", - "data": { - "list": [ - { - "n": 1 - }, - { - "n": 2 - }, - { - "n": 3 - } - ] - }, - "expected": "\"\"", - "template": "\"{{^list}}{{n}}{{/list}}\"", - "desc": "Lists should behave like truthy values." - }, - { - "name": "Empty List", - "data": { - "list": [] - }, - "expected": "\"Yay lists!\"", - "template": "\"{{^list}}Yay lists!{{/list}}\"", - "desc": "Empty lists should behave like falsey values." - }, - { - "name": "Doubled", - "data": { - "two": "second", - "bool": false - }, - "expected": "* first\n* second\n* third\n", - "template": "{{^bool}}\n* first\n{{/bool}}\n* {{two}}\n{{^bool}}\n* third\n{{/bool}}\n", - "desc": "Multiple inverted sections per template should be permitted." - }, - { - "name": "Nested (Falsey)", - "data": { - "bool": false - }, - "expected": "| A B C D E |", - "template": "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", - "desc": "Nested falsey sections should have their contents rendered." - }, - { - "name": "Nested (Truthy)", - "data": { - "bool": true - }, - "expected": "| A E |", - "template": "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", - "desc": "Nested truthy sections should be omitted." - }, - { - "name": "Context Misses", - "data": {}, - "expected": "[Cannot find key 'missing'!]", - "template": "[{{^missing}}Cannot find key 'missing'!{{/missing}}]", - "desc": "Failed context lookups should be considered falsey." - }, - { - "name": "Dotted Names - Truthy", - "data": { - "a": { - "b": { - "c": true - } - } - }, - "expected": "\"\" == \"\"", - "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"\"", - "desc": "Dotted names should be valid for Inverted Section tags." - }, - { - "name": "Dotted Names - Falsey", - "data": { - "a": { - "b": { - "c": false - } - } - }, - "expected": "\"Not Here\" == \"Not Here\"", - "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"", - "desc": "Dotted names should be valid for Inverted Section tags." - }, - { - "name": "Dotted Names - Broken Chains", - "data": { - "a": {} - }, - "expected": "\"Not Here\" == \"Not Here\"", - "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"", - "desc": "Dotted names that cannot be resolved should be considered falsey." - }, - { - "name": "Surrounding Whitespace", - "data": { - "boolean": false - }, - "expected": " | \t|\t | \n", - "template": " | {{^boolean}}\t|\t{{/boolean}} | \n", - "desc": "Inverted sections should not alter surrounding whitespace." - }, - { - "name": "Internal Whitespace", - "data": { - "boolean": false - }, - "expected": " | \n | \n", - "template": " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n", - "desc": "Inverted should not alter internal whitespace." - }, - { - "name": "Indented Inline Sections", - "data": { - "boolean": false - }, - "expected": " NO\n WAY\n", - "template": " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n", - "desc": "Single-line sections should not alter surrounding whitespace." - }, - { - "name": "Standalone Lines", - "data": { - "boolean": false - }, - "expected": "| This Is\n|\n| A Line\n", - "template": "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n", - "desc": "Standalone lines should be removed from the template." - }, - { - "name": "Standalone Indented Lines", - "data": { - "boolean": false - }, - "expected": "| This Is\n|\n| A Line\n", - "template": "| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n", - "desc": "Standalone indented lines should be removed from the template." - }, - { - "name": "Standalone Line Endings", - "data": { - "boolean": false - }, - "expected": "|\r\n|", - "template": "|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|", - "desc": "\"\\r\\n\" should be considered a newline for standalone tags." - }, - { - "name": "Standalone Without Previous Line", - "data": { - "boolean": false - }, - "expected": "^\n/", - "template": " {{^boolean}}\n^{{/boolean}}\n/", - "desc": "Standalone tags should not require a newline to precede them." - }, - { - "name": "Standalone Without Newline", - "data": { - "boolean": false - }, - "expected": "^\n/\n", - "template": "^{{^boolean}}\n/\n {{/boolean}}", - "desc": "Standalone tags should not require a newline to follow them." - }, - { - "name": "Padding", - "data": { - "boolean": false - }, - "expected": "|=|", - "template": "|{{^ boolean }}={{/ boolean }}|", - "desc": "Superfluous in-tag whitespace should be ignored." - } - ] + "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", + "overview": "Inverted Section tags and End Section tags are used in combination to wrap a\nsection of the template.\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Inverted Section tag MUST be\nfollowed by an End Section tag with the same content within the same\nsection.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nThis section MUST NOT be rendered unless the data list is empty.\n\nInverted Section and End Section tags SHOULD be treated as standalone when\nappropriate.\n", + "tests": [ + { + "name": "Falsey", + "data": { "boolean": false }, + "expected": "\"This should be rendered.\"", + "template": "\"{{^boolean}}This should be rendered.{{/boolean}}\"", + "desc": "Falsey sections should have their contents rendered." + }, + { + "name": "Truthy", + "data": { "boolean": true }, + "expected": "\"\"", + "template": "\"{{^boolean}}This should not be rendered.{{/boolean}}\"", + "desc": "Truthy sections should have their contents omitted." + }, + { + "name": "Context", + "data": { "context": { "name": "Joe" } }, + "expected": "\"\"", + "template": "\"{{^context}}Hi {{name}}.{{/context}}\"", + "desc": "Objects and hashes should behave like truthy values." + }, + { + "name": "List", + "data": { "list": [{ "n": 1 }, { "n": 2 }, { "n": 3 }] }, + "expected": "\"\"", + "template": "\"{{^list}}{{n}}{{/list}}\"", + "desc": "Lists should behave like truthy values." + }, + { + "name": "Empty List", + "data": { "list": [] }, + "expected": "\"Yay lists!\"", + "template": "\"{{^list}}Yay lists!{{/list}}\"", + "desc": "Empty lists should behave like falsey values." + }, + { + "name": "Doubled", + "data": { "two": "second", "bool": false }, + "expected": "* first\n* second\n* third\n", + "template": "{{^bool}}\n* first\n{{/bool}}\n* {{two}}\n{{^bool}}\n* third\n{{/bool}}\n", + "desc": "Multiple inverted sections per template should be permitted." + }, + { + "name": "Nested (Falsey)", + "data": { "bool": false }, + "expected": "| A B C D E |", + "template": "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", + "desc": "Nested falsey sections should have their contents rendered." + }, + { + "name": "Nested (Truthy)", + "data": { "bool": true }, + "expected": "| A E |", + "template": "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |", + "desc": "Nested truthy sections should be omitted." + }, + { + "name": "Context Misses", + "data": {}, + "expected": "[Cannot find key 'missing'!]", + "template": "[{{^missing}}Cannot find key 'missing'!{{/missing}}]", + "desc": "Failed context lookups should be considered falsey." + }, + { + "name": "Dotted Names - Truthy", + "data": { "a": { "b": { "c": true } } }, + "expected": "\"\" == \"\"", + "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"\"", + "desc": "Dotted names should be valid for Inverted Section tags." + }, + { + "name": "Dotted Names - Falsey", + "data": { "a": { "b": { "c": false } } }, + "expected": "\"Not Here\" == \"Not Here\"", + "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"", + "desc": "Dotted names should be valid for Inverted Section tags." + }, + { + "name": "Dotted Names - Broken Chains", + "data": { "a": {} }, + "expected": "\"Not Here\" == \"Not Here\"", + "template": "\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"", + "desc": "Dotted names that cannot be resolved should be considered falsey." + }, + { + "name": "Surrounding Whitespace", + "data": { "boolean": false }, + "expected": " | \t|\t | \n", + "template": " | {{^boolean}}\t|\t{{/boolean}} | \n", + "desc": "Inverted sections should not alter surrounding whitespace." + }, + { + "name": "Internal Whitespace", + "data": { "boolean": false }, + "expected": " | \n | \n", + "template": " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n", + "desc": "Inverted should not alter internal whitespace." + }, + { + "name": "Indented Inline Sections", + "data": { "boolean": false }, + "expected": " NO\n WAY\n", + "template": " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n", + "desc": "Single-line sections should not alter surrounding whitespace." + }, + { + "name": "Standalone Lines", + "data": { "boolean": false }, + "expected": "| This Is\n|\n| A Line\n", + "template": "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n", + "desc": "Standalone lines should be removed from the template." + }, + { + "name": "Standalone Indented Lines", + "data": { "boolean": false }, + "expected": "| This Is\n|\n| A Line\n", + "template": "| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n", + "desc": "Standalone indented lines should be removed from the template." + }, + { + "name": "Standalone Line Endings", + "data": { "boolean": false }, + "expected": "|\r\n|", + "template": "|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags." + }, + { + "name": "Standalone Without Previous Line", + "data": { "boolean": false }, + "expected": "^\n/", + "template": " {{^boolean}}\n^{{/boolean}}\n/", + "desc": "Standalone tags should not require a newline to precede them." + }, + { + "name": "Standalone Without Newline", + "data": { "boolean": false }, + "expected": "^\n/\n", + "template": "^{{^boolean}}\n/\n {{/boolean}}", + "desc": "Standalone tags should not require a newline to follow them." + }, + { + "name": "Padding", + "data": { "boolean": false }, + "expected": "|=|", + "template": "|{{^ boolean }}={{/ boolean }}|", + "desc": "Superfluous in-tag whitespace should be ignored." + } + ] } diff --git a/tests/unit/data/mustache_sections.json b/tests/unit/data/mustache_sections.json index 56279257f5..53936eda5a 100644 --- a/tests/unit/data/mustache_sections.json +++ b/tests/unit/data/mustache_sections.json @@ -1,282 +1,187 @@ { - "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", - "overview": "Section tags and End Section tags are used in combination to wrap a section\nof the template for iteration\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Section tag MUST be followed\nby an End Section tag with the same content within the same section.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nFor each element in the data list, the element MUST be pushed onto the\ncontext stack, the section MUST be rendered, and the element MUST be popped\noff the context stack.\n\nSection and End Section tags SHOULD be treated as standalone when\nappropriate.\n", - "tests": [ - { - "name": "Truthy", - "data": { - "boolean": true - }, - "expected": "\"This should be rendered.\"", - "template": "\"{{#boolean}}This should be rendered.{{/boolean}}\"", - "desc": "Truthy sections should have their contents rendered." - }, - { - "name": "Falsey", - "data": { - "boolean": false - }, - "expected": "\"\"", - "template": "\"{{#boolean}}This should not be rendered.{{/boolean}}\"", - "desc": "Falsey sections should have their contents omitted." - }, - { - "name": "Context", - "data": { - "context": { - "name": "Joe" - } - }, - "expected": "\"Hi Joe.\"", - "template": "\"{{#context}}Hi {{name}}.{{/context}}\"", - "desc": "Objects and hashes should be pushed onto the context stack." - }, - { - "name": "Deeply Nested Contexts", - "data": { - "a": { - "one": 1 - }, - "b": { - "two": 2 - }, - "c": { - "three": 3 - }, - "d": { - "four": 4 - }, - "e": { - "five": 5 - } - }, - "expected": "1\n121\n12321\n1234321\n123454321\n1234321\n12321\n121\n1\n", - "template": "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{#e}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}\n{{/e}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n", - "desc": "All elements on the context stack should be accessible." - }, - { - "name": "List", - "data": { - "list": [ - { - "item": 1 - }, - { - "item": 2 - }, - { - "item": 3 - } - ] - }, - "expected": "\"123\"", - "template": "\"{{#list}}{{item}}{{/list}}\"", - "desc": "Lists should be iterated; list items should visit the context stack." - }, - { - "name": "Empty List", - "data": { - "list": [] - }, - "expected": "\"\"", - "template": "\"{{#list}}Yay lists!{{/list}}\"", - "desc": "Empty lists should behave like falsey values." - }, - { - "name": "Doubled", - "data": { - "two": "second", - "bool": true - }, - "expected": "* first\n* second\n* third\n", - "template": "{{#bool}}\n* first\n{{/bool}}\n* {{two}}\n{{#bool}}\n* third\n{{/bool}}\n", - "desc": "Multiple sections per template should be permitted." - }, - { - "name": "Nested (Truthy)", - "data": { - "bool": true - }, - "expected": "| A B C D E |", - "template": "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", - "desc": "Nested truthy sections should have their contents rendered." - }, - { - "name": "Nested (Falsey)", - "data": { - "bool": false - }, - "expected": "| A E |", - "template": "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", - "desc": "Nested falsey sections should be omitted." - }, - { - "name": "Context Misses", - "data": {}, - "expected": "[]", - "template": "[{{#missing}}Found key 'missing'!{{/missing}}]", - "desc": "Failed context lookups should be considered falsey." - }, - { - "name": "Implicit Iterator - String", - "data": { - "list": [ - "a", - "b", - "c", - "d", - "e" - ] - }, - "expected": "\"(a)(b)(c)(d)(e)\"", - "template": "\"{{#list}}({{.}}){{/list}}\"", - "desc": "Implicit iterators should directly interpolate strings." - }, - { - "name": "Implicit Iterator - Integer", - "data": { - "list": [ - 1, - 2, - 3, - 4, - 5 - ] - }, - "expected": "\"(1)(2)(3)(4)(5)\"", - "template": "\"{{#list}}({{.}}){{/list}}\"", - "desc": "Implicit iterators should cast integers to strings and interpolate." - }, - { - "name": "Implicit Iterator - Decimal", - "data": { - "list": [ - 1.1, - 2.2, - 3.3, - 4.4, - 5.5 - ] - }, - "expected": "\"(1.10)(2.20)(3.30)(4.40)(5.50)\"", - "template": "\"{{#list}}({{.}}){{/list}}\"", - "desc": "Implicit iterators should cast decimals to strings and interpolate." - }, - { - "name": "Dotted Names - Truthy", - "data": { - "a": { - "b": { - "c": true - } - } - }, - "expected": "\"Here\" == \"Here\"", - "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"Here\"", - "desc": "Dotted names should be valid for Section tags." - }, - { - "name": "Dotted Names - Falsey", - "data": { - "a": { - "b": { - "c": false - } - } - }, - "expected": "\"\" == \"\"", - "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"", - "desc": "Dotted names should be valid for Section tags." - }, - { - "name": "Dotted Names - Broken Chains", - "data": { - "a": {} - }, - "expected": "\"\" == \"\"", - "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"", - "desc": "Dotted names that cannot be resolved should be considered falsey." - }, - { - "name": "Surrounding Whitespace", - "data": { - "boolean": true - }, - "expected": " | \t|\t | \n", - "template": " | {{#boolean}}\t|\t{{/boolean}} | \n", - "desc": "Sections should not alter surrounding whitespace." - }, - { - "name": "Internal Whitespace", - "data": { - "boolean": true - }, - "expected": " | \n | \n", - "template": " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n", - "desc": "Sections should not alter internal whitespace." - }, - { - "name": "Indented Inline Sections", - "data": { - "boolean": true - }, - "expected": " YES\n GOOD\n", - "template": " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n", - "desc": "Single-line sections should not alter surrounding whitespace." - }, - { - "name": "Standalone Lines", - "data": { - "boolean": true - }, - "expected": "| This Is\n|\n| A Line\n", - "template": "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n", - "desc": "Standalone lines should be removed from the template." - }, - { - "name": "Indented Standalone Lines", - "data": { - "boolean": true - }, - "expected": "| This Is\n|\n| A Line\n", - "template": "| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n", - "desc": "Indented standalone lines should be removed from the template." - }, - { - "name": "Standalone Line Endings", - "data": { - "boolean": true - }, - "expected": "|\r\n|", - "template": "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|", - "desc": "\"\\r\\n\" should be considered a newline for standalone tags." - }, - { - "name": "Standalone Without Previous Line", - "data": { - "boolean": true - }, - "expected": "#\n/", - "template": " {{#boolean}}\n#{{/boolean}}\n/", - "desc": "Standalone tags should not require a newline to precede them." - }, - { - "name": "Standalone Without Newline", - "data": { - "boolean": true - }, - "expected": "#\n/\n", - "template": "#{{#boolean}}\n/\n {{/boolean}}", - "desc": "Standalone tags should not require a newline to follow them." - }, - { - "name": "Padding", - "data": { - "boolean": true - }, - "expected": "|=|", - "template": "|{{# boolean }}={{/ boolean }}|", - "desc": "Superfluous in-tag whitespace should be ignored." - } - ] + "__ATTN__": "Do not edit this file; changes belong in the appropriate YAML file.", + "overview": "Section tags and End Section tags are used in combination to wrap a section\nof the template for iteration\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Section tag MUST be followed\nby an End Section tag with the same content within the same section.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nFor each element in the data list, the element MUST be pushed onto the\ncontext stack, the section MUST be rendered, and the element MUST be popped\noff the context stack.\n\nSection and End Section tags SHOULD be treated as standalone when\nappropriate.\n", + "tests": [ + { + "name": "Truthy", + "data": { "boolean": true }, + "expected": "\"This should be rendered.\"", + "template": "\"{{#boolean}}This should be rendered.{{/boolean}}\"", + "desc": "Truthy sections should have their contents rendered." + }, + { + "name": "Falsey", + "data": { "boolean": false }, + "expected": "\"\"", + "template": "\"{{#boolean}}This should not be rendered.{{/boolean}}\"", + "desc": "Falsey sections should have their contents omitted." + }, + { + "name": "Context", + "data": { "context": { "name": "Joe" } }, + "expected": "\"Hi Joe.\"", + "template": "\"{{#context}}Hi {{name}}.{{/context}}\"", + "desc": "Objects and hashes should be pushed onto the context stack." + }, + { + "name": "Deeply Nested Contexts", + "data": { + "a": { "one": 1 }, + "b": { "two": 2 }, + "c": { "three": 3 }, + "d": { "four": 4 }, + "e": { "five": 5 } + }, + "expected": "1\n121\n12321\n1234321\n123454321\n1234321\n12321\n121\n1\n", + "template": "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{#e}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}\n{{/e}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n", + "desc": "All elements on the context stack should be accessible." + }, + { + "name": "List", + "data": { "list": [{ "item": 1 }, { "item": 2 }, { "item": 3 }] }, + "expected": "\"123\"", + "template": "\"{{#list}}{{item}}{{/list}}\"", + "desc": "Lists should be iterated; list items should visit the context stack." + }, + { + "name": "Empty List", + "data": { "list": [] }, + "expected": "\"\"", + "template": "\"{{#list}}Yay lists!{{/list}}\"", + "desc": "Empty lists should behave like falsey values." + }, + { + "name": "Doubled", + "data": { "two": "second", "bool": true }, + "expected": "* first\n* second\n* third\n", + "template": "{{#bool}}\n* first\n{{/bool}}\n* {{two}}\n{{#bool}}\n* third\n{{/bool}}\n", + "desc": "Multiple sections per template should be permitted." + }, + { + "name": "Nested (Truthy)", + "data": { "bool": true }, + "expected": "| A B C D E |", + "template": "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", + "desc": "Nested truthy sections should have their contents rendered." + }, + { + "name": "Nested (Falsey)", + "data": { "bool": false }, + "expected": "| A E |", + "template": "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |", + "desc": "Nested falsey sections should be omitted." + }, + { + "name": "Context Misses", + "data": {}, + "expected": "[]", + "template": "[{{#missing}}Found key 'missing'!{{/missing}}]", + "desc": "Failed context lookups should be considered falsey." + }, + { + "name": "Implicit Iterator - String", + "data": { "list": ["a", "b", "c", "d", "e"] }, + "expected": "\"(a)(b)(c)(d)(e)\"", + "template": "\"{{#list}}({{.}}){{/list}}\"", + "desc": "Implicit iterators should directly interpolate strings." + }, + { + "name": "Implicit Iterator - Integer", + "data": { "list": [1, 2, 3, 4, 5] }, + "expected": "\"(1)(2)(3)(4)(5)\"", + "template": "\"{{#list}}({{.}}){{/list}}\"", + "desc": "Implicit iterators should cast integers to strings and interpolate." + }, + { + "name": "Implicit Iterator - Decimal", + "data": { "list": [1.1, 2.2, 3.3, 4.4, 5.5] }, + "expected": "\"(1.10)(2.20)(3.30)(4.40)(5.50)\"", + "template": "\"{{#list}}({{.}}){{/list}}\"", + "desc": "Implicit iterators should cast decimals to strings and interpolate." + }, + { + "name": "Dotted Names - Truthy", + "data": { "a": { "b": { "c": true } } }, + "expected": "\"Here\" == \"Here\"", + "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"Here\"", + "desc": "Dotted names should be valid for Section tags." + }, + { + "name": "Dotted Names - Falsey", + "data": { "a": { "b": { "c": false } } }, + "expected": "\"\" == \"\"", + "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"", + "desc": "Dotted names should be valid for Section tags." + }, + { + "name": "Dotted Names - Broken Chains", + "data": { "a": {} }, + "expected": "\"\" == \"\"", + "template": "\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"", + "desc": "Dotted names that cannot be resolved should be considered falsey." + }, + { + "name": "Surrounding Whitespace", + "data": { "boolean": true }, + "expected": " | \t|\t | \n", + "template": " | {{#boolean}}\t|\t{{/boolean}} | \n", + "desc": "Sections should not alter surrounding whitespace." + }, + { + "name": "Internal Whitespace", + "data": { "boolean": true }, + "expected": " | \n | \n", + "template": " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n", + "desc": "Sections should not alter internal whitespace." + }, + { + "name": "Indented Inline Sections", + "data": { "boolean": true }, + "expected": " YES\n GOOD\n", + "template": " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n", + "desc": "Single-line sections should not alter surrounding whitespace." + }, + { + "name": "Standalone Lines", + "data": { "boolean": true }, + "expected": "| This Is\n|\n| A Line\n", + "template": "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n", + "desc": "Standalone lines should be removed from the template." + }, + { + "name": "Indented Standalone Lines", + "data": { "boolean": true }, + "expected": "| This Is\n|\n| A Line\n", + "template": "| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n", + "desc": "Indented standalone lines should be removed from the template." + }, + { + "name": "Standalone Line Endings", + "data": { "boolean": true }, + "expected": "|\r\n|", + "template": "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|", + "desc": "\"\\r\\n\" should be considered a newline for standalone tags." + }, + { + "name": "Standalone Without Previous Line", + "data": { "boolean": true }, + "expected": "#\n/", + "template": " {{#boolean}}\n#{{/boolean}}\n/", + "desc": "Standalone tags should not require a newline to precede them." + }, + { + "name": "Standalone Without Newline", + "data": { "boolean": true }, + "expected": "#\n/\n", + "template": "#{{#boolean}}\n/\n {{/boolean}}", + "desc": "Standalone tags should not require a newline to follow them." + }, + { + "name": "Padding", + "data": { "boolean": true }, + "expected": "|=|", + "template": "|{{# boolean }}={{/ boolean }}|", + "desc": "Superfluous in-tag whitespace should be ignored." + } + ] } diff --git a/tests/unit/data/promise_duplicate_handle.cf b/tests/unit/data/promise_duplicate_handle.cf index 97363a8be6..4286b06bd6 100644 --- a/tests/unit/data/promise_duplicate_handle.cf +++ b/tests/unit/data/promise_duplicate_handle.cf @@ -2,8 +2,6 @@ bundle agent foo { reports: cfengine3:: - "hello world" - handle => "stuff"; - "quux" - handle => "stuff"; + "hello world" handle => "stuff"; + "quux" handle => "stuff"; } diff --git a/tests/unit/data/promiser_empty_varref.cf b/tests/unit/data/promiser_empty_varref.cf index 63b7053f77..4bb240a5c2 100644 --- a/tests/unit/data/promiser_empty_varref.cf +++ b/tests/unit/data/promiser_empty_varref.cf @@ -1,5 +1,5 @@ bundle agent foo { reports: - "$()"; + "$()"; } diff --git a/tests/unit/policy_test.c b/tests/unit/policy_test.c index 26ffea99ce..6d6db41a17 100644 --- a/tests/unit/policy_test.c +++ b/tests/unit/policy_test.c @@ -287,13 +287,13 @@ static void test_policy_json_offsets(void) { JsonElement *main_bundle = JsonArrayGetAsObject(json_bundles, 0); int line = JsonPrimitiveGetAsInteger(JsonObjectGet(main_bundle, "line")); - assert_int_equal(9, line); + assert_int_equal(8, line); JsonElement *json_promise_types = JsonObjectGetAsArray(main_bundle, "promiseTypes"); { JsonElement *json_reports_type = JsonArrayGetAsObject(json_promise_types, 0); line = JsonPrimitiveGetAsInteger(JsonObjectGet(json_reports_type, "line")); - assert_int_equal(11, line); + assert_int_equal(10, line); JsonElement *json_contexts = JsonObjectGetAsArray(json_reports_type, "contexts"); JsonElement *cf_context = JsonArrayGetAsObject(json_contexts, 0); @@ -301,13 +301,13 @@ static void test_policy_json_offsets(void) JsonElement *hello_cf_promise = JsonArrayGetAsObject(cf_context_promises, 0); line = JsonPrimitiveGetAsInteger(JsonObjectGet(hello_cf_promise, "line")); - assert_int_equal(13, line); + assert_int_equal(12, line); JsonElement *hello_cf_attribs = JsonObjectGetAsArray(hello_cf_promise, "attributes"); { JsonElement *friend_pattern_attrib = JsonArrayGetAsObject(hello_cf_attribs, 0); line = JsonPrimitiveGetAsInteger(JsonObjectGet(friend_pattern_attrib, "line")); - assert_int_equal(14, line); + assert_int_equal(12, line); } } } @@ -316,11 +316,11 @@ static void test_policy_json_offsets(void) { JsonElement *control_body = JsonArrayGetAsObject(json_bodies, 0); int line = JsonPrimitiveGetAsInteger(JsonObjectGet(control_body, "line")); - assert_int_equal(4, line); + assert_int_equal(3, line); JsonElement *myperms_body = JsonArrayGetAsObject(json_bodies, 1); line = JsonPrimitiveGetAsInteger(JsonObjectGet(myperms_body, "line")); - assert_int_equal(29, line); + assert_int_equal(27, line); JsonElement *myperms_contexts = JsonObjectGetAsArray(myperms_body, "contexts"); JsonElement *any_context = JsonArrayGetAsObject(myperms_contexts, 0); @@ -328,7 +328,7 @@ static void test_policy_json_offsets(void) { JsonElement *mode_attrib = JsonArrayGetAsObject(any_attribs, 0); line = JsonPrimitiveGetAsInteger(JsonObjectGet(mode_attrib, "line")); - assert_int_equal(31, line); + assert_int_equal(29, line); } }