//
// 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 :