3131#define adam_mkdir (p , m ) mkdir((p), (m))
3232#endif
3333
34+ // Platform-portable temp / second directory used by file-sandbox tests.
35+ // Android's `/` is a read-only rootfs (no mkdir, no symlink); the writable
36+ // scratch dir is `/data/local/tmp`. Other platforms keep the POSIX defaults.
37+ // String concatenation lets us inline these into both plain paths
38+ // (`TEST_TMP "/foo.db"`) and JSON args (`"{\"path\":\"" TEST_TMP "/foo\"}"`).
39+ #ifdef __ANDROID__
40+ #define TEST_TMP "/data/local/tmp"
41+ #define TEST_VAR "/data"
42+ #else
43+ #define TEST_TMP "/tmp"
44+ #define TEST_VAR "/var"
45+ #endif
46+
3447#ifndef ADAM_NO_PTHREADS
3548#include <pthread.h>
3649#endif
@@ -2453,7 +2466,7 @@ TEST(local_with_stream_callback) {
24532466#include <unistd.h>
24542467
24552468TEST (session_open_close ) {
2456- const char * path = "/tmp /adam_test_session.db" ;
2469+ const char * path = TEST_TMP " /adam_test_session.db" ;
24572470 unlink (path );
24582471 adam_memory_t * mem = adam_memory_open (path );
24592472 ASSERT_NOT_NULL (mem );
@@ -2462,7 +2475,7 @@ TEST(session_open_close) {
24622475}
24632476
24642477TEST (session_save_load_simple ) {
2465- const char * path = "/tmp /adam_test_session2.db" ;
2478+ const char * path = TEST_TMP " /adam_test_session2.db" ;
24662479 unlink (path );
24672480 adam_memory_t * mem = adam_memory_open (path );
24682481 ASSERT_NOT_NULL (mem );
@@ -2499,7 +2512,7 @@ TEST(session_save_load_simple) {
24992512}
25002513
25012514TEST (session_save_load_with_tool_calls ) {
2502- const char * path = "/tmp /adam_test_session3.db" ;
2515+ const char * path = TEST_TMP " /adam_test_session3.db" ;
25032516 unlink (path );
25042517 adam_memory_t * mem = adam_memory_open (path );
25052518
@@ -2546,7 +2559,7 @@ TEST(session_save_load_with_tool_calls) {
25462559}
25472560
25482561TEST (session_list_and_delete ) {
2549- const char * path = "/tmp /adam_test_session4.db" ;
2562+ const char * path = TEST_TMP " /adam_test_session4.db" ;
25502563 unlink (path );
25512564 adam_memory_t * mem = adam_memory_open (path );
25522565
@@ -2593,7 +2606,7 @@ TEST(session_list_and_delete) {
25932606
25942607TEST (session_overwrite ) {
25952608 // Saving the same session_id twice should replace the old data
2596- const char * path = "/tmp /adam_test_session5.db" ;
2609+ const char * path = TEST_TMP " /adam_test_session5.db" ;
25972610 unlink (path );
25982611 adam_memory_t * mem = adam_memory_open (path );
25992612
@@ -2622,7 +2635,7 @@ TEST(session_overwrite) {
26222635
26232636TEST (session_persistence_across_reopen ) {
26242637 // Data survives closing and reopening the database
2625- const char * path = "/tmp /adam_test_session6.db" ;
2638+ const char * path = TEST_TMP " /adam_test_session6.db" ;
26262639 unlink (path );
26272640
26282641 // Open, save, close
@@ -3552,47 +3565,47 @@ TEST(tool_file_read_sandbox) {
35523565 ASSERT (strstr (r .for_llm , "denied" ) != NULL );
35533566
35543567 // Allow /tmp
3555- ASSERT_EQ (adam_settings_allow_dir (s , "/tmp" ), ADAM_OK );
3568+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_TMP ), ADAM_OK );
35563569
35573570 // Write a test file
3558- FILE * f = fopen ("/tmp /adam_test_read.txt" , "w" );
3571+ FILE * f = fopen (TEST_TMP " /adam_test_read.txt" , "w" );
35593572 ASSERT_NOT_NULL (f );
35603573 fprintf (f , "hello from adam test" );
35613574 fclose (f );
35623575
35633576 // Read it — should succeed
35643577 arena_reset (a );
3565- args = "{\"path\":\"/tmp /adam_test_read.txt\"}" ;
3578+ args = "{\"path\":\"" TEST_TMP " /adam_test_read.txt\"}" ;
35663579 r = adam_tool_file_read (a , s , args , strlen (args ));
35673580 ASSERT_EQ (r .success , 1 );
35683581 ASSERT_STR_EQ (r .for_llm , "hello from adam test" );
35693582
35703583 // Try to escape sandbox
35713584 arena_reset (a );
3572- args = "{\"path\":\"/tmp /../etc/hosts\"}" ;
3585+ args = "{\"path\":\"" TEST_TMP " /../etc/hosts\"}" ;
35733586 r = adam_tool_file_read (a , s , args , strlen (args ));
35743587 ASSERT_EQ (r .success , 0 );
35753588 ASSERT (strstr (r .for_llm , "denied" ) != NULL );
35763589
3577- remove ("/tmp /adam_test_read.txt" );
3590+ remove (TEST_TMP " /adam_test_read.txt" );
35783591 arena_destroy (a );
35793592 adam_settings_destroy (s );
35803593}
35813594
35823595TEST (tool_file_write_sandbox ) {
35833596 arena_t * a = arena_create (4096 );
35843597 adam_settings_t * s = adam_create_settings ();
3585- ASSERT_EQ (adam_settings_allow_dir (s , "/tmp" ), ADAM_OK );
3598+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_TMP ), ADAM_OK );
35863599
35873600 // Write a file
3588- const char * args = "{\"path\":\"/tmp /adam_test_write.txt\","
3601+ const char * args = "{\"path\":\"" TEST_TMP " /adam_test_write.txt\","
35893602 "\"content\":\"test content\"}" ;
35903603 adam_tool_result_t r = adam_tool_file_write (a , s , args , strlen (args ));
35913604 ASSERT_EQ (r .success , 1 );
35923605 ASSERT (strstr (r .for_llm , "Wrote" ) != NULL );
35933606
35943607 // Verify contents
3595- FILE * f = fopen ("/tmp /adam_test_write.txt" , "r" );
3608+ FILE * f = fopen (TEST_TMP " /adam_test_write.txt" , "r" );
35963609 ASSERT_NOT_NULL (f );
35973610 char buf [64 ];
35983611 size_t n = fread (buf , 1 , 63 , f );
@@ -3602,12 +3615,12 @@ TEST(tool_file_write_sandbox) {
36023615
36033616 // Append
36043617 arena_reset (a );
3605- args = "{\"path\":\"/tmp /adam_test_write.txt\","
3618+ args = "{\"path\":\"" TEST_TMP " /adam_test_write.txt\","
36063619 "\"content\":\" appended\",\"append\":true}" ;
36073620 r = adam_tool_file_write (a , s , args , strlen (args ));
36083621 ASSERT_EQ (r .success , 1 );
36093622
3610- f = fopen ("/tmp /adam_test_write.txt" , "r" );
3623+ f = fopen (TEST_TMP " /adam_test_write.txt" , "r" );
36113624 n = fread (buf , 1 , 63 , f );
36123625 buf [n ] = '\0' ;
36133626 fclose (f );
@@ -3620,22 +3633,22 @@ TEST(tool_file_write_sandbox) {
36203633 ASSERT_EQ (r .success , 0 );
36213634 ASSERT (strstr (r .for_llm , "denied" ) != NULL );
36223635
3623- remove ("/tmp /adam_test_write.txt" );
3636+ remove (TEST_TMP " /adam_test_write.txt" );
36243637 arena_destroy (a );
36253638 adam_settings_destroy (s );
36263639}
36273640
36283641TEST (tool_list_directory_sandbox ) {
36293642 arena_t * a = arena_create (64 * 1024 );
36303643 adam_settings_t * s = adam_create_settings ();
3631- ASSERT_EQ (adam_settings_allow_dir (s , "/tmp" ), ADAM_OK );
3644+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_TMP ), ADAM_OK );
36323645
36333646 // Create test directory with a file
3634- adam_mkdir ("/tmp /adam_test_dir" , 0755 );
3635- FILE * f = fopen ("/tmp /adam_test_dir/test.txt" , "w" );
3647+ adam_mkdir (TEST_TMP " /adam_test_dir" , 0755 );
3648+ FILE * f = fopen (TEST_TMP " /adam_test_dir/test.txt" , "w" );
36363649 if (f ) { fputs ("x" , f ); fclose (f ); }
36373650
3638- const char * args = "{\"path\":\"/tmp /adam_test_dir\"}" ;
3651+ const char * args = "{\"path\":\"" TEST_TMP " /adam_test_dir\"}" ;
36393652 adam_tool_result_t r = adam_tool_list_directory (a , s , args , strlen (args ));
36403653 ASSERT_EQ (r .success , 1 );
36413654 ASSERT (strstr (r .for_llm , "test.txt" ) != NULL );
@@ -3647,8 +3660,8 @@ TEST(tool_list_directory_sandbox) {
36473660 r = adam_tool_list_directory (a , s , args , strlen (args ));
36483661 ASSERT_EQ (r .success , 0 );
36493662
3650- remove ("/tmp /adam_test_dir/test.txt" );
3651- rmdir ("/tmp /adam_test_dir" );
3663+ remove (TEST_TMP " /adam_test_dir/test.txt" );
3664+ rmdir (TEST_TMP " /adam_test_dir" );
36523665 arena_destroy (a );
36533666 adam_settings_destroy (s );
36543667}
@@ -3664,7 +3677,7 @@ TEST(tool_shell_exec_basic) {
36643677 ASSERT (strstr (r .for_llm , "denied" ) != NULL );
36653678
36663679 // Allow /tmp
3667- ASSERT_EQ (adam_settings_allow_dir (s , "/tmp" ), ADAM_OK );
3680+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_TMP ), ADAM_OK );
36683681
36693682 // Simple echo
36703683 arena_reset (a );
@@ -3691,13 +3704,13 @@ TEST(tool_allow_dir) {
36913704 adam_settings_t * s = adam_create_settings ();
36923705
36933706 ASSERT_EQ (s -> allowed_dir_count , 0 );
3694- ASSERT_EQ (adam_settings_allow_dir (s , "/tmp" ), ADAM_OK );
3707+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_TMP ), ADAM_OK );
36953708 ASSERT_EQ (s -> allowed_dir_count , 1 );
3696- ASSERT_EQ (adam_settings_allow_dir (s , "/var" ), ADAM_OK );
3709+ ASSERT_EQ (adam_settings_allow_dir (s , TEST_VAR ), ADAM_OK );
36973710 ASSERT_EQ (s -> allowed_dir_count , 2 );
36983711
36993712 // NULL params
3700- ASSERT_EQ (adam_settings_allow_dir (NULL , "/tmp" ), ADAM_ERR_INVALID_PARAM );
3713+ ASSERT_EQ (adam_settings_allow_dir (NULL , TEST_TMP ), ADAM_ERR_INVALID_PARAM );
37013714 ASSERT_EQ (adam_settings_allow_dir (s , NULL ), ADAM_ERR_INVALID_PARAM );
37023715
37033716 // Nonexistent dir
@@ -4892,18 +4905,18 @@ TEST(integration_json_output) {
48924905TEST (integration_file_tools_sandbox ) {
48934906 // Write a file, read it back, list directory — all sandboxed
48944907 adam_settings_t * s = adam_create_settings ();
4895- adam_settings_allow_dir (s , "/tmp" );
4908+ adam_settings_allow_dir (s , TEST_TMP );
48964909 arena_t * a = arena_create (64 * 1024 );
48974910
48984911 // Write
4899- const char * w_args = "{\"path\":\"/tmp /adam_integ_test.txt\","
4912+ const char * w_args = "{\"path\":\"" TEST_TMP " /adam_integ_test.txt\","
49004913 "\"content\":\"Hello from integration test!\"}" ;
49014914 adam_tool_result_t wr = adam_tool_file_write (a , s , w_args , strlen (w_args ));
49024915 ASSERT_EQ (wr .success , 1 );
49034916
49044917 // Read back
49054918 arena_reset (a );
4906- const char * r_args = "{\"path\":\"/tmp /adam_integ_test.txt\"}" ;
4919+ const char * r_args = "{\"path\":\"" TEST_TMP " /adam_integ_test.txt\"}" ;
49074920 adam_tool_result_t rr = adam_tool_file_read (a , s , r_args , strlen (r_args ));
49084921 ASSERT_EQ (rr .success , 1 );
49094922 ASSERT_STR_EQ (rr .for_llm , "Hello from integration test!" );
@@ -4922,7 +4935,7 @@ TEST(integration_file_tools_sandbox) {
49224935 ASSERT_EQ (dr .success , 0 );
49234936 ASSERT (strstr (dr .for_llm , "denied" ) != NULL );
49244937
4925- remove ("/tmp /adam_integ_test.txt" );
4938+ remove (TEST_TMP " /adam_integ_test.txt" );
49264939 arena_destroy (a );
49274940 adam_settings_destroy (s );
49284941}
@@ -5133,15 +5146,11 @@ int main(void) {
51335146 printf ("Adam Test Suite v%s\n" , ADAM_VERSION_STRING );
51345147 printf ("============================================================\n" );
51355148
5136- // Tests use hardcoded POSIX-style paths (`/tmp/...`, `/var`). Ensure
5137- // both dirs exist before any test runs:
5138- // - Linux/macOS: already there, EEXIST is fine
5139- // - Windows MinGW: `/tmp` resolves to `<drive>:\tmp` (no MSYS path
5140- // translation in MinGW binaries); we create it
5141- // - Android: `/tmp` doesn't exist by default; needs adb root + remount
5142- // (already done in CI) for mkdir at / to succeed
5143- adam_mkdir ("/tmp" , 0755 );
5144- adam_mkdir ("/var" , 0755 );
5149+ // TEST_TMP / TEST_VAR map to platform-writable dirs (see top of file).
5150+ // Ensure TEST_TMP exists so the file-sandbox / session tests can fopen /
5151+ // sqlite3_open inside it. EEXIST is fine; on Android the dir is already
5152+ // there (/data/local/tmp) and mkdir is a no-op.
5153+ adam_mkdir (TEST_TMP , 0755 );
51455154
51465155 adam_init ();
51475156 mem_report_start ();
0 commit comments