dte test coverage


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