adding unit tests for the timer command

This commit is contained in:
2025-10-29 23:18:34 +01:00
parent d388e6f90a
commit 86015a85e0
5 changed files with 101 additions and 66 deletions

View File

@@ -6,6 +6,7 @@ LIBS = -lm -lncurses
SRC_DIR = src
INC_DIR = include
BUILD_DIR = build
TEST_DIR = tests
# Get all .c files recursively from src directory and subdirectories
SOURCES = $(shell find $(SRC_DIR) -name "*.c")
@@ -13,6 +14,9 @@ SOURCES = $(shell find $(SRC_DIR) -name "*.c")
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
TARGET = shell
# Test targets
TEST_TIMER = $(TEST_DIR)/test_timer
all: $(BUILD_DIR) $(TARGET)
$(BUILD_DIR):
@@ -29,4 +33,18 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
clean:
rm -rf $(BUILD_DIR) $(TARGET)
.PHONY: all clean
# Test targets
test: test_timer
test_timer: $(TEST_TIMER)
@echo "\n--- Running timer tests ---"
@./$(TEST_TIMER)
$(TEST_TIMER): $(TEST_DIR)/test_timer.c $(TEST_DIR)/timer_testable.c
@mkdir -p $(TEST_DIR)
$(CC) $(CFLAGS) $^ -o $@
clean_tests:
rm -f $(TEST_TIMER)
.PHONY: all clean test test_timer clean_tests

View File

