dte test coverage


Directory: ./
File: src/history.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 71 84 84.5%
Functions: 6 7 85.7%
Branches: 31 38 81.6%

Line Branch Exec Source
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "history.h"
5 #include "util/arith.h"
6 #include "util/debug.h"
7 #include "util/readfile.h"
8 #include "util/str-util.h"
9 #include "util/xmalloc.h"
10 #include "util/xstdio.h"
11
12 12023 void history_append(History *history, const char *text)
13 {
14 12023 BUG_ON(history->max_entries < 2);
15
2/2
✓ Branch 0 (4→5) taken 12021 times.
✓ Branch 1 (4→21) taken 2 times.
12023 if (text[0] == '\0') {
16 return;
17 }
18
19 12021 HashMap *map = &history->entries;
20 12021 HistoryEntry *e = hashmap_get(map, text);
21
22
2/2
✓ Branch 0 (6→7) taken 5 times.
✓ Branch 1 (6→11) taken 12016 times.
12021 if (e) {
23
2/2
✓ Branch 0 (7→8) taken 4 times.
✓ Branch 1 (7→21) taken 1 times.
5 if (e == history->last) {
24 // Existing entry already at end of list; nothing more to do
25 return;
26 }
27 // Remove existing entry from list
28 4 e->next->prev = e->prev;
29
2/2
✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→10) taken 2 times.
4 if (unlikely(e == history->first)) {
30 2 history->first = e->next;
31 } else {
32 2 e->prev->next = e->next;
33 }
34 } else {
35
2/2
✓ Branch 0 (11→12) taken 11491 times.
✓ Branch 1 (11→15) taken 525 times.
12016 if (map->count == history->max_entries) {
36 // History is full; recycle oldest entry
37 11491 HistoryEntry *old_first = history->first;
38 11491 HistoryEntry *new_first = old_first->next;
39 11491 new_first->prev = NULL;
40 11491 history->first = new_first;
41 11491 e = hashmap_remove(map, old_first->text);
42 11491 BUG_ON(e != old_first);
43 } else {
44 525 e = xmalloc(sizeof(*e));
45 }
46 12016 e->text = xstrdup(text);
47 12016 hashmap_insert(map, e->text, e);
48 }
49
50 // Insert entry at end of list
51 12020 HistoryEntry *old_last = history->last;
52 12020 e->next = NULL;
53 12020 e->prev = old_last;
54 12020 history->last = e;
55
2/2
✓ Branch 0 (18→19) taken 12015 times.
✓ Branch 1 (18→20) taken 5 times.
12020 if (likely(old_last)) {
56 12015 old_last->next = e;
57 } else {
58 5 history->first = e;
59 }
60 }
61
62 4 bool history_search_forward (
63 const History *history,
64 const HistoryEntry **pos,
65 const char *text
66 ) {
67
1/2
✓ Branch 0 (2→3) taken 4 times.
✗ Branch 1 (2→4) not taken.
4 const HistoryEntry *start = *pos ? (*pos)->prev : history->last;
68 4 size_t text_len = strlen(text);
69
2/2
✓ Branch 0 (9→6) taken 4 times.
✓ Branch 1 (9→10) taken 1 times.
5 for (const HistoryEntry *e = start; e; e = e->prev) {
70
2/2
✓ Branch 0 (6→7) taken 3 times.
✓ Branch 1 (6→8) taken 1 times.
4 if (str_has_strn_prefix(e->text, text, text_len)) {
71 3 *pos = e;
72 3 return true;
73 }
74 }
75 return false;
76 }
77
78 4 bool history_search_backward (
79 const History *history,
80 const HistoryEntry **pos,
81 const char *text
82 ) {
83
1/2
✓ Branch 0 (2→3) taken 4 times.
✗ Branch 1 (2→4) not taken.
4 const HistoryEntry *start = *pos ? (*pos)->next : history->first;
84 4 size_t text_len = strlen(text);
85
2/2
✓ Branch 0 (9→6) taken 4 times.
✓ Branch 1 (9→10) taken 1 times.
5 for (const HistoryEntry *e = start; e; e = e->next) {
86
2/2
✓ Branch 0 (6→7) taken 3 times.
✓ Branch 1 (6→8) taken 1 times.
4 if (str_has_strn_prefix(e->text, text, text_len)) {
87 3 *pos = e;
88 3 return true;
89 }
90 }
91 return false;
92 }
93
94 11 void history_load(History *history, ErrorBuffer *ebuf, char *filename, size_t size_limit)
95 {
96 11 BUG_ON(history->filename);
97 11 BUG_ON(history->max_entries < 2);
98 11 hashmap_init(&history->entries, history->max_entries, HMAP_NO_FLAGS);
99 11 history->filename = filename;
100
101 11 char *buf;
102 11 const ssize_t ssize = read_file(filename, &buf, size_limit);
103
2/2
✓ Branch 0 (8→9) taken 10 times.
✓ Branch 1 (8→13) taken 1 times.
11 if (ssize < 0) {
104
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→12) taken 10 times.
10 if (errno != ENOENT) {
105 error_msg(ebuf, "Error reading %s: %s", filename, strerror(errno));
106 }
107 10 return;
108 }
109
110
2/2
✓ Branch 0 (16→14) taken 5 times.
✓ Branch 1 (16→17) taken 1 times.
6 for (size_t pos = 0, size = ssize; pos < size; ) {
111 5 history_append(history, buf_next_line(buf, &pos, size));
112 }
113
114 1 free(buf);
115 }
116
117 1 void history_save(const History *history, ErrorBuffer *ebuf)
118 {
119 1 const char *filename = history->filename;
120
1/2
✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→14) not taken.
1 if (!filename) {
121 return;
122 }
123
124 1 FILE *f = xfopen(filename, "w", O_CLOEXEC, 0666);
125
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→8) taken 1 times.
1 if (!f) {
126 error_msg(ebuf, "Error creating %s: %s", filename, strerror(errno));
127 return;
128 }
129
130
2/2
✓ Branch 0 (12→9) taken 3 times.
✓ Branch 1 (12→13) taken 1 times.
4 for (const HistoryEntry *e = history->first; e; e = e->next) {
131 3 xfputs(e->text, f);
132 3 xfputc('\n', f);
133 }
134
135 1 fclose(f);
136 }
137
138 20 void history_free(History *history)
139 {
140 20 hashmap_free(&history->entries, free);
141 20 free(history->filename);
142 20 history->filename = NULL;
143 20 history->first = NULL;
144 20 history->last = NULL;
145 20 }
146
147 String history_dump(const History *history)
148 {
149 const size_t nr_entries = history->entries.count;
150 const size_t size = next_multiple(16 * nr_entries, 4096);
151 String buf = string_new(size);
152 size_t n = 0;
153 for (const HistoryEntry *e = history->first; e; e = e->next, n++) {
154 string_append_cstring(&buf, e->text);
155 string_append_byte(&buf, '\n');
156 }
157 BUG_ON(n != nr_entries);
158 return buf;
159 }
160