toBeTrue() ->and(file_get_contents($path))->toBe("new\0binary") ->and(glob($root . '/.cacti-rrd-*'))->toBe([]) ->and(fileperms($path) & 0777)->toBe(0644); } finally { @unlink($path); @rmdir($root); } }); test('atomic graph writes reject destinations outside the approved root', function () { $root = rrd_atomic_test_directory('root'); $outside = rrd_atomic_test_directory('outside'); $path = $outside . '/graph.png'; try { expect(rrdtool_atomic_write($path, 'data', $root))->toBeFalse() ->and(file_exists($path))->toBeFalse(); } finally { if (file_exists($path)) { unlink($path); } @rmdir($outside); @rmdir($root); } }); test('atomic graph writes reject missing roots and missing parents', function () { $base = rrd_atomic_test_directory('missing'); try { expect(rrdtool_atomic_write($base . '/graph.png', 'data', $base . '/missing-root'))->toBeFalse() ->and(rrdtool_atomic_write($base . '/missing-parent/graph.png', 'data', $base))->toBeFalse(); } finally { @rmdir($base); } }); test('RRDtool output filenames are escaped at command construction', function () { $source = file_get_contents(CACTI_PATH_LIBRARY . '/rrd.php'); expect($source)->toContain("cacti_escapeshellarg((string) \$graph_data_array['export_filename'])") ->and($source)->toContain("cacti_escapeshellarg((string) \$graph_data_array['output_filename'])"); });