@photostructure/fs-metadata 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/C++_REVIEW_TODO.md +291 -0
- package/CHANGELOG.md +38 -1
- package/CLAUDE.md +346 -0
- package/CONTRIBUTING.md +66 -0
- package/README.md +20 -1
- package/WINDOWS_DEBUG_GUIDE.md +89 -0
- package/binding.gyp +3 -2
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +360 -0
- package/dist/index.d.mts +360 -0
- package/dist/index.d.ts +360 -0
- package/dist/index.mjs +17 -15
- package/dist/index.mjs.map +1 -1
- package/jest.config.cjs +68 -6
- package/package.json +56 -48
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/scripts/check-memory.mjs +123 -0
- package/scripts/clang-tidy.ts +241 -0
- package/scripts/is-platform.mjs +12 -0
- package/scripts/post-build.mjs +21 -0
- package/scripts/prebuild-linux-glibc.sh +108 -0
- package/scripts/precommit.ts +45 -0
- package/scripts/sanitizers-test.sh +157 -0
- package/scripts/{configure.mjs → setup-native.mjs} +4 -1
- package/scripts/valgrind-test.mjs +83 -0
- package/scripts/valgrind-test.sh +85 -0
- package/src/async.ts +3 -3
- package/src/binding.cpp +4 -4
- package/src/common/error_utils.h +17 -0
- package/src/common/metadata_worker.h +4 -1
- package/src/darwin/hidden.cpp +13 -6
- package/src/darwin/volume_metadata.cpp +2 -2
- package/src/darwin/volume_mount_points.cpp +8 -1
- package/src/error.ts +3 -3
- package/src/fs.ts +1 -1
- package/src/glob.ts +2 -2
- package/src/hidden.ts +6 -6
- package/src/index.ts +19 -23
- package/src/linux/blkid_cache.cpp +23 -14
- package/src/linux/dev_disk.ts +2 -2
- package/src/linux/gio_mount_points.cpp +7 -7
- package/src/linux/gio_utils.cpp +19 -8
- package/src/linux/gio_volume_metadata.cpp +15 -15
- package/src/linux/mount_points.ts +9 -9
- package/src/linux/mtab.ts +7 -7
- package/src/linux/volume_metadata.cpp +7 -2
- package/src/object.ts +1 -1
- package/src/options.ts +3 -3
- package/src/path.ts +2 -2
- package/src/platform.ts +25 -0
- package/src/remote_info.ts +5 -5
- package/src/system_volume.ts +8 -8
- package/src/test-utils/assert.ts +2 -2
- package/src/test-utils/benchmark-harness.ts +192 -0
- package/src/test-utils/debuglog-child.ts +30 -4
- package/src/test-utils/debuglog-enabled-child.ts +40 -0
- package/src/test-utils/hidden-tests.ts +1 -1
- package/src/test-utils/jest-setup.ts +14 -0
- package/src/test-utils/platform.ts +3 -3
- package/src/test-utils/worker-thread-helper.cjs +92 -0
- package/src/types/native_bindings.ts +3 -3
- package/src/types/volume_metadata.ts +2 -2
- package/src/unc.ts +2 -2
- package/src/uuid.ts +1 -1
- package/src/volume_health_status.ts +6 -6
- package/src/volume_metadata.ts +20 -23
- package/src/volume_mount_points.ts +12 -17
- package/src/windows/drive_status.h +30 -13
- package/src/windows/hidden.cpp +32 -11
- package/src/windows/volume_metadata.cpp +17 -7
- package/tsup.config.ts +8 -2
- package/dist/types/array.d.ts +0 -25
- package/dist/types/async.d.ts +0 -42
- package/dist/types/debuglog.d.ts +0 -3
- package/dist/types/defer.d.ts +0 -10
- package/dist/types/dirname.d.ts +0 -1
- package/dist/types/error.d.ts +0 -17
- package/dist/types/fs.d.ts +0 -22
- package/dist/types/glob.d.ts +0 -17
- package/dist/types/hidden.d.ts +0 -29
- package/dist/types/index.d.ts +0 -91
- package/dist/types/linux/dev_disk.d.ts +0 -13
- package/dist/types/linux/mount_points.d.ts +0 -6
- package/dist/types/linux/mtab.d.ts +0 -47
- package/dist/types/mount_point.d.ts +0 -2
- package/dist/types/number.d.ts +0 -3
- package/dist/types/object.d.ts +0 -18
- package/dist/types/options.d.ts +0 -33
- package/dist/types/path.d.ts +0 -17
- package/dist/types/platform.d.ts +0 -4
- package/dist/types/random.d.ts +0 -12
- package/dist/types/remote_info.d.ts +0 -6
- package/dist/types/stack_path.d.ts +0 -2
- package/dist/types/string.d.ts +0 -37
- package/dist/types/string_enum.d.ts +0 -19
- package/dist/types/system_volume.d.ts +0 -14
- package/dist/types/types/hidden_metadata.d.ts +0 -32
- package/dist/types/types/mount_point.d.ts +0 -46
- package/dist/types/types/native_bindings.d.ts +0 -51
- package/dist/types/types/options.d.ts +0 -47
- package/dist/types/types/remote_info.d.ts +0 -33
- package/dist/types/types/volume_metadata.d.ts +0 -46
- package/dist/types/unc.d.ts +0 -11
- package/dist/types/units.d.ts +0 -38
- package/dist/types/uuid.d.ts +0 -16
- package/dist/types/volume_health_status.d.ts +0 -24
- package/dist/types/volume_metadata.d.ts +0 -8
- package/dist/types/volume_mount_points.d.ts +0 -6
- package/jest.config.base.cjs +0 -63
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# C++ Code Review TODO List
|
|
2
|
+
|
|
3
|
+
This document outlines a comprehensive review of all C++ files in the fs-metadata project to identify memory leaks, resource management issues, and API usage improvements.
|
|
4
|
+
|
|
5
|
+
## Common Patterns to Review
|
|
6
|
+
|
|
7
|
+
### Memory Management
|
|
8
|
+
- [x] All `malloc`/`free` pairs are properly matched - No malloc/free used, using RAII throughout
|
|
9
|
+
- [x] RAII patterns are used consistently for resource management - Excellent RAII usage across codebase
|
|
10
|
+
- [x] No raw pointers that could leak on exceptions - All resources properly wrapped
|
|
11
|
+
- [x] Smart pointers used appropriately - Consistent use of unique_ptr and custom RAII wrappers
|
|
12
|
+
- [x] All API-allocated resources are properly freed - RAII ensures cleanup
|
|
13
|
+
|
|
14
|
+
### Platform API Usage
|
|
15
|
+
- [x] Verify API availability for target OS versions - APIs match stated OS requirements
|
|
16
|
+
- [x] Check for deprecated API usage - No deprecated N-API functions found
|
|
17
|
+
- [x] Ensure error handling follows platform conventions - Platform-specific error handling implemented
|
|
18
|
+
- [x] Validate string encoding conversions - UTF-8/wide conversions properly sized
|
|
19
|
+
|
|
20
|
+
### Security and Input Validation
|
|
21
|
+
- [x] Comprehensive path validation to prevent directory traversal - Added ".." checks to Windows/Darwin hidden.cpp
|
|
22
|
+
- [ ] Input validation for empty/null mount points across all platforms
|
|
23
|
+
- [x] Buffer size constants defined for platform-specific limits - Fixed Windows buffer sizes
|
|
24
|
+
- [ ] Null byte and special character handling in paths
|
|
25
|
+
|
|
26
|
+
### Thread Safety
|
|
27
|
+
- [x] Replace volatile with std::atomic for thread synchronization - Fixed in drive_status.h
|
|
28
|
+
- [x] Review N-API threadsafe function patterns - All async workers use proper patterns
|
|
29
|
+
- [ ] Document thread safety requirements
|
|
30
|
+
- [x] Ensure proper synchronization in async operations - Using atomic operations with proper memory ordering
|
|
31
|
+
|
|
32
|
+
## File-by-File Review Checklist
|
|
33
|
+
|
|
34
|
+
### 1. `src/binding.cpp` ✅
|
|
35
|
+
- [x] Review NAPI memory management patterns - No issues found
|
|
36
|
+
- [x] Check for proper cleanup in async worker lifecycles - Properly managed
|
|
37
|
+
- [x] Verify promise deferred resolution/rejection paths - Correct implementation
|
|
38
|
+
- [x] Validate string conversions between JS and C++ - Using Napi::String correctly
|
|
39
|
+
- [x] **Added**: const-correctness for Napi::Env variables
|
|
40
|
+
- **Platform APIs verified**: Node-API v9 compatibility
|
|
41
|
+
|
|
42
|
+
### 2. Darwin/macOS Files
|
|
43
|
+
|
|
44
|
+
#### `src/darwin/volume_metadata.cpp` ✅
|
|
45
|
+
- [x] Verify CFRelease for all CoreFoundation objects - Using CFReleaser RAII wrapper
|
|
46
|
+
- DADisk objects properly managed with CFReleaser
|
|
47
|
+
- Disk descriptions properly managed with CFReleaser
|
|
48
|
+
- DASession objects properly managed with CFReleaser
|
|
49
|
+
- [x] Check DiskArbitration API cleanup - RAII ensures cleanup via CFReleaser
|
|
50
|
+
- [x] Review CFStringToString conversion for memory leaks - No leaks, proper string handling
|
|
51
|
+
- [x] Validate statvfs/statfs error handling - Comprehensive error checking
|
|
52
|
+
- [x] Check overflow protection in size calculations - Excellent overflow protection (lines 91-104)
|
|
53
|
+
- **Platform APIs verified**:
|
|
54
|
+
- DiskArbitration framework APIs (macOS 14+) - Proper RAII usage
|
|
55
|
+
- IOKit framework APIs - Not directly used
|
|
56
|
+
- CoreFoundation string handling - CFReleaser ensures proper cleanup
|
|
57
|
+
- **Notes**: Exemplary code with proper RAII, overflow protection, and error handling
|
|
58
|
+
|
|
59
|
+
#### `src/darwin/volume_mount_points.cpp` ✅
|
|
60
|
+
- [x] Review getmntinfo usage and buffer management - Using getmntinfo_r_np with MountBufferRAII
|
|
61
|
+
- [x] Verify CFURLRef lifecycle management - No CFURLRef used in this file
|
|
62
|
+
- [x] Check DADiskCreateFromBSDName cleanup - Not used in this file
|
|
63
|
+
- **Platform APIs verified**:
|
|
64
|
+
- `getmntinfo_r_np` with MNT_NOWAIT for thread safety
|
|
65
|
+
- BSD mount structure compatibility - Properly handled
|
|
66
|
+
- **Notes**: Good use of RAII, async/future for timeout handling, and faccessat for security
|
|
67
|
+
|
|
68
|
+
#### `src/darwin/hidden.cpp` ✅
|
|
69
|
+
- [x] Review NSURL object lifecycle - No NSURL/CFURLRef used, using stat/chflags
|
|
70
|
+
- [x] Check CFURLRef release patterns - Not applicable
|
|
71
|
+
- [x] Verify resource value key handling - Using UF_HIDDEN flag directly
|
|
72
|
+
- **Platform APIs verified**:
|
|
73
|
+
- File attribute APIs (UF_HIDDEN) - Proper usage with stat/chflags
|
|
74
|
+
- Path validation implemented (checks for "..")
|
|
75
|
+
- **Notes**: Simple and correct implementation using BSD APIs
|
|
76
|
+
|
|
77
|
+
### 3. Windows Files
|
|
78
|
+
|
|
79
|
+
#### `src/windows/volume_metadata.cpp` ✅
|
|
80
|
+
- [x] Review WNetConnection RAII wrapper completeness - **FIXED**: Now handles ERROR_MORE_DATA
|
|
81
|
+
- Dynamic buffer resize implemented when ERROR_MORE_DATA is returned
|
|
82
|
+
- [x] Check GetVolumeInformation error handling - **FIXED**: Now uses MAX_PATH+1
|
|
83
|
+
- Using proper VOLUME_NAME_SIZE constant (MAX_PATH+1) for buffers
|
|
84
|
+
- [x] Verify wide string conversion cleanup - Proper PathConverter usage
|
|
85
|
+
- [x] Validate DeviceIoControl buffer management - Not used in this file
|
|
86
|
+
- **Platform APIs verified**:
|
|
87
|
+
- WNet APIs properly handle ERROR_MORE_DATA
|
|
88
|
+
- GetVolumeInformation uses correct MAX_PATH+1 buffers
|
|
89
|
+
- **Notes**: All buffer issues resolved, proper RAII patterns throughout
|
|
90
|
+
|
|
91
|
+
#### `src/windows/volume_mount_points.cpp` ✅
|
|
92
|
+
- [x] Review GetLogicalDrives usage - Proper usage with unique_ptr buffer
|
|
93
|
+
- [x] Check QueryDosDevice buffer allocation - Not used in this file
|
|
94
|
+
- [x] Verify GetVolumePathNamesForVolumeName memory management - Not used in this file
|
|
95
|
+
- **Platform APIs verified**:
|
|
96
|
+
- GetLogicalDriveStringsW - Proper buffer sizing and iteration
|
|
97
|
+
- GetVolumeInformationW - Using MAX_PATH+1 correctly
|
|
98
|
+
- Parallel drive status checking implemented
|
|
99
|
+
- **Notes**: Good implementation with proper buffer management
|
|
100
|
+
|
|
101
|
+
#### `src/windows/hidden.cpp` ✅
|
|
102
|
+
- [x] Review GetFileAttributes error handling - FileAttributeHandler RAII properly throws on error
|
|
103
|
+
- [x] Check SetFileAttributes validation - Proper error checking with exceptions
|
|
104
|
+
- [x] Verify wide string conversions - PathConverter properly handles UTF-8 to wide
|
|
105
|
+
- [x] **Added**: Path validation for directory traversal (".." check)
|
|
106
|
+
- **Platform APIs verified**:
|
|
107
|
+
- GetFileAttributesW/SetFileAttributesW - Proper usage with error handling
|
|
108
|
+
- FILE_ATTRIBUTE_HIDDEN - Correctly manipulated
|
|
109
|
+
- **Notes**: Excellent RAII implementation with FileAttributeHandler, now with path validation
|
|
110
|
+
|
|
111
|
+
### 4. Linux Files (Completed)
|
|
112
|
+
|
|
113
|
+
#### `src/linux/volume_metadata.cpp` ✅
|
|
114
|
+
- [x] Review BlkidCache RAII wrapper - Already excellent
|
|
115
|
+
- [x] Check blkid_get_tag_value free() calls - Correctly using free()
|
|
116
|
+
- [x] Verify GIO integration memory management - Proper smart pointers
|
|
117
|
+
- [x] Validate statvfs error handling - Already comprehensive
|
|
118
|
+
- [x] **Added**: Input validation for empty mount points
|
|
119
|
+
- **Platform APIs verified**:
|
|
120
|
+
- libblkid API availability
|
|
121
|
+
- GIO optional dependency handling
|
|
122
|
+
- statvfs behavior
|
|
123
|
+
|
|
124
|
+
#### `src/linux/blkid_cache.cpp` ✅
|
|
125
|
+
- [x] Verify blkid_cache lifecycle - Already has proper RAII
|
|
126
|
+
- [x] Check error handling for cache operations - Good error handling
|
|
127
|
+
- [x] **Improved**: Double-check pattern in destructor for thread safety
|
|
128
|
+
- [x] **Added**: const-correctness for std::lock_guard instances
|
|
129
|
+
- **Platform APIs verified**:
|
|
130
|
+
- libblkid cache APIs - Proper use of blkid_get_cache/blkid_put_cache
|
|
131
|
+
|
|
132
|
+
#### `src/linux/gio_utils.cpp` (if GIO enabled) ✅
|
|
133
|
+
- [x] Review g_object_unref patterns - Already using GioResource RAII
|
|
134
|
+
- [x] Check GError cleanup - Not used in this file (GError-free patterns)
|
|
135
|
+
- [x] Verify GMount/GVolume reference counting - Proper ref/unref pairs
|
|
136
|
+
- [x] **Added**: Exception handling in forEachMount callback
|
|
137
|
+
- [x] **Added**: Null checks for root.get() before G_IS_FILE
|
|
138
|
+
- [x] **Added**: const-correctness for GioResource and callback results
|
|
139
|
+
- **Platform APIs verified**:
|
|
140
|
+
- GLib/GIO APIs (GNOME) - Proper memory management patterns
|
|
141
|
+
- GVfs mount integration
|
|
142
|
+
|
|
143
|
+
#### `src/linux/gio_mount_points.cpp` (if GIO enabled) ✅
|
|
144
|
+
- [x] Review g_volume_monitor lifecycle - Correct usage (singleton pattern)
|
|
145
|
+
- [x] Check GList cleanup patterns - Using g_list_free_full correctly
|
|
146
|
+
- [x] Verify string ownership - Proper GCharPtr smart pointers
|
|
147
|
+
- [x] **Added**: const-correctness for local variables (GCharPtr, GFileInfoPtr, etc.)
|
|
148
|
+
- **Platform APIs verified**:
|
|
149
|
+
- GVolumeMonitor APIs - Reference counting rules
|
|
150
|
+
- Mount enumeration
|
|
151
|
+
|
|
152
|
+
#### `src/linux/gio_volume_metadata.cpp` (if GIO enabled) ✅
|
|
153
|
+
- [x] Review g_drive object management - Using GObjectPtr smart pointers
|
|
154
|
+
- [x] Check g_icon handling - No GIcon usage in this file
|
|
155
|
+
- [x] Verify string ownership - Proper GCharPtr usage
|
|
156
|
+
- [x] **Added**: Defensive null checks for GObjectPtr::get()
|
|
157
|
+
- [x] **Added**: Null checks for string getters
|
|
158
|
+
- [x] **Added**: const-correctness for all GCharPtr and GObjectPtr locals
|
|
159
|
+
- **Platform APIs verified**:
|
|
160
|
+
- GDrive/GVolume metadata APIs - Ownership transfer rules
|
|
161
|
+
|
|
162
|
+
## Testing Strategy
|
|
163
|
+
|
|
164
|
+
### Memory Leak Detection ✅
|
|
165
|
+
1. [x] Run valgrind, LeakSanitizer, and AddressSanitizer on Linux builds
|
|
166
|
+
- **Implemented**: Comprehensive memory testing infrastructure
|
|
167
|
+
- Valgrind: `npm run test:valgrind` with suppressions in `.valgrind.supp`
|
|
168
|
+
- AddressSanitizer: `npm run asan` with proper clang integration
|
|
169
|
+
- LeakSanitizer: Integrated with ASan, suppressions in `.lsan-suppressions.txt`
|
|
170
|
+
- All tests show 0 memory leaks in fs-metadata code
|
|
171
|
+
2. [ ] Use Application Verifier on Windows
|
|
172
|
+
3. [ ] Enable address sanitizer for macOS builds
|
|
173
|
+
4. [x] Run existing memory.test.ts with extended iterations
|
|
174
|
+
- JavaScript memory tests: `npm run test:memory`
|
|
175
|
+
- Comprehensive suite: `npm run tests:memory`
|
|
176
|
+
|
|
177
|
+
### API Verification
|
|
178
|
+
1. [ ] Use Perplexity/WebSearch to verify:
|
|
179
|
+
- Minimum OS version requirements for each API
|
|
180
|
+
- Deprecated API alternatives
|
|
181
|
+
- Thread safety guarantees
|
|
182
|
+
- Memory ownership rules
|
|
183
|
+
2. [ ] Cross-reference with official documentation:
|
|
184
|
+
- Apple Developer Documentation
|
|
185
|
+
- Microsoft Win32 API Reference
|
|
186
|
+
- Linux man pages and library docs
|
|
187
|
+
|
|
188
|
+
### Resource Management
|
|
189
|
+
1. [ ] Add stress tests for mount/unmount cycles
|
|
190
|
+
2. [ ] Test with network filesystem timeouts
|
|
191
|
+
3. [ ] Verify cleanup on exception paths
|
|
192
|
+
4. [ ] Test with maximum path lengths
|
|
193
|
+
|
|
194
|
+
## Priority Items
|
|
195
|
+
|
|
196
|
+
1. ~~**Critical**: Thread safety in Windows DriveHealthChecker (src/windows/drive_status.h)~~ ✅ Completed
|
|
197
|
+
- ✅ Replaced `volatile bool shouldTerminate` with `std::atomic<bool>`
|
|
198
|
+
- ✅ Replaced `DriveStatus result` with `std::atomic<DriveStatus>`
|
|
199
|
+
- ✅ Removed dangerous `TerminateThread` usage
|
|
200
|
+
- ✅ Increased graceful shutdown timeout from 100ms to 1000ms
|
|
201
|
+
2. ~~**High**: Windows API buffer issues~~ ✅ Completed
|
|
202
|
+
- ✅ WNetConnection: Handle ERROR_MORE_DATA with dynamic buffer resize
|
|
203
|
+
- ✅ GetVolumeInformation: Use MAX_PATH+1 instead of BUFFER_SIZE
|
|
204
|
+
3. ~~**High**: CoreFoundation reference counting in macOS code~~ ✅ Completed - CFReleaser RAII wrapper
|
|
205
|
+
4. ~~**High**: GIO object lifecycle management~~ ✅ Completed - Using smart pointers throughout
|
|
206
|
+
5. ~~**Medium**: Security - Add comprehensive path validation~~ ✅ Completed
|
|
207
|
+
- ✅ Check for ".." in all platforms (Windows/Darwin have it, Linux doesn't have hidden file support)
|
|
208
|
+
- Validate against null bytes and special characters (remaining)
|
|
209
|
+
- Add input validation for empty mount points (remaining)
|
|
210
|
+
6. ~~**Medium**: String encoding conversions across platforms~~ ✅ Completed - Proper UTF-8/wide conversions
|
|
211
|
+
7. ~~**Medium**: Buffer overflow protection in size calculations~~ ✅ Completed - Darwin has exemplary protection
|
|
212
|
+
|
|
213
|
+
### Completed Items
|
|
214
|
+
- ✅ Linux memory management review (all files)
|
|
215
|
+
- ✅ Darwin/macOS memory management review (all files)
|
|
216
|
+
- ✅ Windows memory management review (all files including drive_status.h)
|
|
217
|
+
- ✅ const-correctness improvements across codebase
|
|
218
|
+
- ✅ Memory leak detection infrastructure (Valgrind + ASan)
|
|
219
|
+
- ✅ Static analysis integration (clang-tidy)
|
|
220
|
+
- ✅ Comprehensive memory testing documentation
|
|
221
|
+
- ✅ RAII patterns verified across all platforms
|
|
222
|
+
- ✅ N-API usage verified - no deprecated functions
|
|
223
|
+
- ✅ Platform API compatibility verified
|
|
224
|
+
- ✅ Thread safety issues resolved (std::atomic usage)
|
|
225
|
+
- ✅ Windows API buffer handling fixed
|
|
226
|
+
- ✅ Path validation for directory traversal added
|
|
227
|
+
|
|
228
|
+
## Recent Improvements (Completed)
|
|
229
|
+
|
|
230
|
+
### Code Quality
|
|
231
|
+
1. **const-correctness**: Added `const` qualifiers to all appropriate local variables across the codebase
|
|
232
|
+
- All Napi::Env instances
|
|
233
|
+
- All GCharPtr, GObjectPtr, GFileInfoPtr instances
|
|
234
|
+
- All std::lock_guard instances
|
|
235
|
+
- Improves code safety and enables compiler optimizations
|
|
236
|
+
|
|
237
|
+
2. **Static Analysis**: Integrated clang-tidy
|
|
238
|
+
- Added to CI/CD pipeline
|
|
239
|
+
- Uses bear to generate compile_commands.json
|
|
240
|
+
- Runs only on platform-relevant files
|
|
241
|
+
- Added to precommit checks
|
|
242
|
+
|
|
243
|
+
3. **Memory Testing Infrastructure**
|
|
244
|
+
- Created comprehensive memory testing documentation (`docs/MEMORY_TESTING.md`)
|
|
245
|
+
- Improved ASan configuration with proper suppressions
|
|
246
|
+
- Created standalone test runner (`scripts/sanitizers-test.sh`)
|
|
247
|
+
- Updated `scripts/check-memory.mjs` with better ASan support
|
|
248
|
+
- All memory tests integrated into CI/CD
|
|
249
|
+
|
|
250
|
+
## Documentation Updates Needed
|
|
251
|
+
|
|
252
|
+
- [x] Document RAII pattern usage guidelines - See existing code patterns
|
|
253
|
+
- [x] Add platform-specific memory management notes - Added to MEMORY_TESTING.md
|
|
254
|
+
- [x] Create error handling best practices - Documented in code
|
|
255
|
+
- [ ] Document thread safety requirements
|
|
256
|
+
|
|
257
|
+
## Platform API Resources
|
|
258
|
+
|
|
259
|
+
### macOS/Darwin
|
|
260
|
+
- Core Foundation Memory Management: Functions with "Create" or "Copy" require CFRelease
|
|
261
|
+
- DiskArbitration: Follows standard Core Foundation ownership rules
|
|
262
|
+
- Key Documentation: Apple Developer Documentation for DiskArbitration framework
|
|
263
|
+
- **Review Status**: ✅ All files use proper RAII with CFReleaser template
|
|
264
|
+
|
|
265
|
+
### Windows
|
|
266
|
+
- WNetGetConnection: Start with 256 chars, handle ERROR_MORE_DATA
|
|
267
|
+
- GetVolumeInformation: Fixed buffers of MAX_PATH+1
|
|
268
|
+
- Key Documentation: Microsoft Learn Win32 API Reference
|
|
269
|
+
- **Review Status**: ⚠️ Buffer handling needs fixes, thread safety critical issue
|
|
270
|
+
|
|
271
|
+
### Linux
|
|
272
|
+
- libblkid: Use free() for blkid_get_tag_value results, blkid_put_cache() for cache
|
|
273
|
+
- GLib/GIO: Use g_object_unref() or g_clear_object(), match allocation functions
|
|
274
|
+
- Key Documentation: libblkid man pages, GNOME Developer Documentation
|
|
275
|
+
- **Review Status**: ✅ All files properly reviewed and use smart pointers
|
|
276
|
+
|
|
277
|
+
## Critical Windows Thread Safety Issue ✅ FIXED
|
|
278
|
+
|
|
279
|
+
File: `src/windows/drive_status.h`
|
|
280
|
+
```cpp
|
|
281
|
+
// Fixed code:
|
|
282
|
+
std::atomic<bool> shouldTerminate{false}; // Using atomic with proper memory ordering
|
|
283
|
+
std::atomic<DriveStatus> result{DriveStatus::Unknown}; // Thread-safe result storage
|
|
284
|
+
// Removed TerminateThread - now uses graceful shutdown with 1000ms timeout
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
This critical issue has been resolved:
|
|
288
|
+
- ✅ Race conditions eliminated with std::atomic
|
|
289
|
+
- ✅ Removed dangerous TerminateThread call
|
|
290
|
+
- ✅ Proper memory ordering with acquire/release semantics
|
|
291
|
+
- ✅ Graceful thread shutdown with increased timeout
|
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,43 @@ Fixed for any bug fixes.
|
|
|
14
14
|
Security in case of vulnerabilities.
|
|
15
15
|
-->
|
|
16
16
|
|
|
17
|
+
## [0.6.0] - 2025-06-09
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Comprehensive memory testing framework with Valgrind and AddressSanitizer support
|
|
22
|
+
- Thread safety tests for Windows platform
|
|
23
|
+
- Platform-specific build scripts with automatic OS detection
|
|
24
|
+
- Clang-tidy integration for C++ code analysis
|
|
25
|
+
- Worker thread helper for volume metadata operations
|
|
26
|
+
- NAPI_VERSION=9 definition for improved compatibility
|
|
27
|
+
- Dynamic test timeout configuration based on environment (CI, platform, architecture)
|
|
28
|
+
- Prebuild script for Linux GLIBC compatibility in Docker environments
|
|
29
|
+
|
|
30
|
+
### Breaking
|
|
31
|
+
|
|
32
|
+
- Dropped support for Node.js v18, added support for Node.js v24
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- Simplified ESM/CJS dual module support with unified build configuration
|
|
37
|
+
- Enhanced test coverage with additional error handling and edge case tests
|
|
38
|
+
- Updated all imports to use `node:` prefix for built-in modules
|
|
39
|
+
- Reorganized build and test scripts for better clarity
|
|
40
|
+
- Improved memory test workflow for cross-platform compatibility
|
|
41
|
+
- Renamed native module target from `node_fs_meta` to `fs_metadata` for consistency
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Added path validation to prevent directory traversal vulnerabilities in hidden file operations
|
|
46
|
+
- Improved error handling and null checks across Linux GIO implementation
|
|
47
|
+
- Fixed buffer allocation issues in Windows networking and volume operations
|
|
48
|
+
- Enhanced resource management with better validation for empty mount points
|
|
49
|
+
- Made `SystemPathPatternsDefault` values visible in TypeScript typings
|
|
50
|
+
- Added Napi::HandleScope to OnOK and OnError methods for proper scope management
|
|
51
|
+
- Removed unnecessary std::move operations in worker implementations
|
|
52
|
+
- Resolved CI test reliability issues across different environments, particularly Alpine ARM64 emulation timeouts
|
|
53
|
+
|
|
17
54
|
## [0.4.0] - 2025-01-09
|
|
18
55
|
|
|
19
56
|
- `Fixed`: Switch to thread-safe `getmntinfo_r_np()` for macOS. Improved darwin resource management.
|
|
@@ -23,7 +60,7 @@ Security in case of vulnerabilities.
|
|
|
23
60
|
- `Packaging`: Improved ESM/CJS support with common `__dirname` implementation thanks to `tsup` [shims](https://tsup.egoist.dev/#inject-cjs-and-esm-shims).
|
|
24
61
|
|
|
25
62
|
This change simplifies the implementation and improves inline jsdocs as the exported code and docs have been inlined.
|
|
26
|
-
|
|
63
|
+
|
|
27
64
|
- `Packaging`: Re-enabled test coverage assertions (after finding the magicks to get istanbul to see what the tests were exercising)
|
|
28
65
|
|
|
29
66
|
- `Packaging`: Added debuglog tests
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is @photostructure/fs-metadata - a cross-platform native Node.js module for retrieving filesystem metadata, including mount points, volume information, and space utilization statistics.
|
|
8
|
+
|
|
9
|
+
### Key Features
|
|
10
|
+
- Cross-platform support: Windows 10+ (x64), macOS 14+, Ubuntu 22+ (x64, arm64)
|
|
11
|
+
- Lists all mounted volumes/drives
|
|
12
|
+
- Gets detailed volume metadata (size, usage, filesystem type, etc.)
|
|
13
|
+
- Hidden file/directory attribute support (get/set)
|
|
14
|
+
- Non-blocking async native implementations
|
|
15
|
+
- Timeout handling for unresponsive network volumes
|
|
16
|
+
- ESM and CJS module support
|
|
17
|
+
- Full TypeScript type definitions
|
|
18
|
+
- Worker threads support with proper context isolation
|
|
19
|
+
|
|
20
|
+
### Platform-Specific Notes
|
|
21
|
+
- **Linux**: Optional GIO/GVfs mount support via Gnome libraries
|
|
22
|
+
- **Windows**: Uses separate threads per mountpoint for health checks to handle blocked system calls
|
|
23
|
+
- **System Volumes**: Each platform handles system volumes differently; the library uses heuristics to identify them
|
|
24
|
+
|
|
25
|
+
## Architecture Overview
|
|
26
|
+
|
|
27
|
+
### Core Structure
|
|
28
|
+
- **Native binding layer** (`src/binding.cpp`): Node-API v9 bridge between JavaScript and platform-specific implementations
|
|
29
|
+
- **Platform abstractions** in `src/common/`: Shared C++ interfaces for cross-platform functionality
|
|
30
|
+
- **Platform implementations**:
|
|
31
|
+
- `src/darwin/`: macOS-specific code using Core Foundation APIs
|
|
32
|
+
- `src/linux/`: Linux-specific code with optional GIO support for GNOME/GVfs
|
|
33
|
+
- `src/windows/`: Windows-specific code using Win32 APIs
|
|
34
|
+
|
|
35
|
+
### Key Features
|
|
36
|
+
- **Volume Metadata**: Retrieves filesystem information (mount points, disk usage, health status)
|
|
37
|
+
- **Hidden File Support**: Cross-platform hidden file detection and manipulation
|
|
38
|
+
- **Async Operations**: All native operations use Worker threads to avoid blocking
|
|
39
|
+
|
|
40
|
+
### Build System
|
|
41
|
+
- Uses `node-gyp` for native compilation
|
|
42
|
+
- Conditionally enables GIO support on Linux via `scripts/configure.mjs`
|
|
43
|
+
- Provides prebuilt binaries via `prebuildify` for common platforms
|
|
44
|
+
- Supports both ESM and CJS module formats
|
|
45
|
+
|
|
46
|
+
### Cross-Module Compatibility
|
|
47
|
+
- **Directory Path Resolution**: Use `_dirname()` from `./dirname` instead of `__dirname`
|
|
48
|
+
- Works in both CommonJS and ESM contexts
|
|
49
|
+
- Handles Jest test environments correctly
|
|
50
|
+
- Example: `const dir = _dirname()` instead of `const dir = __dirname`
|
|
51
|
+
|
|
52
|
+
### Testing Strategy
|
|
53
|
+
- Jest for both CJS and ESM test suites
|
|
54
|
+
- Platform-specific test expectations handled via `isWindows`, `isMacOS`, `isLinux` helpers
|
|
55
|
+
- Memory leak testing with garbage collection monitoring
|
|
56
|
+
- Coverage thresholds: 80% for all metrics
|
|
57
|
+
|
|
58
|
+
### Testing File System Metadata
|
|
59
|
+
- **Important**: File system metadata like `available` space, `used` space, and other dynamic properties change continuously as other processes run on the machine
|
|
60
|
+
- Tests should **never** expect exact equality for these values between multiple calls
|
|
61
|
+
- **Do not** make range assertions (e.g., `available > 0` or `used < size`) because:
|
|
62
|
+
- Files can be created or deleted between calls (potentially gigabytes)
|
|
63
|
+
- The changes can be dramatic and unpredictable
|
|
64
|
+
- Instead, for dynamic metadata:
|
|
65
|
+
- Only verify the value exists and has the correct type
|
|
66
|
+
- Use `typeof result.available === 'number'` rather than range checks
|
|
67
|
+
- Focus on testing static properties (e.g., `size`, `mountFrom`, `fstype`) for exact equality
|
|
68
|
+
- Consider using snapshot testing only for stable properties
|
|
69
|
+
|
|
70
|
+
### Timeout Handling
|
|
71
|
+
- Default timeout for volume operations to handle unresponsive network mounts
|
|
72
|
+
- Windows uses separate threads per mountpoint for health checks
|
|
73
|
+
- Configurable via `Options` interface
|
|
74
|
+
|
|
75
|
+
### Debug Logging
|
|
76
|
+
- Enable with `NODE_DEBUG=fs-meta` or `NODE_DEBUG=photostructure:fs-metadata`
|
|
77
|
+
- Debug messages from both JavaScript and native code are sent to `stderr`
|
|
78
|
+
- Uses native Node.js debuglog for determining if logging is enabled
|
|
79
|
+
|
|
80
|
+
## Example Usage
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { getVolumeMountPoints, getVolumeMetadata } from "@photostructure/fs-metadata";
|
|
84
|
+
|
|
85
|
+
// List all mounted volumes
|
|
86
|
+
const mountPoints = await getVolumeMountPoints();
|
|
87
|
+
console.dir({ mountPoints });
|
|
88
|
+
|
|
89
|
+
// Get metadata for a specific volume
|
|
90
|
+
const volumeMetadata = await getVolumeMetadata(mountPoints[0]);
|
|
91
|
+
console.dir({ volumeMetadata });
|
|
92
|
+
|
|
93
|
+
// Check if a file is hidden
|
|
94
|
+
import { isHidden } from "@photostructure/fs-metadata";
|
|
95
|
+
const hidden = await isHidden("/path/to/file");
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## CI/CD Test Reliability Guidelines
|
|
99
|
+
|
|
100
|
+
Based on analysis of recent test failures, here are critical patterns to avoid flaky tests:
|
|
101
|
+
|
|
102
|
+
### 1. Benchmark and Performance Tests
|
|
103
|
+
- **Problem**: "Cannot log after tests are done" errors in worker_threads.test.ts
|
|
104
|
+
- **Solution**:
|
|
105
|
+
- Always await all async operations before test completion
|
|
106
|
+
- Use proper test lifecycle hooks (afterEach/afterAll) for cleanup
|
|
107
|
+
- Avoid console.log in async contexts without proper synchronization
|
|
108
|
+
- Consider using test.concurrent with explicit done() callbacks
|
|
109
|
+
|
|
110
|
+
### 2. Alpine Linux ARM64 Issues
|
|
111
|
+
- **Problem**: Tests timeout on emulated Alpine ARM64 environments
|
|
112
|
+
- **Solution**:
|
|
113
|
+
- Skip process-spawning tests on Alpine ARM64 (`if (isAlpine && isARM64)`)
|
|
114
|
+
- Use increased timeout multipliers (20x) for emulated environments
|
|
115
|
+
- Detect emulation via `/proc/cpuinfo` or environment checks
|
|
116
|
+
- Consider separate test suites for native vs emulated environments
|
|
117
|
+
|
|
118
|
+
### 3. Worker Thread Management
|
|
119
|
+
- **Problem**: Race conditions in concurrent worker operations
|
|
120
|
+
- **Solution**:
|
|
121
|
+
- Implement proper worker pool management with size limits
|
|
122
|
+
- Use Promise.allSettled() instead of Promise.all() for parallel operations
|
|
123
|
+
- Add explicit cleanup in test teardown to terminate all workers
|
|
124
|
+
- Set reasonable concurrency limits based on environment (CPU cores)
|
|
125
|
+
|
|
126
|
+
### 4. Timeout Test Reliability
|
|
127
|
+
- **Problem**: Timeout tests fail due to timing precision issues
|
|
128
|
+
- **Solution**:
|
|
129
|
+
- Never use exact timing assertions (e.g., expect 100ms)
|
|
130
|
+
- Use ranges with adequate margins (e.g., 90-110ms)
|
|
131
|
+
- Account for CI environment variability (slower machines)
|
|
132
|
+
- Consider mocking timers for deterministic behavior
|
|
133
|
+
|
|
134
|
+
### 5. File System Operations
|
|
135
|
+
- **Problem**: ENOENT errors for test directories, permission issues
|
|
136
|
+
- **Solution**:
|
|
137
|
+
- Always use unique temporary directories per test
|
|
138
|
+
- Clean up test artifacts in afterEach hooks
|
|
139
|
+
- Check directory existence before operations
|
|
140
|
+
- Handle platform-specific path separators
|
|
141
|
+
|
|
142
|
+
### 6. Memory and Resource Leaks
|
|
143
|
+
- **Problem**: Tests don't properly clean up resources
|
|
144
|
+
- **Solution**:
|
|
145
|
+
- Explicitly close all file handles, network connections
|
|
146
|
+
- Use try-finally blocks for resource cleanup
|
|
147
|
+
- Monitor memory usage in long-running tests
|
|
148
|
+
- Implement proper garbage collection triggers
|
|
149
|
+
|
|
150
|
+
### 7. Platform-Specific Failures
|
|
151
|
+
- **Problem**: Different behavior across Windows/macOS/Linux
|
|
152
|
+
- **Solution**:
|
|
153
|
+
- Use platform detection helpers consistently
|
|
154
|
+
- Skip platform-specific tests appropriately
|
|
155
|
+
- Account for filesystem differences (case sensitivity, path formats)
|
|
156
|
+
- Test with platform-specific CI matrices
|
|
157
|
+
|
|
158
|
+
### 8. Jest Configuration
|
|
159
|
+
- **Problem**: Tests interfere with each other
|
|
160
|
+
- **Solution**:
|
|
161
|
+
- Use `--runInBand` for tests with shared resources
|
|
162
|
+
- Clear module cache between tests when needed
|
|
163
|
+
- Isolate tests that spawn processes
|
|
164
|
+
- Configure proper test timeouts per environment
|
|
165
|
+
|
|
166
|
+
### Async Cleanup Anti-Patterns
|
|
167
|
+
|
|
168
|
+
**IMPORTANT**: The following approaches are NOT valid solutions for async cleanup issues:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
// BAD: Arbitrary timeouts in tests
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
173
|
+
|
|
174
|
+
// BAD: Forcing garbage collection
|
|
175
|
+
if (global.gc) {
|
|
176
|
+
global.gc();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// BAD: Adding setImmediate in afterAll to "fix" hanging tests
|
|
180
|
+
afterAll(async () => {
|
|
181
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Why these are problematic:**
|
|
186
|
+
|
|
187
|
+
1. **Arbitrary timeouts** are race conditions waiting to happen. They might work on fast machines but fail on slower CI runners.
|
|
188
|
+
2. **Forcing GC** should never be required for correct behavior. If your code depends on GC for correctness, it has a fundamental design flaw.
|
|
189
|
+
3. **setImmediate/nextTick delays** in cleanup hooks don't fix the root cause - they just paper over the real issue.
|
|
190
|
+
4. These approaches mask the real problem instead of fixing it.
|
|
191
|
+
|
|
192
|
+
**Note**: This is different from legitimate uses of timeouts, such as:
|
|
193
|
+
|
|
194
|
+
- Waiting for time to pass to test timestamp changes
|
|
195
|
+
- Rate limiting or throttling tests
|
|
196
|
+
- Testing timeout behavior itself
|
|
197
|
+
|
|
198
|
+
The anti-pattern is using timeouts or GC to "fix" async cleanup issues.
|
|
199
|
+
|
|
200
|
+
**What to do instead:**
|
|
201
|
+
|
|
202
|
+
1. Find the actual resource that's keeping the process alive (use `--detectOpenHandles`)
|
|
203
|
+
2. Ensure all database connections are properly closed
|
|
204
|
+
3. Ensure all file handles are closed
|
|
205
|
+
4. Cancel or await all pending async operations
|
|
206
|
+
5. Use proper resource management patterns (RAII, try-finally, using statements)
|
|
207
|
+
|
|
208
|
+
### Windows-Compatible Directory Cleanup
|
|
209
|
+
|
|
210
|
+
**IMPORTANT**: Never use `fs.rmSync()` or `fs.rm()` without proper Windows retry logic for directory cleanup in tests.
|
|
211
|
+
|
|
212
|
+
**Problem**: On Windows, file handles and directory locks can remain active longer than on Unix systems, causing `EBUSY` errors during cleanup.
|
|
213
|
+
|
|
214
|
+
**Proper Solution**: Use `fsp.rm()` (async) with retry options:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
await fsp.rm(tempDir, {
|
|
218
|
+
recursive: true,
|
|
219
|
+
force: true,
|
|
220
|
+
maxRetries: process.platform === "win32" ? 3 : 1,
|
|
221
|
+
retryDelay: process.platform === "win32" ? 100 : 0,
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Best Practice**: Use existing test utilities that handle Windows-compatible cleanup patterns. Don't manually clean up temp directories - let the test framework handle it with proper retry logic.
|
|
226
|
+
|
|
227
|
+
### Adaptive Timeout Testing
|
|
228
|
+
|
|
229
|
+
**Problem**: Fixed timeouts don't account for varying CI environment performance.
|
|
230
|
+
|
|
231
|
+
**Root Causes**:
|
|
232
|
+
|
|
233
|
+
- Alpine Linux (musl libc) is 2x slower than glibc
|
|
234
|
+
- ARM64 emulation on x64 runners is 5x slower
|
|
235
|
+
- Windows process operations are 4x slower
|
|
236
|
+
- macOS VMs are 4x slower
|
|
237
|
+
- CI environments have resource constraints
|
|
238
|
+
|
|
239
|
+
**Solutions**:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// DON'T: Use fixed timeouts
|
|
243
|
+
test("my test", async () => {
|
|
244
|
+
// Test code
|
|
245
|
+
}, 10000);
|
|
246
|
+
|
|
247
|
+
// DO: Use adaptive timeouts based on environment
|
|
248
|
+
import { getTestTimeout } from "./test-utils/test-timeout-config";
|
|
249
|
+
|
|
250
|
+
test(
|
|
251
|
+
"my test",
|
|
252
|
+
async () => {
|
|
253
|
+
// Test code
|
|
254
|
+
},
|
|
255
|
+
getTestTimeout(10000),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// DO: Account for platform timing differences
|
|
259
|
+
const timingMultiplier = process.platform === "win32" ? 4 :
|
|
260
|
+
process.platform === "darwin" ? 4 :
|
|
261
|
+
process.env.CI ? 2 : 1;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Multi-Process Test Synchronization
|
|
265
|
+
|
|
266
|
+
**Problem**: Multi-process tests failing due to race conditions between process startup and test assertions.
|
|
267
|
+
|
|
268
|
+
**Root Cause**: The timing between process startup and resource acquisition varies significantly by platform.
|
|
269
|
+
|
|
270
|
+
**Solutions**:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// DON'T: Assume immediate process readiness
|
|
274
|
+
const proc = spawn(nodeCmd, [script]);
|
|
275
|
+
const result = await waitForProcessResult(proc);
|
|
276
|
+
expect(result).toBe("expected_outcome"); // May fail due to timing
|
|
277
|
+
|
|
278
|
+
// DO: Use explicit synchronization signals
|
|
279
|
+
const script = `
|
|
280
|
+
// Setup code
|
|
281
|
+
console.log("READY"); // Signal readiness
|
|
282
|
+
// Main test logic
|
|
283
|
+
console.log("RESULT:" + outcome);
|
|
284
|
+
`;
|
|
285
|
+
|
|
286
|
+
const proc = spawn(process.execPath, ["-e", script]);
|
|
287
|
+
await waitForOutput(proc, "READY"); // Wait for process to be ready
|
|
288
|
+
const result = await waitForOutput(proc, "RESULT:");
|
|
289
|
+
expect(result.split(":")[1]).toBe("expected_outcome");
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Wait-for-Condition Pattern
|
|
293
|
+
|
|
294
|
+
**Problem**: Tests failing because they don't wait for asynchronous conditions to be met.
|
|
295
|
+
|
|
296
|
+
**Solution**: Implement robust condition waiting with platform-aware timing:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
async function waitForCondition(
|
|
300
|
+
check: () => boolean | Promise<boolean>,
|
|
301
|
+
options: {
|
|
302
|
+
maxAttempts?: number;
|
|
303
|
+
delay?: number;
|
|
304
|
+
timeoutMs?: number;
|
|
305
|
+
} = {}
|
|
306
|
+
) {
|
|
307
|
+
const {
|
|
308
|
+
maxAttempts = 50,
|
|
309
|
+
delay = 100,
|
|
310
|
+
timeoutMs = 30000
|
|
311
|
+
} = options;
|
|
312
|
+
|
|
313
|
+
const startTime = Date.now();
|
|
314
|
+
|
|
315
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
316
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
317
|
+
throw new Error(`Condition not met within ${timeoutMs}ms`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (await check()) return true;
|
|
321
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Usage example
|
|
328
|
+
await waitForCondition(
|
|
329
|
+
() => fs.existsSync(expectedFile),
|
|
330
|
+
{ timeoutMs: getTestTimeout(10000) }
|
|
331
|
+
);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Best Practices Summary
|
|
335
|
+
1. **Always clean up**: Resources, timers, workers, file handles
|
|
336
|
+
2. **Never assume timing**: Use ranges and adaptive timeouts, not exact values
|
|
337
|
+
3. **Isolate tests**: Each test should be independent
|
|
338
|
+
4. **Platform awareness**: Skip tests that can't work on certain platforms
|
|
339
|
+
5. **Proper async handling**: Always await or return promises
|
|
340
|
+
6. **Resource limits**: Don't spawn unlimited workers/processes
|
|
341
|
+
7. **Environment detection**: Adjust behavior for CI vs local
|
|
342
|
+
8. **Deterministic tests**: Mock external dependencies when possible
|
|
343
|
+
9. **Explicit synchronization**: Use signals for multi-process coordination
|
|
344
|
+
10. **Robust waiting**: Use condition-based waiting instead of arbitrary timeouts
|
|
345
|
+
11. **Windows compatibility**: Use retry logic for file operations on Windows
|
|
346
|
+
12. **Anti-pattern awareness**: Avoid masking problems with timeouts or forced GC
|