@@ -8,7 +8,7 @@
#define MAX_FILES 100
#define MAX_FILENAME_LEN 256
#define MAX_FULL_FILE_LINES 2000
#define MAX_COMMITS 50
#define MAX_COMMITS 1000
#define MAX_COMMIT_TITLE_LEN 256
#define MAX_AUTHOR_INITIALS 3
#define MAX_STASHES 100
@@ -92,8 +92,9 @@ typedef struct {
int file_line_count;
int file_scroll_offset;
int file_cursor_line;
NCursesCommit commits[MAX_COMMITS];
NCursesCommit *commits;
int commit_count;
int commit_capacity;
int selected_commit;
int commit_scroll_offset;
NCursesStash stashes[MAX_STASHES];

View File

@@ -1,13 +1,10 @@
#ifndef TIMER_H
#define TIMER_H
#include "common.h"
void format_time(double ms, char *buffer, size_t buffer_size);
int lsh_timer(char **args);
int lsh_time(char **args);
#endif // TIMER_H

View File

@@ -97,10 +97,15 @@ int init_ncurses_diff_viewer(NCursesDiffViewer *viewer) {
memset(viewer, 0, sizeof(NCursesDiffViewer));
// Initialize fuzzy search
viewer->commit_capacity = MAX_COMMITS;
viewer->commits = malloc(viewer->commit_capacity * sizeof(NCursesCommit));
if (!viewer->commits) {
fprintf(stderr, "Failed to allocate memory for commits\n");
return 0;
}
init_fuzzy_search(viewer);
// Initialize grep search
init_grep_search(viewer);
viewer->commit_scroll_offset = 0;
@@ -308,7 +313,6 @@ int is_ncurses_new_file(const char *filename) {
return !is_tracked; // Return 1 if not tracked (new file)
}
int load_file_with_staging_info(NCursesDiffViewer *viewer,
const char *filename) {
if (!viewer || !filename)
@@ -459,7 +463,6 @@ int load_file_with_staging_info(NCursesDiffViewer *viewer,
return viewer->file_line_count;
}
// this is a change
// this is another change
int stage_hunk_by_line(NCursesDiffViewer *viewer, int line_index) {
@@ -760,7 +763,6 @@ void rebuild_staged_view_from_git(NCursesDiffViewer *viewer) {
pclose(diff_fp);
}
int apply_staged_changes(NCursesDiffViewer *viewer) {
if (!viewer || viewer->staged_line_count == 0)
return 0;
@@ -807,7 +809,6 @@ int apply_staged_changes(NCursesDiffViewer *viewer) {
return (result == 0);
}
int unstage_line_from_git(NCursesDiffViewer *viewer, int staged_line_index) {
if (!viewer || staged_line_index < 0 ||
staged_line_index >= viewer->staged_line_count)
@@ -844,7 +845,6 @@ int unstage_line_from_git(NCursesDiffViewer *viewer, int staged_line_index) {
return 0;
}
int reset_staged_changes(NCursesDiffViewer *viewer) {
if (!viewer)
return 0;
@@ -889,8 +889,7 @@ int get_commit_history(NCursesDiffViewer *viewer) {
if (!viewer)
return 0;
FILE *fp =
popen("git log --oneline -20 --format=\"%h|%an|%s\" 2>/dev/null", "r");
FILE *fp = popen("git log --oneline --format=\"%h|%an|%s\" 2>/dev/null", "r");
if (!fp)
return 0;
@@ -898,14 +897,19 @@ int get_commit_history(NCursesDiffViewer *viewer) {
char line[512];
// Get list of unpushed commits first
char unpushed_hashes[MAX_COMMITS][16];
char **unpushed_hashes = malloc(1000 * sizeof(char *));
int unpushed_count = 0;
int unpushed_capacity = 1000;
for (int i = 0; i < unpushed_capacity; i++) {
unpushed_hashes[i] = malloc(16);
}
FILE *unpushed_fp =
popen("git log origin/HEAD..HEAD --format=\"%h\" 2>/dev/null", "r");
if (unpushed_fp) {
while (fgets(line, sizeof(line), unpushed_fp) != NULL &&
unpushed_count < MAX_COMMITS) {
unpushed_count < unpushed_capacity) {
char *newline = strchr(line, '\n');
if (newline)
*newline = '\0';
@@ -924,7 +928,7 @@ int get_commit_history(NCursesDiffViewer *viewer) {
popen("git log origin/main..HEAD --format=\"%h\" 2>/dev/null", "r");
if (unpushed_fp) {
while (fgets(line, sizeof(line), unpushed_fp) != NULL &&
unpushed_count < MAX_COMMITS) {
unpushed_count < unpushed_capacity) {
char *newline = strchr(line, '\n');
if (newline)
*newline = '\0';
@@ -943,7 +947,7 @@ int get_commit_history(NCursesDiffViewer *viewer) {
popen("git log origin/master..HEAD --format=\"%h\" 2>/dev/null", "r");
if (unpushed_fp) {
while (fgets(line, sizeof(line), unpushed_fp) != NULL &&
unpushed_count < MAX_COMMITS) {
unpushed_count < unpushed_capacity) {
char *newline = strchr(line, '\n');
if (newline)
*newline = '\0';
@@ -957,8 +961,17 @@ int get_commit_history(NCursesDiffViewer *viewer) {
}
}
while (fgets(line, sizeof(line), fp) != NULL &&
viewer->commit_count < MAX_COMMITS) {
while (fgets(line, sizeof(line), fp) != NULL) {
if (viewer->commit_count >= viewer->commit_capacity) {
viewer->commit_capacity *= 2;
NCursesCommit *new_commits = realloc(
viewer->commits, viewer->commit_capacity * sizeof(NCursesCommit));
if (!new_commits) {
fprintf(stderr, "Failed to reallocate commits array\n");
break;
}
viewer->commits = new_commits;
}
// Remove newline
char *newline = strchr(line, '\n');
if (newline)
@@ -1004,6 +1017,12 @@ int get_commit_history(NCursesDiffViewer *viewer) {
}
pclose(fp);
for (int i = 0; i < unpushed_capacity; i++) {
free(unpushed_hashes[i]);
}
free(unpushed_hashes);
return viewer->commit_count;
}
@@ -1190,7 +1209,6 @@ int show_reset_confirmation_dialog(void) {
return 0;
}
int get_commit_title_input(char *title, int max_len, char *message,
int max_message_len) {
if (!title)
@@ -2527,7 +2545,6 @@ void render_file_list_window(NCursesDiffViewer *viewer) {
wrefresh(viewer->file_list_win);
}
void render_commit_list_window(NCursesDiffViewer *viewer) {
if (!viewer || !viewer->commit_list_win)
return;
@@ -2535,9 +2552,15 @@ void render_commit_list_window(NCursesDiffViewer *viewer) {
// CRITICAL: Clear the entire window first
werase(viewer->commit_list_win);
// Draw rounded border and title
draw_rounded_box(viewer->commit_list_win);
mvwprintw(viewer->commit_list_win, 0, 2, " 4. Commits ");
char commit_title[64];
if (viewer->commit_count > 0) {
snprintf(commit_title, sizeof(commit_title), " 4. Commits (%d/%d) ",
viewer->selected_commit + 1, viewer->commit_count);
} else {
snprintf(commit_title, sizeof(commit_title), " 4. Commits (0) ");
}
mvwprintw(viewer->commit_list_win, 0, 2, "%s", commit_title);
int max_commits_visible = viewer->commit_panel_height - 2;
@@ -4777,8 +4800,6 @@ int run_ncurses_diff_viewer(void) {
return 0;
}
int get_ncurses_git_branches(NCursesDiffViewer *viewer) {
if (!viewer)
return 0;
@@ -5654,9 +5675,15 @@ void render_stash_list_window(NCursesDiffViewer *viewer) {
// Clear the entire window first
werase(viewer->stash_list_win);
// Draw rounded border and title
draw_rounded_box(viewer->stash_list_win);
mvwprintw(viewer->stash_list_win, 0, 2, " 5. Stashes ");
char stash_title[64];
if (viewer->stash_count > 0) {
snprintf(stash_title, sizeof(stash_title), " 5. Stashes (%d/%d) ",
viewer->selected_stash + 1, viewer->stash_count);
} else {
snprintf(stash_title, sizeof(stash_title), " 5. Stashes (0) ");
}
mvwprintw(viewer->stash_list_win, 0, 2, "%s", stash_title);
int max_stashes_visible = viewer->stash_panel_height - 2;
@@ -5804,7 +5831,14 @@ void render_branch_list_window(NCursesDiffViewer *viewer) {
werase(viewer->branch_list_win);
draw_rounded_box(viewer->branch_list_win);
mvwprintw(viewer->branch_list_win, 0, 2, " 3. Branches ");
char branch_title[64];
if (viewer->branch_count > 0) {
snprintf(branch_title, sizeof(branch_title), " 3. Branches (%d/%d) ",
viewer->selected_branch + 1, viewer->branch_count);
} else {
snprintf(branch_title, sizeof(branch_title), " 3. Branches (0) ");
}
mvwprintw(viewer->branch_list_win, 0, 2, "%s", branch_title);
int max_branches_visible = viewer->branch_panel_height - 2;
@@ -6809,9 +6843,13 @@ void cleanup_ncurses_diff_viewer(NCursesDiffViewer *viewer) {
delwin(viewer->status_bar_win);
}
// Clean up fuzzy search windows
cleanup_fuzzy_search(viewer);
if (viewer->commits) {
free(viewer->commits);
viewer->commits = NULL;
}
// Clean up grep search windows
cleanup_grep_search(viewer);
}

View File

@@ -1,21 +1,16 @@
#include "utils/timer.h"
#include "builtins.h"
#include "common.h"
#include "shell.h"
#include <time.h>
void format_time(double ms, char *buffer, size_t buffer_size) {
if (ms < 1.0) {
// Microseconds
snprintf(buffer, buffer_size, "%.2f μs", ms * 1000);
} else if (ms < 1000.0) {
// Milliseconds
snprintf(buffer, buffer_size, "%.2f ms", ms);
} else if (ms < 60000.0) {
// Seconds
snprintf(buffer, buffer_size, "%.2f s", ms / 1000);
} else {
// Minutes and seconds
int minutes = (int)(ms / 60000);
double seconds = (ms - minutes * 60000) / 1000;
snprintf(buffer, buffer_size, "%d min %.2f s", minutes, seconds);
@@ -24,49 +19,35 @@ void format_time(double ms, char *buffer, size_t buffer_size) {
int lsh_timer(char **args) {
if (!args[1]) {
printf("timer: usage: timer COMMAND [ARGS...]\n");
printf("Measures execution time of a command\n");
fprintf(stderr, "timer: usage: timer COMMAND [ARGS...]\n");
return 1;
}
// Create a new array for the command to execute (without "timer" prefix)
char **cmd_args = &args[1];
// Start timing
clock_t start_time = clock();
// Execute the command
int result;
// Don't time commands that affect shell state
if (strcmp(cmd_args[0], "cd") == 0 || strcmp(cmd_args[0], "exit") == 0 ||
strcmp(cmd_args[0], "timer") == 0) {
// Handle built-in commands that might affect shell state
printf("timer: can't time built-in command: %s\n", cmd_args[0]);
result = 1;
} else {
// Execute the command normally
result = lsh_execute(cmd_args);
fprintf(stderr, "timer: can't time built-in command: %s\n", cmd_args[0]);
return 1;
}
// End timing
clock_t end_time = clock();
clock_t start = clock();
int result = lsh_execute(cmd_args);
clock_t end = clock();
// Calculate execution time in milliseconds
double execution_time_ms =
((double)(end_time - start_time) / CLOCKS_PER_SEC) * 1000;
double ms = ((double)(end - start) / CLOCKS_PER_SEC) * 1000;
// Format the execution time
char time_str[64];
format_time(execution_time_ms, time_str, sizeof(time_str));
format_time(ms, time_str, sizeof(time_str));
// Print the execution time with color
printf("\n");
printf(ANSI_COLOR_GREEN); // Green text for highlighted display
printf("╭───────────────────────────────╮\n");
printf("│ Execution time: %-14s │\n", time_str);
printf("╰───────────────────────────────╯\n");
printf(ANSI_COLOR_RESET); // Reset color
printf("\n" ANSI_COLOR_GREEN);
printf("╭───────────────────────────────────╮\n");
printf("│ Execution time: %-18s │\n", time_str);
printf("╰───────────────────────────────────╯\n");
printf(ANSI_COLOR_RESET);
return result;
}
int lsh_time(char **args) { return lsh_timer(args); }
int lsh_time(char **args) { return lsh_timer(args); }