8e389c03f8
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
163 lines
6.0 KiB
C
163 lines
6.0 KiB
C
/*
|
|
* p7_select_probe.c — target-регрессия выбора записей панели.
|
|
*
|
|
* Проверяет Insert/invert через настоящий EMM-store, статистику выбора и
|
|
* запрет синтетических записей без зависимости от UI-таймингов.
|
|
*/
|
|
|
|
#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_select.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;
|
|
while (*end != '\0') end++;
|
|
if (result_fd >= 0) {
|
|
(void)write(result_fd, text, (uint16_t)(end - text));
|
|
}
|
|
}
|
|
|
|
static void check(uint8_t condition, const char *name)
|
|
{
|
|
log_text(condition ? "PASS " : "FAIL ");
|
|
if (!condition) failures++;
|
|
log_text(name);
|
|
log_text("\r\n");
|
|
}
|
|
|
|
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, uint8_t attr, uint8_t flags)
|
|
{
|
|
ScDirEntry entry;
|
|
memset(&entry, 0, sizeof(entry));
|
|
strcpy(entry.name, name);
|
|
entry.size = size;
|
|
entry.attr = attr;
|
|
entry.flags = flags;
|
|
(void)sc_store_put(panel, index, &entry);
|
|
}
|
|
|
|
static uint8_t selected(const ScPanel *panel, uint16_t index)
|
|
{
|
|
ScDirEntry entry;
|
|
return sc_store_get(panel, index, &entry) == 0 &&
|
|
(entry.flags & SC_ENTRY_SELECTED) != 0u;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
ScPanel panel;
|
|
uint16_t total_before = 0u;
|
|
uint16_t free_before = 0u;
|
|
uint16_t total_after = 0u;
|
|
uint16_t free_after = 0u;
|
|
uint8_t block;
|
|
uint8_t page;
|
|
|
|
result_fd = open("D:\\SELRES.TXT", O_WRONLY | O_CREAT | O_TRUNC);
|
|
mem_info(&total_before, &free_before);
|
|
block = mem_alloc_pages(1u);
|
|
page = block ? mem_get_page(block, 0u) : 0u;
|
|
if (!block || !page) {
|
|
log_text("FAIL EMM allocation\r\n");
|
|
if (result_fd >= 0) (void)close(result_fd);
|
|
return 1;
|
|
}
|
|
|
|
memset(&panel, 0, sizeof(panel));
|
|
panel.phys_page = page;
|
|
panel.count = 5u;
|
|
put_entry(&panel, 0u, "..", 0ul, FA_DIREC, SC_ENTRY_PARENT);
|
|
put_entry(&panel, 1u, "ADIR", 0ul, FA_DIREC, 0u);
|
|
put_entry(&panel, 2u, "A.BIN", 10ul, 0u, 0u);
|
|
put_entry(&panel, 3u, "B.BIN", 20ul, 0u, 0u);
|
|
put_entry(&panel, 4u, ">>> MORE...", 0ul, 0u, SC_ENTRY_DSS_LIMIT);
|
|
|
|
check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 1 &&
|
|
panel.cursor == 0u &&
|
|
panel.selected_count == 0u, "parent cannot be selected");
|
|
|
|
panel.cursor = 2u;
|
|
check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 &&
|
|
panel.cursor == 3u &&
|
|
panel.selected_count == 1u && panel.selected_size == 10ul &&
|
|
selected(&panel, 2u), "Insert selects file and advances");
|
|
check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 &&
|
|
panel.cursor == 4u &&
|
|
panel.selected_count == 2u && panel.selected_size == 30ul &&
|
|
selected(&panel, 3u), "second Insert accumulates size");
|
|
check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 1 &&
|
|
panel.cursor == 4u &&
|
|
panel.selected_count == 2u && !selected(&panel, 4u),
|
|
"DSS marker cannot be selected");
|
|
|
|
check(sc_select_apply(&panel, SC_SELECT_INVERT, 0) == 0 &&
|
|
panel.selected_count == 0u && panel.selected_size == 0ul &&
|
|
!selected(&panel, 1u) && !selected(&panel, 2u) &&
|
|
!selected(&panel, 3u), "invert changes files only");
|
|
check(sc_select_apply(&panel, SC_SELECT_INVERT, 0) == 0 &&
|
|
panel.selected_count == 2u && panel.selected_size == 30ul &&
|
|
!selected(&panel, 1u) && selected(&panel, 2u) &&
|
|
selected(&panel, 3u), "second invert restores file selection");
|
|
|
|
panel.cursor = 1u;
|
|
check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 &&
|
|
panel.selected_count == 3u && panel.selected_size == 30ul &&
|
|
selected(&panel, 1u), "individual Insert may select directory");
|
|
check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "*") == 0 &&
|
|
panel.selected_count == 1u && panel.selected_size == 0ul &&
|
|
selected(&panel, 1u), "clear group ignores selected directory");
|
|
check(sc_select_apply(&panel, SC_SELECT_MASK_SET, "*.bin") == 0 &&
|
|
panel.selected_count == 3u && panel.selected_size == 30ul &&
|
|
selected(&panel, 1u) &&
|
|
selected(&panel, 2u) && selected(&panel, 3u),
|
|
"mask is ASCII case insensitive");
|
|
check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "?.b?n") == 0 &&
|
|
panel.selected_count == 1u && panel.selected_size == 0ul &&
|
|
selected(&panel, 1u),
|
|
"question wildcard clears matching files");
|
|
check(sc_select_apply(&panel, SC_SELECT_MASK_SET, "*.*") == 0 &&
|
|
panel.selected_count == 3u && panel.selected_size == 30ul &&
|
|
selected(&panel, 1u), "DOS star-dot-star selects all files only");
|
|
check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "A*") == 0 &&
|
|
panel.selected_count == 2u && panel.selected_size == 20ul &&
|
|
selected(&panel, 1u) && !selected(&panel, 2u) &&
|
|
selected(&panel, 3u), "group mask never changes directory flag");
|
|
|
|
sc_store_clear(&panel);
|
|
check(panel.count == 0u && panel.selected_count == 0u &&
|
|
panel.selected_size == 0ul, "refresh clear resets selection totals");
|
|
|
|
mem_free_block(block);
|
|
mem_info(&total_after, &free_after);
|
|
check(total_before == total_after && free_before == free_after,
|
|
"EMM restored");
|
|
if (failures == 0u) log_text("ALL PASS\r\n");
|
|
if (result_fd >= 0) (void)close(result_fd);
|
|
return failures;
|
|
}
|