8e389c03f8
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
202 lines
6.9 KiB
C
202 lines
6.9 KiB
C
/*
|
|
* p6_sort_probe.c — target-регрессия четырёх ключей сортировки панели.
|
|
*
|
|
* Проверяет реальный EMM-store и банковый sc_sort.c. Записи намеренно
|
|
* перемешаны; `..` и маркер DSS обязаны сохранять крайние позиции, а
|
|
* каталоги — оставаться перед файлами при любом направлении.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <dir.h>
|
|
#include <sprinter_mem.h>
|
|
#include "sc_panel.h"
|
|
#include "sc_sort.h"
|
|
#include "sc_store.h"
|
|
|
|
static int result_fd = -1;
|
|
static uint8_t failures;
|
|
|
|
static void log_text(const char *text)
|
|
{
|
|
const char *end = text;
|
|
uint16_t length;
|
|
|
|
while (*end != '\0') end++;
|
|
length = (uint16_t)(end - text);
|
|
if (result_fd >= 0) (void)write(result_fd, text, length);
|
|
}
|
|
|
|
static void check(uint8_t condition, const char *name)
|
|
{
|
|
log_text(condition ? "PASS " : "FAIL ");
|
|
if (!condition) failures++;
|
|
log_text(name);
|
|
log_text("\r\n");
|
|
}
|
|
|
|
/* sc_sort_set() вызывает общий panel-инвариант. В probe достаточно той же
|
|
* нормализации cursor/top без подтягивания файлового source-модуля. */
|
|
void sc_panel_normalize_view(ScPanel *panel)
|
|
{
|
|
if (panel->count == 0u) {
|
|
panel->cursor = 0u;
|
|
panel->top = 0u;
|
|
return;
|
|
}
|
|
if (panel->cursor >= panel->count) panel->cursor = panel->count - 1u;
|
|
if (panel->top > panel->cursor) panel->top = panel->cursor;
|
|
if (panel->cursor >= (uint16_t)(panel->top + 27u)) {
|
|
panel->top = (uint16_t)(panel->cursor - 26u);
|
|
}
|
|
}
|
|
|
|
static void put_entry(ScPanel *panel, uint16_t index, const char *name,
|
|
uint32_t size, uint16_t date, uint8_t attr,
|
|
uint8_t flags)
|
|
{
|
|
ScDirEntry entry;
|
|
|
|
memset(&entry, 0, sizeof(entry));
|
|
strcpy(entry.name, name);
|
|
entry.size = size;
|
|
entry.date = date;
|
|
entry.time = (uint16_t)(index << 5);
|
|
entry.attr = attr;
|
|
entry.flags = flags;
|
|
(void)sc_store_put(panel, index, &entry);
|
|
}
|
|
|
|
static void fill_panel(ScPanel *panel, uint8_t page)
|
|
{
|
|
memset(panel, 0, sizeof(*panel));
|
|
panel->phys_page = page;
|
|
panel->count = 8u;
|
|
panel->sort_key = SC_SORT_NAME;
|
|
put_entry(panel, 0u, "BETA.TXT", 20ul, 0x5621u, 0u, 0u);
|
|
put_entry(panel, 1u, ">>> MORE...", 0ul, 0u, 0u,
|
|
SC_ENTRY_DSS_LIMIT);
|
|
put_entry(panel, 2u, "ZDIR", 0ul, 0x5A21u, FA_DIREC, 0u);
|
|
put_entry(panel, 3u, "ALPHA.BIN", 50ul, 0x5A21u, 0u, 0u);
|
|
put_entry(panel, 4u, "..", 0ul, 0u, FA_DIREC,
|
|
SC_ENTRY_PARENT);
|
|
put_entry(panel, 5u, "GAMMA.TXT", 10ul, 0x5821u, 0u, 0u);
|
|
put_entry(panel, 6u, "ADIR", 0ul, 0x5421u, FA_DIREC, 0u);
|
|
put_entry(panel, 7u, "DELTA.EXE", 30ul, 0x5421u, 0u, 0u);
|
|
}
|
|
|
|
static uint8_t order_is(const ScPanel *panel, const char *const *names)
|
|
{
|
|
ScDirEntry entry;
|
|
uint16_t i;
|
|
|
|
for (i = 0u; i < panel->count; i++) {
|
|
if (sc_store_get(panel, i, &entry) != 0 ||
|
|
strcmp(entry.name, names[i]) != 0) return 0u;
|
|
}
|
|
return 1u;
|
|
}
|
|
|
|
static uint8_t selected_is(const ScPanel *panel, const char *name)
|
|
{
|
|
ScDirEntry entry;
|
|
return sc_store_get(panel, panel->cursor, &entry) == 0 &&
|
|
strcmp(entry.name, name) == 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
static const char *const name_up[] = {
|
|
"..", "ADIR", "ZDIR", "ALPHA.BIN", "BETA.TXT", "DELTA.EXE",
|
|
"GAMMA.TXT", ">>> MORE..."
|
|
};
|
|
static const char *const name_down[] = {
|
|
"..", "ZDIR", "ADIR", "GAMMA.TXT", "DELTA.EXE", "BETA.TXT",
|
|
"ALPHA.BIN", ">>> MORE..."
|
|
};
|
|
static const char *const ext_up[] = {
|
|
"..", "ADIR", "ZDIR", "ALPHA.BIN", "DELTA.EXE", "BETA.TXT",
|
|
"GAMMA.TXT", ">>> MORE..."
|
|
};
|
|
static const char *const ext_down[] = {
|
|
"..", "ZDIR", "ADIR", "GAMMA.TXT", "BETA.TXT", "DELTA.EXE",
|
|
"ALPHA.BIN", ">>> MORE..."
|
|
};
|
|
static const char *const size_up[] = {
|
|
"..", "ADIR", "ZDIR", "GAMMA.TXT", "BETA.TXT", "DELTA.EXE",
|
|
"ALPHA.BIN", ">>> MORE..."
|
|
};
|
|
static const char *const size_down[] = {
|
|
"..", "ZDIR", "ADIR", "ALPHA.BIN", "DELTA.EXE", "BETA.TXT",
|
|
"GAMMA.TXT", ">>> MORE..."
|
|
};
|
|
static const char *const date_up[] = {
|
|
"..", "ADIR", "ZDIR", "DELTA.EXE", "BETA.TXT", "GAMMA.TXT",
|
|
"ALPHA.BIN", ">>> MORE..."
|
|
};
|
|
static const char *const date_down[] = {
|
|
"..", "ZDIR", "ADIR", "ALPHA.BIN", "GAMMA.TXT", "BETA.TXT",
|
|
"DELTA.EXE", ">>> MORE..."
|
|
};
|
|
ScPanel left;
|
|
ScPanel right;
|
|
uint8_t block;
|
|
uint8_t left_page;
|
|
uint8_t right_page;
|
|
|
|
result_fd = open("D:\\SORTRES.TXT", O_WRONLY | O_CREAT | O_TRUNC);
|
|
block = mem_alloc_pages(2u);
|
|
if (block == 0u) {
|
|
log_text("FAIL EMM allocation\r\n");
|
|
if (result_fd >= 0) (void)close(result_fd);
|
|
return 1;
|
|
}
|
|
left_page = mem_get_page(block, 0u);
|
|
right_page = mem_get_page(block, 1u);
|
|
fill_panel(&left, left_page);
|
|
fill_panel(&right, right_page);
|
|
|
|
check(sc_sort_panel(&left) == 0 && order_is(&left, name_up),
|
|
"name ascending and fixed groups");
|
|
left.cursor = 4u; /* BETA.TXT в уже отсортированном списке. */
|
|
check(selected_is(&left, "BETA.TXT"),
|
|
"selection fixture points to BETA");
|
|
|
|
check(sc_sort_set(&left, SC_SORT_NAME) == 0 &&
|
|
left.sort_descending && order_is(&left, name_down),
|
|
"name descending toggle");
|
|
check(selected_is(&left, "BETA.TXT"),
|
|
"selection preserved after sort");
|
|
check(sc_sort_set(&left, SC_SORT_EXTENSION) == 0 &&
|
|
!left.sort_descending && order_is(&left, ext_up),
|
|
"extension ascending");
|
|
check(sc_sort_set(&left, SC_SORT_EXTENSION) == 0 &&
|
|
left.sort_descending && order_is(&left, ext_down),
|
|
"extension descending toggle");
|
|
check(sc_sort_set(&left, SC_SORT_SIZE) == 0 &&
|
|
!left.sort_descending && order_is(&left, size_up),
|
|
"size ascending");
|
|
check(sc_sort_set(&left, SC_SORT_SIZE) == 0 &&
|
|
left.sort_descending && order_is(&left, size_down),
|
|
"size descending toggle");
|
|
check(sc_sort_set(&left, SC_SORT_DATE) == 0 &&
|
|
!left.sort_descending && order_is(&left, date_up),
|
|
"date ascending");
|
|
check(sc_sort_set(&left, SC_SORT_DATE) == 0 &&
|
|
left.sort_descending && order_is(&left, date_down),
|
|
"date descending toggle");
|
|
|
|
check(sc_sort_set(&right, SC_SORT_EXTENSION) == 0 &&
|
|
right.sort_key == SC_SORT_EXTENSION && !right.sort_descending &&
|
|
left.sort_key == SC_SORT_DATE && left.sort_descending,
|
|
"panel sort modes are independent");
|
|
check(order_is(&right, ext_up), "second EMM page sorted independently");
|
|
|
|
mem_free_block(block);
|
|
if (failures == 0u) log_text("ALL PASS\r\n");
|
|
if (result_fd >= 0) (void)close(result_fd);
|
|
return failures;
|
|
}
|