@@ -6,7 +6,11 @@ use std::process::Command;
6
6
use goblin:: Object ;
7
7
use tempfile:: { tempdir, TempDir } ;
8
8
9
- use crate :: build:: build;
9
+ use crate :: { build:: build, make:: make} ;
10
+
11
+ static VMLINUX : & ' static str = include_str ! ( "../test_data/vmlinux.h" ) ;
12
+ static BPF_HELPERS : & ' static str = include_str ! ( "../test_data/bpf_helpers.h" ) ;
13
+ static BPF_HELPER_DEFS : & ' static str = include_str ! ( "../test_data/bpf_helper_defs.h" ) ;
10
14
11
15
/// Creates a temporary directory and initializes a default cargo project inside.
12
16
///
@@ -89,6 +93,22 @@ fn validate_bpf_o(path: &Path) {
89
93
}
90
94
}
91
95
96
+ /// Returns the path to the local libbpf-rs
97
+ ///
98
+ /// Warning: hacky! But necessary to run tests. We assume that the current working directory is
99
+ /// libbpf-cargo project root. Hopefully this is a cargo-provided invariant. I tried using the
100
+ /// file!() macro but it returns a relative path and seems even hackier to make work.
101
+ fn get_libbpf_rs_path ( ) -> PathBuf {
102
+ let cwd = std:: env:: current_dir ( ) . expect ( "failed to get cwd" ) ;
103
+
104
+ Path :: new ( & cwd)
105
+ . parent ( )
106
+ . expect ( "failed to get parent of cwd" )
107
+ . join ( "libbpf-rs" )
108
+ . canonicalize ( )
109
+ . expect ( "failed to canonicalize libbpf-rs" )
110
+ }
111
+
92
112
#[ test]
93
113
fn test_build_default ( ) {
94
114
let ( _dir, proj_dir, cargo_toml) = setup_temp_project ( ) ;
@@ -263,3 +283,298 @@ fn test_build_workspace_collision() {
263
283
0
264
284
) ;
265
285
}
286
+
287
+ #[ test]
288
+ fn test_make_basic ( ) {
289
+ let ( _dir, proj_dir, cargo_toml) = setup_temp_project ( ) ;
290
+
291
+ // Add prog dir
292
+ create_dir ( proj_dir. join ( "src/bpf" ) ) . expect ( "failed to create prog dir" ) ;
293
+
294
+ // Add a prog
295
+ let _prog_file =
296
+ File :: create ( proj_dir. join ( "src/bpf/prog.bpf.c" ) ) . expect ( "failed to create prog file" ) ;
297
+
298
+ assert_eq ! (
299
+ make(
300
+ true ,
301
+ Some ( & cargo_toml) ,
302
+ Path :: new( "/bin/clang" ) ,
303
+ true ,
304
+ true ,
305
+ Vec :: new( )
306
+ ) ,
307
+ 0
308
+ ) ;
309
+
310
+ // Validate generated object file
311
+ validate_bpf_o ( proj_dir. as_path ( ) . join ( "target/bpf/prog.bpf.o" ) . as_path ( ) ) ;
312
+
313
+ // Check that skeleton exists (other tests will check for skeleton validity)
314
+ assert ! ( proj_dir
315
+ . as_path( )
316
+ . join( "src/bpf/prog.skel.rs" )
317
+ . as_path( )
318
+ . exists( ) ) ;
319
+ }
320
+
321
+ #[ test]
322
+ fn test_make_workspace ( ) {
323
+ let ( _dir, workspace_dir, workspace_cargo_toml, proj_one_dir, proj_two_dir) =
324
+ setup_temp_workspace ( ) ;
325
+
326
+ // Create bpf prog for project one
327
+ create_dir ( proj_one_dir. join ( "src/bpf" ) ) . expect ( "failed to create prog dir" ) ;
328
+ let _prog_file_1 = File :: create ( proj_one_dir. join ( "src/bpf/prog1.bpf.c" ) )
329
+ . expect ( "failed to create prog file 1" ) ;
330
+
331
+ // Create bpf prog for project two, same name as project one
332
+ create_dir ( proj_two_dir. join ( "src/bpf" ) ) . expect ( "failed to create prog dir" ) ;
333
+ let _prog_file_2 = File :: create ( proj_two_dir. join ( "src/bpf/prog2.bpf.c" ) )
334
+ . expect ( "failed to create prog file 2" ) ;
335
+
336
+ assert_eq ! (
337
+ make(
338
+ true ,
339
+ Some ( & workspace_cargo_toml) ,
340
+ Path :: new( "/bin/clang" ) ,
341
+ true ,
342
+ true ,
343
+ Vec :: new( )
344
+ ) ,
345
+ 0
346
+ ) ;
347
+
348
+ // Validate generated object files
349
+ validate_bpf_o (
350
+ workspace_dir
351
+ . as_path ( )
352
+ . join ( "target/bpf/prog1.bpf.o" )
353
+ . as_path ( ) ,
354
+ ) ;
355
+ validate_bpf_o (
356
+ workspace_dir
357
+ . as_path ( )
358
+ . join ( "target/bpf/prog2.bpf.o" )
359
+ . as_path ( ) ,
360
+ ) ;
361
+
362
+ // Check that skeleton exists (other tests will check for skeleton validity)
363
+ assert ! ( proj_one_dir
364
+ . as_path( )
365
+ . join( "src/bpf/prog1.skel.rs" )
366
+ . as_path( )
367
+ . exists( ) ) ;
368
+ assert ! ( proj_two_dir
369
+ . as_path( )
370
+ . join( "src/bpf/prog2.skel.rs" )
371
+ . as_path( )
372
+ . exists( ) ) ;
373
+ }
374
+
375
+ #[ test]
376
+ fn test_skeleton_empty_source ( ) {
377
+ let ( _dir, proj_dir, cargo_toml) = setup_temp_project ( ) ;
378
+
379
+ // Add prog dir
380
+ create_dir ( proj_dir. join ( "src/bpf" ) ) . expect ( "failed to create prog dir" ) ;
381
+
382
+ // Add a prog
383
+ let _prog_file =
384
+ File :: create ( proj_dir. join ( "src/bpf/prog.bpf.c" ) ) . expect ( "failed to create prog file" ) ;
385
+
386
+ assert_eq ! (
387
+ make(
388
+ true ,
389
+ Some ( & cargo_toml) ,
390
+ Path :: new( "/bin/clang" ) ,
391
+ true ,
392
+ true ,
393
+ Vec :: new( )
394
+ ) ,
395
+ 0
396
+ ) ;
397
+
398
+ let mut cargo = OpenOptions :: new ( )
399
+ . append ( true )
400
+ . open ( & cargo_toml)
401
+ . expect ( "failed to open Cargo.toml" ) ;
402
+
403
+ // Make test project use our development libbpf-rs version
404
+ writeln ! (
405
+ cargo,
406
+ r#"
407
+ libbpf-rs = {{ path = "{}" }}
408
+ "# ,
409
+ get_libbpf_rs_path( ) . as_path( ) . display( )
410
+ )
411
+ . expect ( "failed to write to Cargo.toml" ) ;
412
+
413
+ let mut source = OpenOptions :: new ( )
414
+ . write ( true )
415
+ . truncate ( true )
416
+ . open ( proj_dir. join ( "src/main.rs" ) )
417
+ . expect ( "failed to open main.rs" ) ;
418
+
419
+ write ! (
420
+ source,
421
+ r#"
422
+ mod bpf;
423
+ use bpf::*;
424
+
425
+ fn main() {{
426
+ let mut builder = ProgSkelBuilder::default();
427
+ let _skel = builder
428
+ .open()
429
+ .expect("failed to open skel")
430
+ .load()
431
+ .expect("failed to load skel");
432
+ }}
433
+ "# ,
434
+ )
435
+ . expect ( "failed to write to main.rs" ) ;
436
+
437
+ let status = Command :: new ( "cargo" )
438
+ . arg ( "build" )
439
+ . arg ( "--quiet" )
440
+ . arg ( "--manifest-path" )
441
+ . arg ( cargo_toml. into_os_string ( ) )
442
+ . status ( )
443
+ . expect ( "failed to spawn cargo-build" ) ;
444
+ assert ! ( status. success( ) ) ;
445
+ }
446
+
447
+ #[ test]
448
+ fn test_skeleton_basic ( ) {
449
+ let ( _dir, proj_dir, cargo_toml) = setup_temp_project ( ) ;
450
+
451
+ // Add prog dir
452
+ create_dir ( proj_dir. join ( "src/bpf" ) ) . expect ( "failed to create prog dir" ) ;
453
+
454
+ // Add a prog
455
+ let mut prog = OpenOptions :: new ( )
456
+ . write ( true )
457
+ . create ( true )
458
+ . open ( proj_dir. join ( "src/bpf/prog.bpf.c" ) )
459
+ . expect ( "failed to open prog.bpf.c" ) ;
460
+
461
+ write ! (
462
+ prog,
463
+ r#"
464
+ #include "vmlinux.h"
465
+ #include "bpf_helpers.h"
466
+
467
+ struct {{
468
+ __uint(type, BPF_MAP_TYPE_HASH);
469
+ __uint(max_entries, 1024);
470
+ __type(key, u32);
471
+ __type(value, u64);
472
+ }} mymap SEC(".maps");
473
+
474
+ SEC("kprobe/foo")
475
+ int this_is_my_prog(u64 *ctx)
476
+ {{
477
+ return 0;
478
+ }}
479
+ "# ,
480
+ )
481
+ . expect ( "failed to write prog.bpf.c" ) ;
482
+
483
+ // Lay down the necessary header files
484
+ let mut vmlinux = OpenOptions :: new ( )
485
+ . create ( true )
486
+ . write ( true )
487
+ . open ( proj_dir. join ( "src/bpf/vmlinux.h" ) )
488
+ . expect ( "failed to open vmlinux.h" ) ;
489
+ write ! ( vmlinux, "{}" , VMLINUX ) . expect ( "failed to write vmlinux.h" ) ;
490
+
491
+ let mut bpf_helpers = OpenOptions :: new ( )
492
+ . create ( true )
493
+ . write ( true )
494
+ . open ( proj_dir. join ( "src/bpf/bpf_helpers.h" ) )
495
+ . expect ( "failed to open bpf_helpers.h" ) ;
496
+ write ! ( bpf_helpers, "{}" , BPF_HELPERS ) . expect ( "failed to write bpf_helpers.h" ) ;
497
+
498
+ let mut bpf_helper_defs = OpenOptions :: new ( )
499
+ . create ( true )
500
+ . write ( true )
501
+ . open ( proj_dir. join ( "src/bpf/bpf_helper_defs.h" ) )
502
+ . expect ( "failed to open bpf_helper_defs.h" ) ;
503
+ write ! ( bpf_helper_defs, "{}" , BPF_HELPER_DEFS ) . expect ( "failed to write bpf_helper_defs.h" ) ;
504
+
505
+ assert_eq ! (
506
+ make(
507
+ true ,
508
+ Some ( & cargo_toml) ,
509
+ Path :: new( "/bin/clang" ) ,
510
+ true ,
511
+ true ,
512
+ Vec :: new( )
513
+ ) ,
514
+ 0
515
+ ) ;
516
+
517
+ let mut cargo = OpenOptions :: new ( )
518
+ . append ( true )
519
+ . open ( & cargo_toml)
520
+ . expect ( "failed to open Cargo.toml" ) ;
521
+
522
+ // Make test project use our development libbpf-rs version
523
+ writeln ! (
524
+ cargo,
525
+ r#"
526
+ libbpf-rs = {{ path = "{}" }}
527
+ "# ,
528
+ get_libbpf_rs_path( ) . as_path( ) . display( )
529
+ )
530
+ . expect ( "failed to write to Cargo.toml" ) ;
531
+
532
+ let mut source = OpenOptions :: new ( )
533
+ . write ( true )
534
+ . truncate ( true )
535
+ . open ( proj_dir. join ( "src/main.rs" ) )
536
+ . expect ( "failed to open main.rs" ) ;
537
+
538
+ write ! (
539
+ source,
540
+ r#"
541
+ mod bpf;
542
+ use bpf::*;
543
+
544
+ fn main() {{
545
+ let mut builder = ProgSkelBuilder::default();
546
+ let mut open_skel = builder
547
+ .open()
548
+ .expect("failed to open skel");
549
+
550
+ // Check that we can grab handles to open maps/progs
551
+ let _open_map = open_skel.maps().mymap();
552
+ let _open_prog = open_skel.progs().this_is_my_prog();
553
+
554
+ let mut skel = open_skel
555
+ .load()
556
+ .expect("failed to load skel");
557
+
558
+ // Check that we can grab handles to loaded maps/progs
559
+ let _map = skel.maps().mymap();
560
+ let _prog = skel.progs().this_is_my_prog();
561
+
562
+ // Check that attach() is generated
563
+ skel.attach().expect("failed to attach progs");
564
+
565
+ // Check that Option<Link> field is generated
566
+ let _mylink = skel.links.this_is_my_prog.unwrap();
567
+ }}
568
+ "# ,
569
+ )
570
+ . expect ( "failed to write to main.rs" ) ;
571
+
572
+ let status = Command :: new ( "cargo" )
573
+ . arg ( "build" )
574
+ . arg ( "--quiet" )
575
+ . arg ( "--manifest-path" )
576
+ . arg ( cargo_toml. into_os_string ( ) )
577
+ . status ( )
578
+ . expect ( "failed to spawn cargo-build" ) ;
579
+ assert ! ( status. success( ) ) ;
580
+ }
0 commit comments