// src/testsuite/test-history.cc - Copyright 2005, 2006, University // of Padova, dept. of Pure and Applied // Mathematics // // This file is part of SGPEMv2. // // This 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 2 of the License, or // (at your option) any later version. // // SGPEMv2 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 SGPEMv2; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA /* This executable tests for workingness of the whole History services, * and nothing else. */ #include "config.h" #include "gettext.h" #include #include #include #include "backend/concrete_environment.hh" #include "backend/concrete_history.hh" #include "backend/history_observer.hh" using namespace sgpem; using namespace std; using Glib::ustring; // History observer used to see how many times // it is updated class DummyObserver : public HistoryObserver { public: DummyObserver() : _i(0) {} void update(const History& history) { cout << "Observer.update() : " << ++_i << endl; } unsigned int _i; }; struct ProcessCreationData { ustring name; time_t arrival_time; History::prio_t base_priority; }; struct ThreadCreationData { ustring name; Process* parent; time_t cpu_time; time_t arrival_time; History::prio_t base_priority; }; struct ResourceCreationData { ustring name; bool preemptable; size_t places; size_t availability; }; struct RequestCreationData { Thread* owner; time_t instant; }; struct SubRequestCreationData { Request* request; History::resource_key_t resource_key; time_t duration; }; DynamicThread& find_thread(ConcreteEnvironment& env, Thread& _thread) { DynamicThread& thread = (DynamicThread&)_thread; Environment::Processes& processes = env.get_processes(); // please forgive me, I'm sick of using iterators... for(unsigned int i = 0; i < processes.size(); ++i) { Process& p = *processes[i]; vector threads = p.get_threads(); for(unsigned int j = 0; j < threads.size(); ++j) { DynamicThread& t = (DynamicThread&)*threads[j]; if(t == thread) return t; } } return thread; } const DynamicThread& find_thread(const ConcreteEnvironment& env, const Thread& _thread) { const DynamicThread& thread = (DynamicThread&)_thread; const Environment::Processes& processes = env.get_processes(); // please forgive me, I'm sick of using iterators... for(unsigned int i = 0; i < processes.size(); ++i) { const Process& p = *processes[i]; vector threads = p.get_threads(); for(unsigned int j = 0; j < threads.size(); ++j) { const DynamicThread& t = (const DynamicThread&)*threads[j]; if(t == thread) return t; } } return thread; } int main(int argc, char** argv) { ostream& info = cout; ostream& test = cerr; using namespace sgpem; ConcreteHistory h; info << "Created the ConcreteHistory instance\n"; test << "Checking if it contains only one Environment... "; if(h.get_size() == 1) test << "PASS"; else test << "FAIL"; test << endl; const ConcreteEnvironment& environment = h.get_last_environment(); info << "Obtained the only contained environment\n"; test << "Checking if the environment is empty... "; if(environment.get_processes().size() == 0 && environment.get_resources().size() == 0 && environment.get_sorted_queue().size() == 0) test << "PASS"; else test << "FAIL"; test << endl; DummyObserver observer; info << "Created the observer\n"; h.attach(observer); info << "Attached to the history\n"; const ProcessCreationData processes[2] = { { "p1", 0, 3 }, { "p2", 3, 1 } }; DynamicProcess& p1 = h.add_process(processes[0].name, processes[0].arrival_time, processes[0].base_priority); DynamicProcess& p2 = h.add_process(processes[1].name, processes[1].arrival_time, processes[1].base_priority); const ThreadCreationData threads[4] = { { "p2_1", &p2, 3, 0, 0 }, { "p1_1", &p1, 2, 0, 2 }, { "p1_2", &p1, 1, 1, 0 }, { "p2_2", &p2, 5, 3, 1 } }; DynamicThread& p2_1 = h.add_thread(threads[0].name, *threads[0].parent, threads[0].cpu_time, threads[0].arrival_time, threads[0].base_priority); DynamicThread& p1_1 = h.add_thread(threads[1].name, *threads[1].parent, threads[1].cpu_time, threads[1].arrival_time, threads[1].base_priority); DynamicThread& p1_2 = h.add_thread(threads[2].name, *threads[2].parent, threads[2].cpu_time, threads[2].arrival_time, threads[2].base_priority); DynamicThread& p2_2 = h.add_thread(threads[3].name, *threads[3].parent, threads[3].cpu_time, threads[3].arrival_time, threads[3].base_priority); const ResourceCreationData resources[2] = { { "res1", true, 2, 0 }, { "res2", false, 1, 0 } }; const History::ResourcePair res1 = h.add_resource(resources[0].name, resources[0].preemptable, resources[0].places, resources[0].availability); const History::ResourcePair res2 = h.add_resource(resources[1].name, resources[1].preemptable, resources[1].places, resources[1].availability); const RequestCreationData requests[2] = { { &p1_2, 0 }, { &p2_1, 1 } }; DynamicRequest& req1 = h.add_request(*requests[0].owner, requests[0].instant); DynamicRequest& req2 = h.add_request(*requests[1].owner, requests[1].instant); const SubRequestCreationData subrequests[3] = { { &req1, res1.first, 1 }, { &req2, res2.first, 2 }, { &req2, res1.first, 1 } }; const DynamicSubRequest& sreq1 = h.add_subrequest(*subrequests[0].request, subrequests[0].resource_key, subrequests[0].duration); const DynamicSubRequest& sreq2 = h.add_subrequest(*subrequests[1].request, subrequests[1].resource_key, subrequests[1].duration); const DynamicSubRequest& sreq3 = h.add_subrequest(*subrequests[2].request, subrequests[2].resource_key, subrequests[2].duration); info << "Done adding required data by using the History factory interface\n"; const Environment::Requests res1_queue = environment.get_request_queue(res1.first); const Environment::Requests res2_queue = environment.get_request_queue(res2.first); test << "Checking if the environment contains the correct request queues... "; typedef Environment::Requests::const_iterator ReqIterator; if(res1_queue.size() == 2 && res2_queue.size() == 1) { bool res1_req1_match = false; bool res1_req2_match = false; bool res2_req1_match = false; bool bad_match = false; for(ReqIterator it = res1_queue.begin(); it != res1_queue.end(); ++it) { if(!res1_req1_match && *it == &req1) res1_req1_match = true; else if(!res1_req2_match && *it == &req2) res1_req2_match == true; else bad_match = true; } if(!res2_req1_match && *res2_queue.begin() == &req1) res2_req1_match = true; else bad_match = true; if(!bad_match && res1_req1_match && res1_req2_match && res2_req1_match) test << "PASS"; else test << "FAIL"; } else test << "FAIL"; test << endl; ConcreteEnvironment* environment1 = new ConcreteEnvironment(environment); ConcreteEnvironment* environment2 = new ConcreteEnvironment(environment); ConcreteEnvironment* environment3 = new ConcreteEnvironment(environment); ConcreteEnvironment* environment4 = new ConcreteEnvironment(environment); ConcreteEnvironment* environment5 = new ConcreteEnvironment(environment); info << "Created 5 copies of inital environment\n"; const History::prio_t priorities[5] = { 3, 1, 4, 5, 2 }; find_thread(*environment1, p1_1).set_priority_push(priorities[0]); find_thread(*environment2, p1_1).set_priority_push(priorities[1]); find_thread(*environment3, p1_1).set_priority_push(priorities[2]); find_thread(*environment4, p1_1).set_priority_push(priorities[3]); find_thread(*environment5, p1_1).set_priority_push(priorities[4]); info << "Updated dynamic priority of threads\n"; h.append_new_environment(environment1); h.append_new_environment(environment2); h.append_new_environment(environment3); h.append_new_environment(environment4); h.append_new_environment(environment5); info << "Done appending the environment copies to the history\n"; test << "Checking if history size is 6... "; if(h.get_size() == 6) test << "PASS"; else test << "FAIL"; test << endl; const DynamicThread& t1 = find_thread(h.get_environment_at(1), p1_1); const DynamicThread& t5 = find_thread(h.get_environment_at(5), p1_1); test << "Checking if dynamic priority of thread at instant 1 and 5 is different... "; if(t1.get_priority_push() != t5.get_priority_push()) test << "PASS"; else test << "FAIL"; test << endl; const DynamicThread& t4 = find_thread(h.get_environment_at(4), p1_1); test << "Checking if dynamic priority of thread at instant 4, is different "; test << "from that at instant 1 and 5, and equal to the one expected... "; if(t4.get_priority_push() != t1.get_priority_push() && t4.get_priority_push() != t5.get_priority_push() && t4.get_priority_push() == priorities[3]) test << "PASS"; else test << "FAIL"; test << endl; test << "Checking whether all subrequests in the last environment "; test << "refer to valid resources... "; const ConcreteEnvironment& last_env = h.get_last_environment(); const Environment::Processes& le_processes = last_env.get_processes(); const Environment::Resources& le_resources = last_env.get_resources(); bool subreq_pass = true; for(unsigned int i = 0; i < le_processes.size(); ++i) { vector threads = le_processes[i]->get_threads(); for(unsigned int j = 0; j < threads.size(); ++j) { vector requests = threads[j]->get_requests(); for(unsigned int k = 0; k < requests.size(); ++k) { vector subrequests = requests[k]->get_subrequests(); for(unsigned int x = 0; x < subrequests.size(); ++x) { if(le_resources.find(subrequests[x]->get_resource_key()) == le_resources.end()) subreq_pass = false; } } } } if(subreq_pass) test << "PASS"; else test << "FAIL"; test << endl; test << "Checking if history throws an exception on accessing a nonexistent environment... "; try { h.get_environment_at(6); test << "FAIL"; } catch(out_of_range) { test << "PASS"; } test << endl; ThreadCreationData p1_3_d = { "p1_3", &p1, 3, 4 }; Thread& p1_3 = h.add_thread(p1_3_d.name, *p1_3_d.parent, p1_3_d.cpu_time, p1_3_d.arrival_time, p1_3_d.base_priority); info << "Added new thread, child of process 1\n"; test << "Checking if history has size 1 after adding a thread... "; if(h.get_size() == 1) test << "PASS"; else test << "FAIL"; test << endl; ConcreteEnvironment* environment6 = new ConcreteEnvironment(h.get_last_environment()); h.append_new_environment(environment6); info << "Added a new environment to the history as a clear-cut copy of the last\n"; h.remove(res1.first); info << "Checking if history has size 1 after removing a resource... "; if(h.get_size() == 1) test << "PASS"; else test << "FAIL"; test << endl; test << "Checking whether objects inside the history are the same as the beginning, "; test << "except for the missing thread and associated requests... "; const Environment::Processes& final_processes = h.get_last_environment().get_processes(); const Environment::Resources& final_resources = h.get_last_environment().get_resources(); bool final_p1_match = false; bool final_p2_match = false; bool final_bad_match = false; bool final_sreq2_match = false; for(unsigned int i = 0; i < final_processes.size(); ++i) { DynamicProcess& p = (DynamicProcess&)*final_processes[i]; if(!final_p1_match && p == p1) final_p1_match = true; else if(!final_p2_match && p == p2) final_p2_match = true; else final_bad_match = true; vector& threads = p.get_dynamic_threads(); for(unsigned int j = 0; j < threads.size(); ++j) { vector requests = threads[j]->get_dynamic_requests(); for(unsigned int k = 0; k < requests.size(); ++k) { vector subrequests = requests[k]->get_dynamic_subrequests(); for(unsigned int x = 0; x < subrequests.size(); ++x) { // NOTE this will of course fail if the subrequests which should not be here // are still in the environment if(!final_sreq2_match && *subrequests[i] == sreq2) final_sreq2_match = true; else final_bad_match = true; } } } } typedef Environment::Resources::const_iterator ResourceIt; if(final_resources.size() == 1) { DynamicResource& r = (DynamicResource&)*(final_resources.begin()->second); if(!(r == *res2.second)) final_bad_match = true; } else final_bad_match = true; if(!final_bad_match && final_p1_match && final_p2_match && final_sreq2_match) test << "PASS"; else test << "FAIL"; test << "Checking whether ConcreteHistory throws an exception if we try to use "; test << "a negative index to access an environment... "; try { // FIXME This causes a warning because we force a conversion of a negative number // to an unsigned integer. What did the designers wanted to test by doing this? // The compiler? h.get_environment_at(-17); test << "FAIL"; } catch(out_of_range) { test << "PASS"; } test << endl; test << "Internal observer's counter should be 21... "; if(observer._i == 21) test << "PASS"; else test << "FAIL"; test << endl; return 0; }