Skip to content

Commit b19e112

Browse files
committed
FileOutput tests: Use MemoryFileSystem instead of real file system
1 parent a27ba42 commit b19e112

File tree

5 files changed

+100
-73
lines changed

5 files changed

+100
-73
lines changed

lib/src/outputs/advanced_file_output.dart

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'dart:io';
44

5+
import 'package:meta/meta.dart';
6+
57
import '../log_level.dart';
68
import '../log_output.dart';
79
import '../output_event.dart';
@@ -94,8 +96,9 @@ class AdvancedFileOutput extends LogOutput {
9496
],
9597
_maxRotatedFilesCount = maxRotatedFilesCount,
9698
_fileSorter = fileSorter ?? _defaultFileSorter,
97-
_fileUpdateDuration = fileUpdateDuration,
98-
_file = maxFileSizeKB > 0 ? File('$path/$latestFileName') : File(path);
99+
_fileUpdateDuration = fileUpdateDuration {
100+
_file = createFile(maxFileSizeKB > 0 ? '$path/$latestFileName' : path);
101+
}
99102

100103
/// Logs directory path by default, particular log file path if [_maxFileSizeKB] is 0.
101104
final String _path;
@@ -115,7 +118,7 @@ class AdvancedFileOutput extends LogOutput {
115118
final Comparator<File> _fileSorter;
116119
final Duration _fileUpdateDuration;
117120

118-
final File _file;
121+
late final File _file;
119122
IOSink? _sink;
120123
Timer? _bufferFlushTimer;
121124
Timer? _targetFileUpdater;
@@ -142,14 +145,18 @@ class AdvancedFileOutput extends LogOutput {
142145
return a.lastModifiedSync().compareTo(b.lastModifiedSync());
143146
}
144147

148+
@protected
149+
File createFile(String path) {
150+
return File(path);
151+
}
152+
145153
@override
146154
Future<void> init() async {
147155
if (_rotatingFilesMode) {
148-
final dir = Directory(_path);
149156
// We use sync directory check to avoid losing potential initial boot logs
150157
// in early crash scenarios.
151-
if (!dir.existsSync()) {
152-
dir.createSync(recursive: true);
158+
if (!_file.parent.existsSync()) {
159+
_file.parent.createSync(recursive: true);
153160
}
154161

155162
_targetFileUpdater = Timer.periodic(
@@ -232,8 +239,7 @@ class AdvancedFileOutput extends LogOutput {
232239
// If maxRotatedFilesCount is not set, keep all files
233240
if (_maxRotatedFilesCount == null) return;
234241

235-
final dir = Directory(_path);
236-
final files = dir
242+
final files = _file.parent
237243
.listSync()
238244
.whereType<File>()
239245
// Filter out the latest file

lib/src/outputs/advanced_file_output_stub.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'dart:convert';
22
import 'dart:io';
33

4+
import 'package:meta/meta.dart';
5+
46
import '../log_level.dart';
57
import '../log_output.dart';
68
import '../output_event.dart';
@@ -76,6 +78,11 @@ class AdvancedFileOutput extends LogOutput {
7678
throw UnsupportedError("Not supported on this platform.");
7779
}
7880

81+
@protected
82+
File createFile(String path) {
83+
throw UnsupportedError("Not supported on this platform.");
84+
}
85+
7986
@override
8087
void output(OutputEvent event) {
8188
throw UnsupportedError("Not supported on this platform.");

pubspec.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ topics:
1010
environment:
1111
sdk: ">=2.17.0 <4.0.0"
1212

13+
dependencies:
14+
meta: ^1.16.0
15+
1316
dev_dependencies:
1417
test: ^1.16.8
1518
lints: ^2.0.1
19+
file: ">=6.0.0 <8.0.0"
1620

1721
platforms:
1822
android:

test/outputs/advanced_file_output_test.dart

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,53 @@
11
import 'dart:io';
22

3+
import 'package:file/memory.dart';
34
import 'package:logger/logger.dart';
45
import 'package:test/test.dart';
56

6-
void main() {
7-
var file = File("${Directory.systemTemp.path}/dart_advanced_logger_test.log");
8-
var dir = Directory("${Directory.systemTemp.path}/dart_advanced_logger_dir");
9-
setUp(() async {
10-
await file.create(recursive: true);
11-
await dir.create(recursive: true);
7+
var memory = MemoryFileSystem();
8+
9+
class MemoryAdvancedFileOutput extends AdvancedFileOutput {
10+
MemoryAdvancedFileOutput({
11+
required super.path,
12+
super.maxDelay,
13+
super.maxFileSizeKB,
14+
super.writeImmediately,
15+
super.maxRotatedFilesCount,
16+
super.fileNameFormatter,
17+
super.fileSorter,
18+
super.fileHeader,
19+
super.fileFooter,
1220
});
1321

14-
tearDown(() async {
15-
await file.delete();
16-
await dir.delete(recursive: true);
22+
late File _file;
23+
24+
get file => _file;
25+
26+
@override
27+
File createFile(String path) {
28+
return _file = memory.file(path);
29+
}
30+
}
31+
32+
void main() {
33+
final fileName = "dart_advanced_logger_test.log";
34+
final dirName = "dart_advanced_logger_dir";
35+
36+
tearDown(() {
37+
var file = memory.file(fileName);
38+
if (file.existsSync()) {
39+
file.deleteSync();
40+
}
41+
42+
var directory = memory.directory(dirName);
43+
if (directory.existsSync()) {
44+
directory.deleteSync(recursive: true);
45+
}
1746
});
1847

1948
test('Real file read and write with buffer accumulation', () async {
20-
var output = AdvancedFileOutput(
21-
path: file.path,
49+
var output = MemoryAdvancedFileOutput(
50+
path: fileName,
2251
maxDelay: const Duration(milliseconds: 500),
2352
maxFileSizeKB: 0,
2453
);
@@ -32,12 +61,9 @@ void main() {
3261
output.output(event1);
3362
output.output(event2);
3463

35-
// Wait until buffer is flushed to file
36-
await Future.delayed(const Duration(seconds: 1));
37-
3864
await output.destroy();
3965

40-
var content = await file.readAsString();
66+
var content = await output.file.readAsString();
4167
expect(
4268
content,
4369
allOf(
@@ -50,8 +76,8 @@ void main() {
5076

5177
test('Real file read and write with rotating file names and immediate output',
5278
() async {
53-
var output = AdvancedFileOutput(
54-
path: dir.path,
79+
var output = MemoryAdvancedFileOutput(
80+
path: dirName,
5581
writeImmediately: [Level.info],
5682
);
5783
await output.init();
@@ -66,8 +92,7 @@ void main() {
6692

6793
await output.destroy();
6894

69-
final logFile = File('${dir.path}/latest.log');
70-
var content = await logFile.readAsString();
95+
var content = await output.file.readAsString();
7196
expect(
7297
content,
7398
allOf(
@@ -79,8 +104,8 @@ void main() {
79104
});
80105

81106
test('Rolling files', () async {
82-
var output = AdvancedFileOutput(
83-
path: dir.path,
107+
var output = MemoryAdvancedFileOutput(
108+
path: dirName,
84109
maxFileSizeKB: 1,
85110
);
86111
await output.init();
@@ -100,17 +125,16 @@ void main() {
100125
output.output(event2);
101126
await output.destroy();
102127

103-
final files = dir.listSync();
104-
128+
final files = output.file.parent.listSync();
105129
expect(
106130
files,
107131
(hasLength(3)),
108132
);
109133
});
110134

111135
test('Rolling files with rotated files deletion', () async {
112-
var output = AdvancedFileOutput(
113-
path: dir.path,
136+
var output = MemoryAdvancedFileOutput(
137+
path: dirName,
114138
maxFileSizeKB: 1,
115139
maxRotatedFilesCount: 1,
116140
);
@@ -120,34 +144,25 @@ void main() {
120144
output.output(event0);
121145
await output.destroy();
122146

123-
// TODO Find out why test is so flaky with durations <1000ms
124-
// Give the OS a chance to flush to the file system (should reduce flakiness)
125-
await Future.delayed(const Duration(milliseconds: 1000));
126-
127147
// Start again to roll files on init without waiting for timer tick
128148
await output.init();
129149
final event1 = OutputEvent(LogEvent(Level.fatal, ""), ["2" * 1500]);
130150
output.output(event1);
131151
await output.destroy();
132152

133-
await Future.delayed(const Duration(milliseconds: 1000));
134-
135153
// And again for another roll
136154
await output.init();
137155
final event2 = OutputEvent(LogEvent(Level.fatal, ""), ["3" * 1500]);
138156
output.output(event2);
139157
await output.destroy();
140158

141-
await Future.delayed(const Duration(milliseconds: 1000));
142-
143-
final files = dir.listSync();
144-
159+
final latestFile = output.file;
160+
final files = latestFile.parent.listSync();
145161
// Expect only 2 files: the "latest" that is the current log file
146162
// and only one rotated file. The first created file should be deleted.
147163
expect(files, hasLength(2));
148-
final latestFile = File('${dir.path}/latest.log');
149-
final rotatedFile = dir
150-
.listSync()
164+
165+
final rotatedFile = files
151166
.whereType<File>()
152167
.firstWhere((file) => file.path != latestFile.path);
153168
expect(await latestFile.readAsString(), contains("3"));
@@ -158,8 +173,8 @@ void main() {
158173
const fileHeader = "TEST-HEADER";
159174
const fileFooter = "TEST-FOOTER";
160175

161-
var output = AdvancedFileOutput(
162-
path: dir.path,
176+
var output = MemoryAdvancedFileOutput(
177+
path: dirName,
163178
maxFileSizeKB: 1,
164179
maxRotatedFilesCount: 1,
165180
fileHeader: fileHeader,
@@ -172,23 +187,23 @@ void main() {
172187
output.output(event0);
173188
await output.destroy();
174189

175-
// Give the OS a chance to flush to the file system (should reduce flakiness)
176-
await Future.delayed(const Duration(milliseconds: 1000));
177-
178-
final latestFile = File('${dir.path}/latest.log');
190+
final latestFile = output.file;
179191
expect(await latestFile.readAsString(), startsWith(fileHeader));
180192
expect(await latestFile.readAsString(), endsWith("$fileFooter\n"));
181193
});
182194

183195
test('Rolling files with custom file sorter', () async {
184-
var output = AdvancedFileOutput(
185-
path: dir.path,
196+
int fileNameCounter = 0;
197+
var output = MemoryAdvancedFileOutput(
198+
path: dirName,
186199
maxFileSizeKB: 1,
187200
maxRotatedFilesCount: 1,
188201
// Define a custom file sorter that sorts files by their length
189202
// (strange behavior for testing purposes) from the longest to
190203
// the shortest: the longest file should be deleted first.
191204
fileSorter: (a, b) => b.lengthSync().compareTo(a.lengthSync()),
205+
// The default uses date time until milliseconds and sometimes this test is faster and would re-use the same name multiple times.
206+
fileNameFormatter: (timestamp) => "${fileNameCounter++}.log",
192207
);
193208

194209
await output.init();
@@ -203,31 +218,27 @@ void main() {
203218
output.output(event1);
204219
await output.destroy();
205220

206-
// Give the OS a chance to flush to the file system (should reduce flakiness)
207-
await Future.delayed(const Duration(milliseconds: 50));
208-
209221
// And again for another roll
210222
await output.init();
211223
final event2 = OutputEvent(LogEvent(Level.fatal, ""), ["3" * 1500]);
212224
output.output(event2);
213225
await output.destroy();
214226

215-
final files = dir.listSync();
216-
227+
final latestFile = output.file;
228+
final files = latestFile.parent.listSync();
217229
// Expect only 2 files: the "latest" that is the current log file
218230
// and only one rotated file (the shortest one).
219231
expect(files, hasLength(2));
220-
final latestFile = File('${dir.path}/latest.log');
221-
final rotatedFile = dir
222-
.listSync()
232+
233+
final rotatedFile = files
223234
.whereType<File>()
224235
.firstWhere((file) => file.path != latestFile.path);
225236
expect(await latestFile.readAsString(), contains("3"));
226237
expect(await rotatedFile.readAsString(), contains("1"));
227238
});
228239

229240
test('Flush temporary buffer on destroy', () async {
230-
var output = AdvancedFileOutput(path: dir.path);
241+
var output = MemoryAdvancedFileOutput(path: dirName);
231242
await output.init();
232243

233244
final event0 = OutputEvent(LogEvent(Level.info, ""), ["Last event"]);
@@ -238,8 +249,7 @@ void main() {
238249

239250
await output.destroy();
240251

241-
final logFile = File('${dir.path}/latest.log');
242-
var content = await logFile.readAsString();
252+
var content = await output.file.readAsString();
243253
expect(
244254
content,
245255
allOf(

test/outputs/file_output_test.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import 'dart:io';
2-
1+
import 'package:file/memory.dart';
32
import 'package:logger/logger.dart';
43
import 'package:test/test.dart';
54

5+
var memory = MemoryFileSystem();
6+
67
void main() {
7-
var file = File("${Directory.systemTemp.path}/dart_logger_test.log");
8-
setUp(() async {
9-
await file.create(recursive: true);
10-
});
8+
final file = memory.file("dart_logger_test.log");
119

12-
tearDown(() async {
13-
await file.delete();
10+
tearDown(() {
11+
if (file.existsSync()) {
12+
file.deleteSync();
13+
}
1414
});
1515

1616
test('Real file read and write', () async {

0 commit comments

Comments
 (0)