---
:cmock:
  :enforce_strict_ordering: 1
  :plugins:
  - :array
  - :cexception
  - :ignore
  - :callback
  - :return_thru_ptr
  - :ignore_arg
  - :expect_any_args
  :callback_after_arg_check: true
  :callback_include_count: false
  :treat_externs: :include
  :treat_inlines: :include

:systest:
  :types: |
    typedef struct _POINT_T {
      int x;
      int y;
    } POINT_T;

  :mockable: |
    #include  "CException.h"
    extern void foo(POINT_T* a);
    POINT_T* bar(void);
    void fooa(POINT_T a[]);
    void foos(const char * a);
    const char * bars(void);
    void no_pointers(int a, const char* b);
    int mixed(int a, int* b, int c);
    void no_args(void);

  :source:
    :header: |
      #include "CException.h"
      void function_a(void);
      void function_b(void);
      void function_c(void);
      int function_d(void);
      void function_e(void);
      int function_f(void);

    :code: |
      void function_a(void)
      {
        foo(bar());
      }

      void function_b(void) {
        fooa(bar());
      }

      void function_c(void) {
        CEXCEPTION_T e;
        Try {
          foos(bars());
        } Catch(e) { foos("err"); }
      }

      int function_d(void) {
        int test_list[] = { 1, 2, 3, 4, 5 };
        no_pointers(1, "silly");
        return mixed(6, test_list, 7);
      }

      void function_e(void) {
        foos("Hello");
        foos("Tuna");
        foos("Oranges");
      }

      int function_f(void) {
        int a = 1;
        int b = 2;
        int c;
        POINT_T* pt = bar();
        c = pt->x;
        c = mixed(a, &b, c);
        return b + c;
      }

  :tests:
    :common: |
      #include "CException.h"
      void setUp(void) {}
      void tearDown(void) {}
      void my_foo_callback(POINT_T* a) { TEST_ASSERT_EQUAL_INT(2, a->x); }
      int  my_mixed_callback(int a, int* b, int c) { return a + *b + c; }

    :units:
    - :pass: TRUE
      :should: 'handle the situation where we pass nulls to pointers'
      :code: |
        test()
        {
          bar_ExpectAndReturn(NULL);
          foo_Expect(NULL);

          function_a();
        }

    - :pass: FALSE
      :should: 'handle the situation where we expected nulls to pointers but did not get that'
      :code: |
        test()
        {
          POINT_T pt = {1, 2};
          bar_ExpectAndReturn(&pt);
          foo_Expect(NULL);

          function_a();
        }

    - :pass: FALSE
      :should: 'handle the situation where we did not expect nulls to pointers but got null'
      :code: |
        test()
        {
          POINT_T ex = {1, 2};
          bar_ExpectAndReturn(NULL);
          foo_Expect(&ex);

          function_a();
        }

    - :pass: FALSE
      :should: 'handle the situation where we pass single object with expect and it is wrong'
      :code: |
        test()
        {
          POINT_T pt = {1, 2};
          POINT_T ex = {1, 3};
          bar_ExpectAndReturn(&pt);
          foo_Expect(&ex);

          function_a();
        }

    - :pass: TRUE
      :should: 'handle the situation where we pass single object with expect and use array handler'
      :code: |
        test()
        {
          POINT_T pt = {1, 2};
          POINT_T ex = {1, 2};
          bar_ExpectAndReturn(&pt);
          foo_ExpectWithArray(&ex, 1);

          function_a();
        }

    - :pass: FALSE
      :should: 'handle the situation where we pass single object with expect and use array handler and it is wrong'
      :code: |
        test()
        {
          POINT_T pt = {1, 2};
          POINT_T ex = {1, 3};
          bar_ExpectAndReturn(&pt);
          foo_ExpectWithArray(&ex, 1);

          function_a();
        }

    - :pass: TRUE
      :should: 'handle the situation where we pass multiple objects with expect and use array handler'
      :code: |
        test()
        {
          POINT_T pt[] = {{1, 2}, {3, 4}, {5, 6}};
          POINT_T ex[] = {{1, 2}, {3, 4}, {5, 6}};
          bar_ExpectAndReturn(pt);
          foo_ExpectWithArray(ex, 3);

          function_a();
        }

    - :pass: FALSE
      :should: 'handle the situation where we pass multiple objects with expect and use array handler and it is wrong at end'
      :code: |
        test()
        {
          POINT_T pt[] = {{1, 2}, {3, 4}, {5, 6}};
          POINT_T ex[] = {{1, 2}, {3, 4}, {5, 9}};
          bar_ExpectAndReturn(pt);
          foo_ExpectWithArray(ex, 3);

          function_a();
        }

    - :pass: TRUE
      :should: 'handle the situation where we pass single array element with expect'
      :code: |
        test()
        {
          POINT_T pt = {1, 2};
          POINT_T ex = {1, 2};
          bar_ExpectAndReturn(&pt);
          fooa_Expect(&ex);

          function_b();
        }

    - :pass: TRUE
      :should: 'handle standard c string as null terminated and not do crappy memory compares of a byte, passing'
      :code: |
        test()
        {
          const char* retval = "This is a\0 silly string";
          bars_ExpectAndReturn((char*)retval);
          foos_Expect("This is a\0 wacky string");

          function_c();
        }

    - :pass: FALSE
      :should: 'handle standard c string as null terminated and not do crappy memory compares of a byte, finding failures'
      :code: |
        test()
        {
          const char* retval = "This is a silly string";
          bars_ExpectAndReturn((char*)retval);
          foos_Expect("This is a wacky string");

          function_c();
        }

    - :pass: TRUE
      :should: 'handle creating array expects when we have mixed arguments for single object'
      :code: |
        test()
        {
          int expect_list[] = { 1, 9 };
          no_pointers_Expect(1, "silly");
          mixed_ExpectAndReturn(6, expect_list, 7, 13);

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: FALSE
      :should: 'handle creating array expects when we have mixed arguments and handle failures for single object'
      :code: |
        test()
        {
          int expect_list[] = { 9, 1 };
          no_pointers_Expect(1, "silly");
          mixed_ExpectAndReturn(6, expect_list, 7, 13);

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: TRUE
      :should: 'handle creating array expects when we have mixed arguments for multiple objects'
      :code: |
        test()
        {
          int expect_list[] = { 1, 2, 3, 4, 6 };
          no_pointers_Expect(1, "silly");
          mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13);

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: FALSE
      :should: 'handle creating array expects when we have mixed arguments and handle failures for multiple objects'
      :code: |
        test()
        {
          int expect_list[] = { 1, 2, 3, 4, 6 };
          no_pointers_Expect(1, "silly");
          mixed_ExpectWithArrayAndReturn(6, expect_list, 5, 7, 13);

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: TRUE
      :should: 'handle an exception being caught'
      :code: |
        test()
        {
          const char* retval = "This is a\0 silly string";
          bars_ExpectAndReturn((char*)retval);
          foos_ExpectAndThrow("This is a\0 wacky string", 55);
          foos_Expect("err");

          function_c();
        }

    - :pass: FALSE
      :should: 'handle an exception being caught but still catch following errors'
      :code: |
        test()
        {
          const char* retval = "This is a\0 silly string";
          bars_ExpectAndReturn((char*)retval);
          foos_ExpectAndThrow("This is a\0 wacky string", 55);
          foos_Expect("wrong error");

          function_c();
        }

    - :pass: FALSE
      :should: 'fail strict ordering problems even though we would otherwise have passed'
      :code: |
        test()
        {
          int expect_list[] = { 1, 2, 3, 4, 6 };
          mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13);
          no_pointers_Expect(1, "silly");

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: TRUE
      :should: 'that we can properly ignore last  function but the other will work properly'
      :code: |
        test()
        {
          int expect_list[] = { 1, 2, 3, 4, 6 };
          mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13);
          no_pointers_Ignore();

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: TRUE
      :should: 'that we can properly ignore first function but the other will work properly'
      :code: |
        test()
        {
          mixed_IgnoreAndReturn(13);
          no_pointers_Expect(1, "silly");

          TEST_ASSERT_EQUAL(13, function_d());
        }

    - :pass: TRUE
      :should: 'that we just have to ignore a call once because we are not counting calls'
      :code: |
        test()
        {
          foos_Ignore();

          function_e();
        }

    - :pass: TRUE
      :should: 'that we can use a callback and an expect'
      :code: |
        test()
        {
          POINT_T pt1 = {2, 3};
          POINT_T pt2 = {2, 3};
          bar_ExpectAndReturn(&pt1);
          foo_Expect(&pt2);
          foo_StubWithCallback((CMOCK_foo_CALLBACK)my_foo_callback);

          function_a();
        }

    - :pass: FALSE
      :should: 'that we can fail even when using a callback if we want to expect call but did not and we are checking that'
      :code: |
        test()
        {
          POINT_T pt = {2, 3};
          bar_ExpectAndReturn(&pt);
          foo_StubWithCallback((CMOCK_foo_CALLBACK)my_foo_callback);

          function_a();
        }

    - :pass: FALSE
      :should: 'that we can fail even when using a callback if args are wrong and we are checking those'
      :code: |
        test()
        {
          POINT_T pt1 = {2, 3};
          POINT_T pt2 = {1, 3};
          bar_ExpectAndReturn(&pt1);
          foo_Expect(&pt2);
          foo_StubWithCallback((CMOCK_foo_CALLBACK)my_foo_callback);

          function_a();
        }

    - :pass: FALSE
      :should: 'that we can fail from the callback itself'
      :code: |
        test()
        {
          POINT_T pt = {3, 3};
          bar_ExpectAndReturn(&pt);
          foo_Expect(&pt);
          foo_StubWithCallback((CMOCK_foo_CALLBACK)my_foo_callback);

          function_a();
        }

    - :pass: TRUE
      :should: 'that a callback return value overrides the one from ExpectAndReturn'
      :code: |
        test()
        {
          int b = 2;
          POINT_T pt1 = {3, 4};
          bar_ExpectAndReturn(&pt1);
          mixed_StubWithCallback((CMOCK_mixed_CALLBACK)my_mixed_callback);
          mixed_ExpectAndReturn(1,&b,3,100);

          TEST_ASSERT_EQUAL(8, function_f());
        }

    - :pass: TRUE
      :should: 'that a callback return value overrides the one from ExpectAndReturn AND ReturnThruPtr still works'
      :code: |
        test()
        {
          int b_in = 2;
          int b_out = 3;
          POINT_T pt1 = {3, 4};
          bar_ExpectAndReturn(&pt1);
          mixed_StubWithCallback((CMOCK_mixed_CALLBACK)my_mixed_callback);
          mixed_ExpectAndReturn(1,&b_in,3,100);
          mixed_ReturnThruPtr_b(&b_out);

          TEST_ASSERT_EQUAL(9, function_f()); // (a=1, bin=2, c=pt.x=3, bout=3, sum=9)
        }

    - :pass: TRUE
      :should: 'that a callback return value overrides the one from ExpectAnyArgs'
      :code: |
        test()
        {
          POINT_T pt1 = {5, 4};
          bar_ExpectAndReturn(&pt1);
          mixed_StubWithCallback((CMOCK_mixed_CALLBACK)my_mixed_callback);
          mixed_ExpectAnyArgsAndReturn(100);

          TEST_ASSERT_EQUAL(10, function_f()); // (a=1, bin=2, c=pt.x=5, bout=2, sum=10)
        }

    - :pass: TRUE
      :should: 'that a callback return value overrides the one from ExpectAnyArgs AND ReturnThruPtr still works'
      :code: |
        test()
        {
          int b_out = 3;
          POINT_T pt1 = {5, 4};
          bar_ExpectAndReturn(&pt1);
          mixed_StubWithCallback((CMOCK_mixed_CALLBACK)my_mixed_callback);
          mixed_ExpectAnyArgsAndReturn(100);
          mixed_ReturnThruPtr_b(&b_out);

          TEST_ASSERT_EQUAL(11, function_f()); // (a=1, bin=2, c=pt.x=5, bout=3, sum=11)
        }

...
