1 /*
2  * Copyright (c) 2020 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Test logging to file system
10  *
11  */
12 
13 #include <stdbool.h>
14 #include <stdlib.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/ztest.h>
17 #include <zephyr/fs/fs.h>
18 #include <zephyr/fff.h>
19 #include <zephyr/logging/log_backend.h>
20 
21 #define DT_DRV_COMPAT zephyr_fstab_littlefs
22 #define TEST_AUTOMOUNT DT_PROP(DT_DRV_INST(0), automount)
23 #if !TEST_AUTOMOUNT
24 #include <zephyr/fs/littlefs.h>
25 #define PARTITION_NODE DT_NODELABEL(lfs1)
26 FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
27 #endif
28 
29 #define MAX_PATH_LEN (256 + 7)
30 
31 static const char *log_prefix = CONFIG_LOG_BACKEND_FS_FILE_PREFIX;
32 static const struct log_backend *backend;
33 
34 DEFINE_FFF_GLOBALS;
35 FAKE_VOID_FUNC(log_output_dropped_process, const struct log_output *, uint32_t);
36 FAKE_VALUE_FUNC(log_format_func_t, log_format_func_t_get, uint32_t);
37 
38 int write_log_to_file(uint8_t *data, size_t length, void *ctx);
39 
40 
ZTEST(test_log_backend_fs,test_fs_nonexist)41 ZTEST(test_log_backend_fs, test_fs_nonexist)
42 {
43 	#if TEST_AUTOMOUNT
44 	ztest_test_skip();
45 	#else
46 	uint8_t to_log[] = "Log to left behind";
47 	int rc;
48 
49 	rc = write_log_to_file(to_log, sizeof(to_log), NULL);
50 	zassert_equal(rc, sizeof(to_log), "Unexpected rteval.");
51 	struct fs_mount_t *mp = &FS_FSTAB_ENTRY(PARTITION_NODE);
52 
53 	rc = fs_mount(mp);
54 	zassert_equal(rc, 0, "Can not mount FS.");
55 	#endif
56 }
57 
ZTEST(test_log_backend_fs,test_wipe_fs_logs)58 ZTEST(test_log_backend_fs, test_wipe_fs_logs)
59 {
60 	int rc;
61 	struct fs_dir_t dir;
62 	struct fs_file_t file;
63 	char fname[MAX_PATH_LEN];
64 
65 	fs_dir_t_init(&dir);
66 	fs_file_t_init(&file);
67 
68 	rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
69 	if (rc) {
70 		/* log directory might not exist jet */
71 		return;
72 	}
73 
74 	/* Iterate over logging directory. */
75 	while (1) {
76 		struct fs_dirent ent = { 0 };
77 
78 		rc = fs_readdir(&dir, &ent);
79 		zassert_equal(rc, 0, "Can not read directory.");
80 		if ((rc < 0) || (ent.name[0] == 0)) {
81 			break;
82 		}
83 		if (ent.type == FS_DIR_ENTRY_FILE &&
84 		    strncmp(ent.name, log_prefix, strlen(log_prefix)) == 0) {
85 			sprintf(fname, "%s/%s", CONFIG_LOG_BACKEND_FS_DIR,
86 				ent.name);
87 			rc = fs_unlink(fname);
88 			zassert_equal(rc, 0, "Can not remove file %s.", fname);
89 			TC_PRINT("removed: %s\n", fname);
90 		}
91 	}
92 
93 	(void)fs_closedir(&dir);
94 }
95 
ZTEST(test_log_backend_fs,test_log_fs_file_content)96 ZTEST(test_log_backend_fs, test_log_fs_file_content)
97 {
98 	int rc;
99 	struct fs_file_t file;
100 	char log_read[MAX_PATH_LEN];
101 	uint8_t to_log[] = "Correct Log 1";
102 	static char fname[MAX_PATH_LEN];
103 
104 	fs_file_t_init(&file);
105 
106 	rc = write_log_to_file(to_log, sizeof(to_log), NULL);
107 	backend->api->notify(backend, LOG_BACKEND_EVT_PROCESS_THREAD_DONE, NULL);
108 
109 	sprintf(fname, "%s/%s0000", CONFIG_LOG_BACKEND_FS_DIR, log_prefix);
110 
111 	zassert_equal(fs_open(&file, fname, FS_O_READ), 0,
112 		      "Can not open log file.");
113 
114 	zassert_true(fs_read(&file, log_read, MAX_PATH_LEN) >= 0,
115 		     "Can not read log file.");
116 
117 	rc = strncmp(log_read, to_log, sizeof(log_read));
118 	zassert_equal(rc, 0, "Text inside log file is not correct.");
119 
120 	zassert_equal(fs_close(&file), 0, "Can not close log file.");
121 
122 	to_log[sizeof(to_log)-2] = '2';
123 
124 	rc = write_log_to_file(to_log, sizeof(to_log), NULL);
125 	backend->api->notify(backend, LOG_BACKEND_EVT_PROCESS_THREAD_DONE, NULL);
126 
127 	zassert_equal(fs_open(&file, fname, FS_O_READ), 0,
128 		      "Can not open log file.");
129 
130 	zassert_equal(fs_seek(&file, sizeof(to_log), FS_SEEK_SET), 0,
131 		      "Bad file size");
132 
133 	zassert_true(fs_read(&file, log_read, MAX_PATH_LEN) >= 0,
134 		     "Can not read log file.");
135 
136 	rc = strncmp(log_read, to_log, sizeof(log_read));
137 	zassert_equal(rc, 0, "Text inside log file is not correct.");
138 
139 	zassert_equal(fs_close(&file), 0, "Can not close log file.");
140 }
141 
ZTEST(test_log_backend_fs,test_log_fs_file_size)142 ZTEST(test_log_backend_fs, test_log_fs_file_size)
143 {
144 	int rc;
145 	int i;
146 	struct fs_dir_t dir;
147 	int file_ctr = 0;
148 	static char fname[MAX_PATH_LEN];
149 	uint8_t to_log[] = "Text Log";
150 	struct fs_dirent entry;
151 
152 	fs_dir_t_init(&dir);
153 
154 	sprintf(fname, "%s/%s0000", CONFIG_LOG_BACKEND_FS_DIR, log_prefix);
155 	zassert_equal(fs_stat(fname, &entry), 0, "Can not get file info.");
156 
157 	/* Fill in log file over size limit. */
158 	for (i = 0;
159 	     i <= (CONFIG_LOG_BACKEND_FS_FILE_SIZE - entry.size) /
160 		  sizeof(to_log);
161 	     i++) {
162 		rc = write_log_to_file(to_log, sizeof(to_log), NULL);
163 		/* Written length not tracked here. */
164 		ARG_UNUSED(rc);
165 	}
166 
167 	backend->api->notify(backend, LOG_BACKEND_EVT_PROCESS_THREAD_DONE, NULL);
168 
169 	zassert_equal(fs_stat(fname, &entry), 0, "Can not get file info.");
170 	size_t exp_size = CONFIG_LOG_BACKEND_FS_FILE_SIZE -
171 			  (CONFIG_LOG_BACKEND_FS_FILE_SIZE - entry.size) %
172 			  sizeof(to_log);
173 	zassert_equal(entry.size, exp_size, "Unexpected %s file size (%d B)",
174 		      fname, entry.size);
175 
176 	sprintf(fname, "%s/%s0001", CONFIG_LOG_BACKEND_FS_DIR, log_prefix);
177 	zassert_equal(fs_stat(fname, &entry), 0, "Can not get file info.");
178 
179 	zassert_equal(entry.size, sizeof(to_log),
180 		      "Unexpected %s file size (%d B)",
181 		      fname, entry.size);
182 
183 	rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
184 	zassert_equal(rc, 0, "Can not open directory.");
185 	/* Count number of log files. */
186 	while (rc >= 0) {
187 		struct fs_dirent ent = { 0 };
188 
189 		rc = fs_readdir(&dir, &ent);
190 		if ((rc < 0) || (ent.name[0] == 0)) {
191 			break;
192 		}
193 		if (strstr(ent.name, log_prefix) != NULL) {
194 			++file_ctr;
195 		}
196 	}
197 	(void)fs_closedir(&dir);
198 	zassert_equal(file_ctr, 2, "File changing failed");
199 }
200 
ZTEST(test_log_backend_fs,test_log_fs_files_max)201 ZTEST(test_log_backend_fs, test_log_fs_files_max)
202 {
203 	int rc;
204 	int i;
205 	struct fs_dir_t dir;
206 	int file_ctr = 0;
207 	uint8_t to_log[] = "Text Log";
208 	struct fs_dirent ent;
209 	uint32_t test_mask = 0;
210 
211 	fs_dir_t_init(&dir);
212 
213 	/* Fill in log files over files count limit. */
214 	for (i = 0;
215 	     i <= CONFIG_LOG_BACKEND_FS_FILE_SIZE /
216 		  sizeof(to_log) * (CONFIG_LOG_BACKEND_FS_FILES_LIMIT - 1);
217 	     i++) {
218 		rc = write_log_to_file(to_log, sizeof(to_log), NULL);
219 		/* Written length not tracked here. */
220 		ARG_UNUSED(rc);
221 	}
222 
223 	backend->api->notify(backend, LOG_BACKEND_EVT_PROCESS_THREAD_DONE, NULL);
224 
225 	rc = fs_opendir(&dir, CONFIG_LOG_BACKEND_FS_DIR);
226 	zassert_equal(rc, 0, "Can not open directory.");
227 	/* Count log files. */
228 	while (rc >= 0) {
229 
230 		rc = fs_readdir(&dir, &ent);
231 		if ((rc < 0) || (ent.name[0] == 0)) {
232 			break;
233 		}
234 		if (strstr(ent.name, log_prefix) != NULL) {
235 			++file_ctr;
236 			test_mask |= 1 << atoi(&ent.name[strlen(log_prefix)]);
237 		}
238 	}
239 	(void)fs_closedir(&dir);
240 	zassert_equal(file_ctr, CONFIG_LOG_BACKEND_FS_FILES_LIMIT,
241 		      "Bad files count: expected %d, got %d ",
242 		      CONFIG_LOG_BACKEND_FS_FILES_LIMIT, file_ctr);
243 	/*expected files: log.0001, log.0002, log.0003, log.0004 */
244 	zassert_equal(test_mask, 0b11110, "Unexpected file numeration");
245 }
246 
backend_find(char const * name)247 static const struct log_backend *backend_find(char const *name)
248 {
249 	size_t slen = strlen(name);
250 
251 	STRUCT_SECTION_FOREACH(log_backend, backend) {
252 		if (strncmp(name, backend->name, slen) == 0) {
253 			return backend;
254 		}
255 	}
256 
257 	return NULL;
258 }
259 
260 
suite_setup(void)261 void *suite_setup(void)
262 {
263 	backend = backend_find("log_backend_fs");
264 	zassert_not_null(backend);
265 
266 	return NULL;
267 }
268 
269 ZTEST_SUITE(test_log_backend_fs, NULL, suite_setup, NULL, NULL, NULL);
270