dte test coverage


Directory: ./
File: src/file-history.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 68 104 65.4%
Functions: 6 8 75.0%
Branches: 30 46 65.2%

Line Branch Exec Source
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include "file-history.h"
7 #include "util/debug.h"
8 #include "util/path.h"
9 #include "util/readfile.h"
10 #include "util/str-util.h"
11 #include "util/string-view.h"
12 #include "util/strtonum.h"
13 #include "util/xmalloc.h"
14 #include "util/xstdio.h"
15
16 enum {
17 FILEHIST_MAX_ENTRIES = 512
18 };
19
20 47 void file_history_append(FileHistory *history, unsigned long row, unsigned long col, const char *filename)
21 {
22 47 BUG_ON(row == 0);
23 47 BUG_ON(col == 0);
24 47 HashMap *map = &history->entries;
25 47 FileHistoryEntry *e = hashmap_get(map, filename);
26
27
2/2
✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→13) taken 46 times.
47 if (e) {
28
1/2
✓ Branch 0 (8→9) taken 1 times.
✗ Branch 1 (8→10) not taken.
1 if (e == history->last) {
29 1 e->row = row;
30 1 e->col = col;
31 1 return;
32 }
33 e->next->prev = e->prev;
34 if (unlikely(e == history->first)) {
35 history->first = e->next;
36 } else {
37 e->prev->next = e->next;
38 }
39 } else {
40
1/2
✗ Branch 0 (13→14) not taken.
✓ Branch 1 (13→17) taken 46 times.
46 if (map->count == FILEHIST_MAX_ENTRIES) {
41 // History is full; recycle the oldest entry
42 FileHistoryEntry *old_first = history->first;
43 FileHistoryEntry *new_first = old_first->next;
44 new_first->prev = NULL;
45 history->first = new_first;
46 e = hashmap_remove(map, old_first->filename);
47 BUG_ON(e != old_first);
48 } else {
49 46 e = xmalloc(sizeof(*e));
50 }
51 46 e->filename = xstrdup(filename);
52 46 hashmap_insert(map, e->filename, e);
53 }
54
55 // Insert the entry at the end of the list
56 46 FileHistoryEntry *old_last = history->last;
57 46 e->next = NULL;
58 46 e->prev = old_last;
59 46 e->row = row;
60 46 e->col = col;
61 46 history->last = e;
62
2/2
✓ Branch 0 (20→21) taken 44 times.
✓ Branch 1 (20→22) taken 2 times.
46 if (likely(old_last)) {
63 44 old_last->next = e;
64 } else {
65 2 history->first = e;
66 }
67 }
68
69 25 static bool parse_ulong_field(StringView *sv, unsigned long *valp)
70 {
71 25 size_t n = buf_parse_ulong(sv->data, sv->length, valp);
72
6/6
✓ Branch 0 (3→4) taken 22 times.
✓ Branch 1 (3→8) taken 3 times.
✓ Branch 2 (4→5) taken 20 times.
✓ Branch 3 (4→8) taken 2 times.
✓ Branch 4 (5→6) taken 16 times.
✓ Branch 5 (5→8) taken 4 times.
25 if (n == 0 || *valp == 0 || sv->data[n] != ' ') {
73 return false;
74 }
75 16 strview_remove_prefix(sv, n + 1);
76 16 return true;
77 }
78
79 6 void file_history_load(FileHistory *history, ErrorBuffer *ebuf, char *filename, size_t size_limit)
80 {
81 6 BUG_ON(history->filename);
82 6 hashmap_init(&history->entries, FILEHIST_MAX_ENTRIES, HMAP_NO_FLAGS);
83 6 history->filename = filename;
84
85 6 char *buf;
86 6 const ssize_t ssize = read_file(filename, &buf, size_limit);
87
2/2
✓ Branch 0 (6→7) taken 5 times.
✓ Branch 1 (6→11) taken 1 times.
6 if (ssize < 0) {
88
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 5 times.
5 if (errno != ENOENT) {
89 error_msg(ebuf, "Error reading %s: %s", filename, strerror(errno));
90 }
91 5 return;
92 }
93
94
2/2
✓ Branch 0 (24→12) taken 15 times.
✓ Branch 1 (24→25) taken 1 times.
16 for (size_t pos = 0, size = ssize; pos < size; ) {
95 15 unsigned long row, col;
96 15 StringView line = buf_slice_next_line(buf, &pos, size);
97
9/10
✓ Branch 0 (14→15) taken 10 times.
✓ Branch 1 (14→20) taken 5 times.
✓ Branch 2 (16→17) taken 6 times.
✓ Branch 3 (16→20) taken 4 times.
✓ Branch 4 (17→18) taken 5 times.
✓ Branch 5 (17→20) taken 1 times.
✓ Branch 6 (18→19) taken 3 times.
✓ Branch 7 (18→20) taken 2 times.
✗ Branch 8 (19→20) not taken.
✓ Branch 9 (19→21) taken 3 times.
15 if (unlikely(
98 !parse_ulong_field(&line, &row)
99 || !parse_ulong_field(&line, &col)
100 || line.length < 2
101 || line.data[0] != '/'
102 || buf[pos - 1] != '\n'
103 )) {
104 12 continue;
105 }
106 3 buf[pos - 1] = '\0'; // null-terminate line, by replacing '\n' with '\0'
107 3 file_history_append(history, row, col, line.data);
108 }
109
110 1 free(buf);
111 }
112
113 void file_history_save(const FileHistory *history, ErrorBuffer *ebuf)
114 {
115 const char *filename = history->filename;
116 if (!filename) {
117 return;
118 }
119
120 FILE *f = xfopen(filename, "w", O_CLOEXEC, 0666);
121 if (!f) {
122 error_msg(ebuf, "Error creating %s: %s", filename, strerror(errno));
123 return;
124 }
125
126 for (const FileHistoryEntry *e = history->first; e; e = e->next) {
127 xfprintf(f, "%lu %lu %s\n", e->row, e->col, e->filename);
128 }
129
130 fclose(f);
131 }
132
133 8 bool file_history_find(const FileHistory *history, const char *filename, unsigned long *row, unsigned long *col)
134 {
135 8 const FileHistoryEntry *e = hashmap_get(&history->entries, filename);
136
2/2
✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→5) taken 4 times.
8 if (!e) {
137 return false;
138 }
139 4 *row = e->row;
140 4 *col = e->col;
141 4 return true;
142 }
143
144 9 void file_history_free(FileHistory *history)
145 {
146 9 hashmap_free(&history->entries, free);
147 9 free(history->filename);
148 9 history->filename = NULL;
149 9 history->first = NULL;
150 9 history->last = NULL;
151 9 }
152
153 1 String file_history_dump(const FileHistory *history)
154 {
155 1 size_t nr_entries = history->entries.count;
156 1 size_t size = next_multiple(64 * nr_entries, 4096);
157 1 String buf = string_new(size);
158 1 size_t n = 0;
159
160
2/2
✓ Branch 0 (7→4) taken 43 times.
✓ Branch 1 (7→8) taken 1 times.
44 for (const FileHistoryEntry *e = history->first; e; e = e->next, n++) {
161 43 string_append_cstring(&buf, e->filename);
162 43 string_append_byte(&buf, '\n');
163 }
164
165 1 BUG_ON(n != nr_entries);
166 1 return buf;
167 }
168
169 String file_history_dump_relative(const FileHistory *history)
170 {
171 char cwdbuf[8192];
172 const char *cwd = getcwd(cwdbuf, sizeof(cwdbuf));
173 if (unlikely(!cwd)) {
174 return file_history_dump(history);
175 }
176
177 size_t nr_entries = history->entries.count;
178 size_t size = next_multiple(16 * nr_entries, 4096);
179 String buf = string_new(size);
180 size_t n = 0;
181
182 for (const FileHistoryEntry *e = history->first; e; e = e->next, n++) {
183 const char *relative = path_slice_relative(e->filename, cwd);
184 string_append_cstring(&buf, relative);
185 string_append_byte(&buf, '\n');
186 }
187
188 BUG_ON(n != nr_entries);
189 return buf;
190 }
191