@@ -62,6 +62,7 @@ enum Command {
6262enum DocsTopic {
6363 Config ,
6464 Behavior ,
65+ Development ,
6566 Security ,
6667}
6768
@@ -151,6 +152,7 @@ fn docs_text(topic: DocsTopic) -> &'static str {
151152 match topic {
152153 DocsTopic :: Config => include_str ! ( "../docs/configuration.md" ) ,
153154 DocsTopic :: Behavior => include_str ! ( "../docs/behavior.md" ) ,
155+ DocsTopic :: Development => include_str ! ( "../docs/development.md" ) ,
154156 DocsTopic :: Security => include_str ! ( "../docs/security.md" ) ,
155157 }
156158}
@@ -345,32 +347,69 @@ mod tests {
345347 format_output_prefix, normalize_internal_log_label, normalize_source_label,
346348 } ;
347349 use clap:: Parser ;
348- use std:: sync:: { Mutex , OnceLock } ;
350+ use std:: ffi:: OsString ;
351+ use std:: sync:: { Mutex , MutexGuard , OnceLock } ;
349352
350353 fn rust_log_lock ( ) -> & ' static Mutex < ( ) > {
351354 static LOCK : OnceLock < Mutex < ( ) > > = OnceLock :: new ( ) ;
352355 LOCK . get_or_init ( || Mutex :: new ( ( ) ) )
353356 }
354357
355- #[ test]
356- fn default_rust_log_uses_info_when_unset ( ) {
357- let _guard = rust_log_lock ( ) . lock ( ) . expect ( "lock RUST_LOG test mutex" ) ;
358+ struct RustLogGuard {
359+ _lock : MutexGuard < ' static , ( ) > ,
360+ original : Option < OsString > ,
361+ }
362+
363+ impl RustLogGuard {
364+ fn set ( value : Option < & str > ) -> Self {
365+ let lock = rust_log_lock ( ) . lock ( ) . expect ( "lock RUST_LOG test mutex" ) ;
366+ let original = std:: env:: var_os ( "RUST_LOG" ) ;
367+ match value {
368+ Some ( value) => set_test_env_var ( "RUST_LOG" , value) ,
369+ None => remove_test_env_var ( "RUST_LOG" ) ,
370+ }
371+ Self {
372+ _lock : lock,
373+ original,
374+ }
375+ }
376+ }
377+
378+ impl Drop for RustLogGuard {
379+ fn drop ( & mut self ) {
380+ match & self . original {
381+ Some ( value) => set_test_env_var ( "RUST_LOG" , value) ,
382+ None => remove_test_env_var ( "RUST_LOG" ) ,
383+ }
384+ }
385+ }
386+
387+ fn set_test_env_var ( key : & str , value : impl AsRef < std:: ffi:: OsStr > ) {
388+ // SAFETY: tests serialize all RUST_LOG mutation through `rust_log_lock`,
389+ // so no concurrent test can observe partially updated process-global state.
358390 unsafe {
359- std:: env:: remove_var ( "RUST_LOG" ) ;
391+ std:: env:: set_var ( key , value ) ;
360392 }
393+ }
394+
395+ fn remove_test_env_var ( key : & str ) {
396+ // SAFETY: tests serialize all RUST_LOG mutation through `rust_log_lock`,
397+ // so removing the variable cannot race with other tests here.
398+ unsafe {
399+ std:: env:: remove_var ( key) ;
400+ }
401+ }
402+
403+ #[ test]
404+ fn default_rust_log_uses_info_when_unset ( ) {
405+ let _guard = RustLogGuard :: set ( None ) ;
361406 assert_eq ! ( default_rust_log( ) , "info" ) ;
362407 }
363408
364409 #[ test]
365410 fn default_rust_log_respects_environment_override ( ) {
366- let _guard = rust_log_lock ( ) . lock ( ) . expect ( "lock RUST_LOG test mutex" ) ;
367- unsafe {
368- std:: env:: set_var ( "RUST_LOG" , "debug,devloop=trace" ) ;
369- }
411+ let _guard = RustLogGuard :: set ( Some ( "debug,devloop=trace" ) ) ;
370412 assert_eq ! ( default_rust_log( ) , "debug,devloop=trace" ) ;
371- unsafe {
372- std:: env:: remove_var ( "RUST_LOG" ) ;
373- }
374413 }
375414
376415 #[ test]
@@ -418,6 +457,14 @@ mod tests {
418457 assert ! ( rendered. contains( "startup_workflows" ) ) ;
419458 }
420459
460+ #[ test]
461+ fn docs_text_uses_embedded_development_reference ( ) {
462+ let rendered = docs_text ( DocsTopic :: Development ) ;
463+
464+ assert ! ( rendered. starts_with( "# Development Guide" ) ) ;
465+ assert ! ( rendered. contains( "DEVLOOP_RUN_WATCH_FLAKE_SMOKE" ) ) ;
466+ }
467+
421468 #[ test]
422469 fn rendered_docs_drop_markdown_heading_markers ( ) {
423470 let rendered = render_docs_text ( DocsTopic :: Config ) ;
@@ -460,4 +507,14 @@ mod tests {
460507 _ => panic ! ( "expected docs subcommand" ) ,
461508 }
462509 }
510+
511+ #[ test]
512+ fn cli_parses_development_docs_subcommand ( ) {
513+ let cli = Cli :: try_parse_from ( [ "devloop" , "docs" , "development" ] ) . expect ( "parse cli" ) ;
514+
515+ match cli. command {
516+ super :: Command :: Docs { topic } => assert ! ( matches!( topic, DocsTopic :: Development ) ) ,
517+ _ => panic ! ( "expected docs subcommand" ) ,
518+ }
519+ }
463520}
0 commit comments