// This file is part of BOINC. // https://boinc.berkeley.edu // Copyright (C) 2024 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . // A framework that lets you run jobs under a BOINC client // without a project, and without fake XML files // This lets you debug client/app interactions. // // To use this framework: // - edit this file to describe your application: // input/output files, attributes, etc. // NOTE: currently it's set up for an app that uses the WSL wrapper // // - build the BOINC client with these changes // - make a BOINC data directory, say 'test' // (or you can use an existing BOINC data directory, // in which case the client will also run jobs that are there) // - make a directory test/slots/app_test // The client will run the test job there. // Clean it out between runs. // - make a dir test/projects/app_test // - In the project directory, put: // - the executable file // - the input file(s) with physical names // - run boinc (in the data directory if you created one) // when the job is done, the client won't clean out the slot dir. // You can examine the contents of the slot dir, // and examine the output files in the project dir. // Clean out the slot dir between tests. #include "client_state.h" // set to 0 to enable app test #if 1 void CLIENT_STATE::app_test_init() {} #else #include "client_types.h" #include "log_flags.h" #include "project.h" #include "result.h" // The following functions create client data structures // (PROJECT, APP, APP_VERSION, WORKUNIT, RESULT, FILE_REF, FILE_INFO) // for the test app and job. // The names and version numbers must match up. static PROJECT* make_project() { PROJECT *proj = new PROJECT; strcpy(proj->project_name, "app_test project"); strcpy(proj->master_url, "https://app.test/"); strcpy(proj->_project_dir, "projects/app_test"); proj->app_test = true; // tell the client to use the slots/app_test slot dir for this project gstate.projects.push_back(proj); return proj; } static APP* make_app(PROJECT* proj) { APP *app = new APP; strcpy(app->name, "test_app"); strcpy(app->user_friendly_name, "test_app"); app->project = proj; gstate.apps.push_back(app); return app; } #define INPUT_FILE 0 #define OUTPUT_FILE 1 #define MAIN_PROG 2 static FILE_REF* make_file( PROJECT *proj, const char* phys_name, const char* log_name, int ftype, bool copy_file ) { FILE_INFO *fip = new FILE_INFO; strcpy(fip->name, phys_name); fip->project = proj; fip->status = (ftype == OUTPUT_FILE)?FILE_NOT_PRESENT:FILE_PRESENT; if (ftype == MAIN_PROG) fip->executable = true; if (ftype == OUTPUT_FILE) { fip->max_nbytes = 1e9; fip->upload_urls.add(string("foobar")); } gstate.file_infos.push_back(fip); FILE_REF * fref = new FILE_REF; if (log_name) { strcpy(fref->open_name, log_name); } fref->file_info = fip; if (ftype == MAIN_PROG) fref->main_program = true; fref->copy_file = copy_file; return fref; } static APP_VERSION* make_app_version(APP *app) { APP_VERSION *av = new APP_VERSION; strcpy(av->app_name, app->name); strcpy(av->api_version, "8.0"); av->app = app; av->project = app->project; av->avg_ncpus = 1; av->version_num = 1; av->flops = 1e9; gstate.app_versions.push_back(av); return av; } static WORKUNIT* make_workunit(APP_VERSION *av) { WORKUNIT *wu = new WORKUNIT; APP* app = av->app; strcpy(wu->name, "test_wu"); strcpy(wu->app_name, app->name); wu->app = app; wu->project = app->project; wu->rsc_fpops_est = 1e9; wu->rsc_fpops_bound = 1e12; wu->rsc_memory_bound = 1e9; wu->rsc_disk_bound = 1e9; wu->version_num = av->version_num; gstate.workunits.push_back(wu); return wu; } static RESULT* make_result(APP_VERSION *av, WORKUNIT* wu) { RESULT *res = new RESULT; strcpy(res->name, "test_result"); strcpy(res->wu_name, wu->name); res->project = av->project; res->avp = av; res->wup = wu; res->app = av->app; res->report_deadline = dtime()+86400; gstate.results.push_back(res); return res; } // app_test_init() sets up above data structures // so that the client runs a test job. // void CLIENT_STATE::app_test_init() { PROJECT *proj = make_project(); APP *app = make_app(proj); // can put other stuff here like #if 0 app->sporadic = true; have_sporadic_app = true; #endif APP_VERSION *av = make_app_version(app); av->app_files.push_back( *make_file(app->project, "wsl_wrapper.exe", NULL, MAIN_PROG, false) ); av->app_files.push_back( *make_file(app->project, "main", NULL, INPUT_FILE, true) ); av->app_files.push_back( *make_file(app->project, "worker", NULL, INPUT_FILE, false) ); // can put other stuff here like #if 0 av->gpu_ram = 1e7; av->gpu_usage.rsc_type = PROC_TYPE_NVIDIA_GPU; av->gpu_usage.usage = 1; #endif WORKUNIT *wu = make_workunit(av); #if 1 //wu->command_line = "--nsecs 60"; wu->input_files.push_back( *make_file(proj, "infile", "in", INPUT_FILE, false) ); #endif RESULT *result = make_result(av, wu); #if 1 result->output_files.push_back( *make_file(proj, "outfile", "out", OUTPUT_FILE, false) ); #endif // tell the client not to get work or run benchmarks // cc_config.unsigned_apps_ok = true; cc_config.skip_cpu_benchmarks = true; } #endif