#######################################################
#
# Check the syntax of all examples in examples directory
#
# Is this test failing? If you added an example, make sure you add the
# necessary bodies/bundles to mock_stdlib.cf
# To find out which example is failing, run in tests/acceptance:
# $ ./testall 04_examples/syntax/check_syntax.cf ; less test.log
#
#######################################################
body common control
{
  inputs => { "../../default.sub.cf" };
  bundlesequence => { default("$(this.promise_filename)") };
}

#######################################################
bundle agent init
{
  vars:
    "basedir" string => "$(G.cwd)/../../examples";
    "all_examples" slist => findfiles("$(basedir)/*.cf");
    "stdlib" string => "$(G.testdir)/inputs/lib/stdlib.cf";
    "mock" string => "$(this.promise_dirname)/../mock_stdlib.cf";

  methods:
    "stdlib_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/stdlib.cf"
      );

    "paths_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/paths.cf"
      );

    "users_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/users.cf"
      );

    "services_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/services.cf"
      );

    "packages_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/packages.cf"
      );

    "commands_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/commands.cf"
      );

    "files_symlink"
      usebundle => lib_maybe_symlink(
        "$(mock)", "$(G.testdir)/inputs/lib/files.cf"
      );

    DEBUG_LOTS::
      "$(this.bundle): will consider example $(all_examples)";
}

bundle agent lib_maybe_symlink(src, dst)
# Symlink a file, only if dst does not exist
# If dst already exists, assume it's correct (testing with masterfiles)
{
  classes:
    "file_missing" if => not(fileexists("$(dst)"));

  methods:
    file_missing::
      "symlink" usebundle => symlink("$(src)", "$(dst)");
}

bundle agent symlink(src, dst)
{
  vars:
    "dir" string => dirname("$(dst)");

  files:
    "$(dir)/." create => "true";
    "$(dst)" link_from => ln_s("$(src)");
}

body link_from ln_s(x)
{
  link_type => "symlink";
  source => "$(x)";
}

#######################################################
bundle agent test
{
  meta:
    # It's unrealistic to keep this test perfectly stable on all non-Linux platforms.
    # It would require examples that quickly get very cluttered.
    "test_skip_unsupported" string => "!linux";
    "description" string => "Syntax check each policy (.cf) file in examples";

  vars:
    "examples" slist => { @(init.all_examples) };
    "canon[$(examples)]" string => canonify($(examples));

  methods:
    "$(examples)" usebundle => test_example($(examples));

  reports:
    DEBUG::
      "$(this.bundle): found example with output $(examples)"
        if => "has_output_block_$(canon[$(examples)])";
}

bundle agent test_example(file)
{
  vars:
    "cfile" string => canonify($(file));

    "checker"
      # -c option is omitted, because it enforces bundlesequence / main
      string => "$(sys.cf_promises) --workdir=$(G.testdir)";

  classes:
    "failure_$(cfile)"
      not => returnszero("$(checker) $(file)", "noshell"),
      scope => "namespace";

  reports:
    DEBUG::
      "Syntax error in example: '$(file)'"
        if => "failure_$(cfile)",
        handle => "reported_error_$(cfile)";

  methods:
    "verbose_$(cfile)"
      usebundle => run_with_output("$(checker) $(file)"),
      if => "failure_$(cfile)",
      depends_on => { "reported_error_$(cfile)" };
}

bundle agent run_with_output(runme)
# This bundle is only used in case of failure
# It reruns a command, as a commands promise, which will show the output
# from the failing cf-promises command.
{
  vars:
    "c" string => canonify("$(runme)");

  reports:
    "Running: $(runme)" handle => "printed_$(c)";

  commands:
    "$(runme)" depends_on => { "printed_$(c)" };
}

#######################################################
bundle agent check
{
  classes:
    "ok" not => classmatch("failure_.*");

  reports:
    ok::
      "$(this.promise_filename) Pass";

    !ok::
      "$(this.promise_filename) FAIL";
}
