// Usage:
#if 0 {
   #run,stallable {
      Test("thing", t => {
         Expect(some_condition, "error message: %", value);
      });

      Test("other thing", t => {
         Expect(other_condition, "error message: %", value);
      });

      // ...
   }
}

/// Test defines a new suite to be executed by the test runner.
///
/// See: Expect for more information.
///
///      Test("my_proc does what it should", t => {
///         value1 := my_proc(/* ... */);
///         Expect(value1 != 0, "my_proc returned zero!");
///
///         value2 := my_proc(/* .... */);
///         Expect(value2 > 0, "my_proc returned a negative number!");
///      });
///
Test :: (name: string, proc: (t: *void) -> (), loc := #caller_location) {
   // @note(judah): incredibly dumb way to get nicer test runs
   WriteString(SmallerPath(loc.fully_pathed_filename), ",");
   WriteNumber(loc.line_number);
   WriteString(": ", name, "... ");

   t: TestRun;
   proc(*t);

   if t.failed {
      WriteString("failed");
   }
   else {
      WriteString("ok");
   }

   WriteString(" (");
   WriteNumber(t.total_ok);
   WriteString("/");
   WriteNumber(t.total_expects);
   WriteString(")\n");
}

/// Expect checks the given condition, failing the current test if it is false.
///
/// Note: Expect must be called within a test. Additionally, it expects the test
/// parameter to be called 't'.
Expect :: (cond: bool, message := "", args: ..Any, loc := #caller_location) #expand {
   run := `t.(*TestRun);
   run.total_expects += 1;
   if cond {
      run.total_ok += 1;
      return;
   }

   msg := "expectation failed";
   if message.count != 0 {
      msg = basic.tprint(message, ..args);
   }

   run.failed = true;
   if #compile_time {
      CompileError(msg, loc = loc);
   }
   else {
      WriteStderrLocation(loc);
      WriteStderrString(": ", msg, "\n");
   }
}


#scope_file

TestRun :: struct {
   location:      Source_Code_Location;
   total_expects: s64;
   total_ok:      s64;
   failed:        bool;
}

SmallerPath :: (path: string) -> string {
   p := path;
   i := p.count - 1;
   found_first_slash := false;

   while i >= 0 {
      if p[i] == "/" {
         if found_first_slash {
            i += 1;
            break;
         }

         found_first_slash = true;
      }

      i -= 1;
   }

   if !found_first_slash {
      p = strings.path_filename(path);
   }
   else {
      p.count -= i;
      p.data  += i;
   }

   return p;
}

basic    :: #import "Basic"; // @future
strings  :: #import "String"; // @future
compiler :: #import "Compiler"; // @future
