boinc/client/app_test.cpp

204 lines
6.0 KiB
C++

// 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 <http://www.gnu.org/licenses/>.
// 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