// // aegis - project change supervisor // Copyright (C) 2000, 2002-2008, 2011, 2012, 2014 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include batch_result_list_ty * project_test_run_list(project *pp, string_list_ty *wlp, user_ty::pointer up, bool progress_flag, time_t time_limit, const nstring_list &variable_assignments) { batch_result_list_ty *result; // // create a fake change, // so can set environment variables // for the test // trace(("project_run_test_list(pp = %p, wlp = %p, up = %p, " "progress_flag = %d, time_limit = %ld)\n{\n", pp, wlp, up.get(), progress_flag, (long)time_limit)); change::pointer cp = change::create_bogus(pp); // // do each of the tests // result = change_test_run_list ( cp, wlp, up, false, // not baseline, positive! progress_flag, time_limit, variable_assignments ); trace(("return %p;\n", result)); trace(("}\n")); return result; } static batch_result_list_ty * change_test_run_list_inner(change::pointer cp, string_list_ty *wlp, user_ty::pointer up, bool baseline_flag, int current, int total, time_t time_limit, const nstring_list &variable_assignments, const long *countdown) { trace(("change_test_run_list_inner(cp = %p, wlp = %p, up = %p, " "baseline_flag = %d, current = %d, total = %d, time_limit = %ld, " "countdown = %p)\n{\n", cp.get(), wlp, up.get(), baseline_flag, current, total, (long)time_limit, countdown)); pconf_ty *pconf_data = change_pconf_get(cp, 1); batch_result_list_ty *result = 0; if (wlp->size() == 0) { result = batch_result_list_new(); } else if (pconf_data->batch_test_command) { result = change_test_batch ( cp, wlp, up, baseline_flag, current, total, variable_assignments, countdown ); } else { result = change_test_batch_fake ( cp, wlp, up, baseline_flag, current, total, time_limit, variable_assignments, countdown ); } trace(("return %p;\n", result)); trace(("}\n")); return result; } static double calculate_average_elapsed(change::pointer cp, const nstring &attr_name) { double sum = 0; long n = 0; for (size_t j = 0; ; ++j) { fstate_src_ty *src = cp->project_get()->file_nth(j, view_path_extreme); if (!src) break; if (src->usage != file_usage_test) { // // we don't bother with the timings for manual tests, // they will have human induced oscillations. // continue; } double secs = attributes_list_find_real(src->attribute, attr_name.c_str(), -1); if (secs > 0) { sum += secs; ++n; } } if (n == 0) return 60; sum /= n; if (sum < 1e-3) sum = 1e-3; return sum; } static void calculate_eta(change::pointer cp, string_list_ty *wlp, long *countdown) { nstring arch_name(change_architecture_name(cp, 0)); if (arch_name.empty()) arch_name = "unspecified"; nstring attr_name = "test/" + arch_name + "/elapsed"; double average_elapsed = -1; double eta = 0; for (size_t nn = 0; nn < wlp->size(); ++nn) { size_t n = wlp->size() - 1 - nn; string_ty *fn = (*wlp)[n]; fstate_src_ty *src = cp->file_find(nstring(fn), view_path_simple); double secs = -1; assert(src); if (src) { secs = attributes_list_find_real ( src->attribute, attr_name.c_str(), -1 ); } if (secs < 0) { if (average_elapsed < 0) average_elapsed = calculate_average_elapsed(cp, attr_name); assert(average_elapsed > 0); secs = average_elapsed; } assert(secs > 0); eta += secs; countdown[n] = long(ceil(eta)); } } // // The above function is the real one, this one simply breaks things up // if it looks like the batch test command line may get too long. // batch_result_list_ty * change_test_run_list(change::pointer cp, string_list_ty *wlp, user_ty::pointer up, bool baseline_flag, bool progress_flag, time_t time_limit, const nstring_list &variable_assignments) { // // Build an ETA based on the past performance of the tests on this // architecture. // long *countdown = new long [wlp->size()]; calculate_eta(cp, wlp, countdown); // // We limit ourselves to commands with at most 100 tests, because // some Unix implementations have very short command lines and can't // cope with more. // size_t multiple = 100; if (time_limit) { // // The batch_test_command is expected to run tests in parallel. // Even if it doesn't, we don't have the ability to tell it to // stop. So, when we have a time limit, reduce the number of // tests run between checks to see if we have run out of time. // multiple = 12; } if (wlp->size() <= multiple) { batch_result_list_ty *result = change_test_run_list_inner ( cp, wlp, up, baseline_flag, 0, // start (progress_flag ? wlp->size() : 0), time_limit, variable_assignments, countdown ); delete [] countdown; return result; } trace(("change_test_run_list(cp = %p, wlp = %p, up = %p, " "baseline_flag = %d, progress_flag = %d, time_limit = %ld)\n{\n", cp.get(), wlp, up.get(), baseline_flag, progress_flag, (long)time_limit)); batch_result_list_ty *result = batch_result_list_new(); int persevere = up->persevere_preference(true); for (size_t j = 0; j < wlp->size(); j += multiple) { size_t end = j + multiple; if (end > wlp->size()) end = wlp->size(); string_list_ty wl2; for (size_t k = j; k < end; ++k) wl2.push_back((*wlp)[k]); batch_result_list_ty *result2 = change_test_run_list_inner ( cp, &wl2, up, baseline_flag, j, (progress_flag ? wlp->size() : 0), time_limit, variable_assignments, countdown + j ); batch_result_list_append_list(result, result2); batch_result_list_delete(result2); // // Don't keep going if the user asked us not to. // if (!persevere && (result->fail_count || result->no_result_count)) break; // // If we have been given a time limit, and that time has passed, // do not continue testing. // if (time_limit) { time_t now; time(&now); if (now >= time_limit) break; } } delete [] countdown; trace(("return %p;\n", result)); trace(("}\n")); return result; } // vim: set ts=8 sw=4 et :