dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 63.5% 66 / 0 / 104
Functions: 75.0% 6 / 0 / 8
Branches: 60.9% 28 / 12 / 58

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