//-------------------------------------------------
//          TestSuite: Shared Acccess
//-------------------------------------------------

#include "lcd.h"
#include "util.h"
#include "os_core.h"
#include "os_scheduler.h"
#include "os_memory.h"

#define NUM_MAL 5
#define SIZE 10

#if (NUM_MAL * SIZE > 255)
  #error Reduce SIZE or NUM_MAL
#endif
#if (SIZE < 4)
  #error Chunks must at least be 4 Bytes
#endif

#define DELAY 100

#define DRIVER extHeap

PROGRAM(1, AUTOSTART) {
  MemAddr allocs[NUM_MAL];
  int errors = 0;
  int i, j;
  lcd_writeProgString(PSTR("1/7 Allocating ..."));
  delayMs(10*DELAY);
  for (i = 0; i < NUM_MAL; i++) {
    lcd_clear();
    lcd_writeDec(i);
    allocs[i] = os_sh_malloc(DRIVER, 10);
    bool different = true;
    for (j = 0; different && j < i; j++) {
      different = allocs[j] != allocs[i];
    }
    lcd_writeChar(' ');
    if (different) {
      lcd_writeProgString(PSTR("OK"));
    } else {
      errors++;
      os_error("FAILURE");
    }
    delayMs(1*DELAY);
  }
  lcd_clear();
  lcd_writeProgString(PSTR("2/7 Accessing (open/close) ..."));
  delayMs(10*DELAY);
  for (i = 0; i < NUM_MAL; i++) {
    lcd_clear();
    lcd_writeDec(i);
    lcd_writeChar(' ');
    for (j = 0; j < SIZE; j++) {
      MemAddr const p = allocs[i] + j;
      os_sh_writeOpen(DRIVER, &p);
      os_sh_close(DRIVER, &p);
      os_sh_readOpen(DRIVER, &p);
      os_sh_close(DRIVER, &p);
    }
    lcd_writeProgString(PSTR("OK"));
    delayMs(1*DELAY);
  }
  lcd_clear();
  lcd_writeProgString(PSTR("3/7 Accessing (write) ..."));
  delayMs(10*DELAY);
  for (i = 0; i < NUM_MAL; i++) {
    lcd_clear();
    lcd_writeDec(i);
    lcd_writeChar(' ');
    for (j = 0; j < SIZE; j++) {
      MemValue const a = i*SIZE + j;
      MemAddr const p = allocs[i] + j;
      os_sh_write(DRIVER, &p, 0, &a, 1);
    }
    lcd_writeProgString(PSTR("OK"));
    delayMs(1*DELAY);
  }
  lcd_clear();
  lcd_writeProgString(PSTR("4/7 Accessing (read) ..."));
  delayMs(10*DELAY);
  for (i = 0; i < NUM_MAL; i++) {
    lcd_clear();
    lcd_writeDec(i);
    lcd_writeChar(' ');
    MemValue a;
    for (j = 0; j < SIZE; j++) {
      MemAddr const p = allocs[i];
      a = (MemValue)-1;
      os_sh_read(DRIVER, &p, j, &a, 1);
      if (a != i*SIZE + j) {
        lcd_writeProgString(PSTR("FAILURE @ "));
        lcd_writeDec(j);
        lcd_writeChar('/');
        lcd_writeDec(SIZE);
        errors++;
        os_error("");
      }
    }
    lcd_writeProgString(PSTR("OK"));
    delayMs(1*DELAY);
  }

  lcd_clear();
  lcd_writeProgString(PSTR("5/7 Provoking viol. (read) ..."));
  delayMs(10*DELAY);
  {
    lcd_clear();
    lcd_writeChar('1');
    lcd_writeChar(' ');
    MemAddr const p = allocs[1]+1;
    uint32_t a;
    os_sh_read(DRIVER, &p, SIZE-3, (MemValue*)&a, 4);
    delayMs(5*DELAY);
    lcd_writeProgString(PSTR("OK (if error)"));
    delayMs(5*DELAY);
  }

  lcd_clear();
  lcd_writeProgString(PSTR("6/7 Provoking viol. (write) ..."));
  delayMs(10*DELAY);
  {
    lcd_clear();
    lcd_writeChar('1');
    lcd_writeChar(' ');
    MemAddr const p = allocs[1]+2;
    MemValue const a = 0xFF;
    os_sh_write(DRIVER, &p, SIZE-2, &a, 1);
    delayMs(5*DELAY);
    lcd_writeProgString(PSTR("OK (if error)"));
    delayMs(5*DELAY);
  }

  lcd_clear();
  lcd_writeProgString(PSTR("7/7 Checking ..."));
  delayMs(10*DELAY);
  {
    lcd_clear();
    lcd_writeChar('1');
    lcd_writeChar(' ');
    MemAddr const p = allocs[2];
    MemValue a = 0xBB;
    os_sh_read(DRIVER, &p, 0, &a, 1);
    delayMs(2*DELAY);
    if (a == 2*SIZE + 0) {
      lcd_writeProgString(PSTR("OK"));
    } else {
      lcd_writeProgString(PSTR("FAILURE @ Checking"));
      errors++;
      os_error("FAILURE @ Checking");
    }
  }
  delayMs(8*DELAY);
  lcd_clear();
  if (errors) {
    lcd_writeDec(errors);
    lcd_writeProgString(PSTR(" errors!"));
  } else {
    lcd_writeProgString(PSTR("All tests passed"));
  }
  delayMs(8*DELAY);
}
