// TaskYonetici: oluştur/ata/durum makinesi geçiş kuralları, ilerleme kapısı, // devret (kuyruk/atandı→yeni task; çalışan/terminal devredilemez), aktif sayım. #include "fiko/orkestra/task.hpp" #include "fiko_test.hpp" using namespace fiko::orkestra; namespace { TaskHedef oto() { return TaskHedef{HedefTipi::Otomatik, 0, ""}; } } // namespace FIKO_TEST(task_olustur_ve_kimlik) { TaskYonetici y; const uint64_t a = y.olustur("render", {{"kalite", "yuksek"}}, oto(), 1, 1000); const uint64_t b = y.olustur("step_donustur", {}, oto(), 1, 1001); FIKO_KONTROL(a == 1 && b == 2 && a != b); // artan, benzersiz, 0 değil FIKO_KONTROL(y.bul(a)->tur == "render"); FIKO_KONTROL(y.bul(a)->param.size() == 1 && y.bul(a)->param[0].second == "yuksek"); FIKO_KONTROL(y.bul(a)->durum == TaskDurum::Kuyrukta); FIKO_KONTROL(y.aktifSayisi() == 2); } FIKO_TEST(task_durum_makinesi) { // Geçerli akış: Kuyrukta→Atandı→Çalışıyor→Bitti. FIKO_KONTROL(taskGecisGecerli(TaskDurum::Kuyrukta, TaskDurum::Atandi)); FIKO_KONTROL(taskGecisGecerli(TaskDurum::Atandi, TaskDurum::Calisiyor)); FIKO_KONTROL(taskGecisGecerli(TaskDurum::Calisiyor, TaskDurum::Bitti)); FIKO_KONTROL(taskGecisGecerli(TaskDurum::Calisiyor, TaskDurum::Iptal)); // Geçersiz: çalışan DEVREDİLEMEZ; terminal'den çıkış yok; atlama yok. FIKO_KONTROL(!taskGecisGecerli(TaskDurum::Calisiyor, TaskDurum::Devredildi)); FIKO_KONTROL(!taskGecisGecerli(TaskDurum::Kuyrukta, TaskDurum::Calisiyor)); // atlama FIKO_KONTROL(!taskGecisGecerli(TaskDurum::Bitti, TaskDurum::Kuyrukta)); FIKO_KONTROL(!taskGecisGecerli(TaskDurum::Iptal, TaskDurum::Calisiyor)); } FIKO_TEST(task_ata_ve_ilerleme) { TaskYonetici y; const uint64_t id = y.olustur("render", {}, oto(), 1, 1000); FIKO_KONTROL(y.ata(id, 42)); FIKO_KONTROL(y.bul(id)->durum == TaskDurum::Atandi && y.bul(id)->atananNode == 42); FIKO_KONTROL(!y.ata(id, 99)); // zaten atandı → geçersiz geçiş // İlerleme yalnız Çalışıyor'da. FIKO_KONTROL(!y.ilerlemeAyarla(id, 50)); // Atandı'da ilerleme yok FIKO_KONTROL(y.durumGecir(id, TaskDurum::Calisiyor)); FIKO_KONTROL(y.bul(id)->ilerleme == 0); // Çalışıyor'a geçince sıfırlanır FIKO_KONTROL(y.ilerlemeAyarla(id, 150) && y.bul(id)->ilerleme == 100); // clamp FIKO_KONTROL(y.durumGecir(id, TaskDurum::Bitti)); FIKO_KONTROL(y.bul(id)->ilerleme == 100); FIKO_KONTROL(!y.durumGecir(id, TaskDurum::Calisiyor)); // terminal kilitli FIKO_KONTROL(y.aktifSayisi() == 0); // Bitti aktif değil } FIKO_TEST(task_otomatik_yerlestirme) { NodeKayitDefteri defter; auto ekle = [&](uint64_t id, float cpu, bool dogr, const char* etiket) { NodeBilgi n; n.nodeId = id; n.dogrulanmis = dogr; n.etiketler = etiket; defter.ilanIsle(n, 1000); defter.metrikIsle(id, NodeMetrikOzet{.cpuYuzde = cpu, .ramMB = 8192}, 1000); }; ekle(1, 80.f, true, "gpu"); // meşgul CPU ekle(2, 10.f, true, "cpu"); // boş CPU → tercih edilmeli ekle(3, 5.f, false, "gpu"); // en boş ama DOĞRULANMAMIŞ → elenir TaskYonetici y; YerlestirmeAgirlik ag; Task t; // Otomatik hedef FIKO_KONTROL(y.otomatikHedefSec(t, defter, ag) == 2); // en boş + doğrulanmış // Etiket politikası: yalnız "gpu" etiketli + uygun node. Task tg; tg.hedef = TaskHedef{HedefTipi::Etiket, 0, "gpu"}; FIKO_KONTROL(y.otomatikHedefSec(tg, defter, ag) == 1); // 3 doğrulanmamış, 2 gpu değil // Offline/Yanıtsız elenir: 2'yi çevrimdışına düşür → 1 kalır. defter.esikAyarla(100, 200); defter.tazele(2000); // tümü bayat → Offline FIKO_KONTROL(y.otomatikHedefSec(t, defter, ag) == 0); // uygun yok → kuyrukta bekle } FIKO_TEST(task_yerlestirme_yuk_dengesi) { NodeKayitDefteri defter; for (uint64_t id : {1ull, 2ull}) { NodeBilgi n; n.nodeId = id; n.dogrulanmis = true; defter.ilanIsle(n, 1000); defter.metrikIsle(id, NodeMetrikOzet{.cpuYuzde = 20.f, .ramMB = 4096}, 1000); } TaskYonetici y; YerlestirmeAgirlik ag; // Node 1'e 3 aktif task yükle → yeni task 2'ye gitmeli (yük dengesi). for (int i = 0; i < 3; ++i) { const uint64_t id = y.olustur("x", {}, TaskHedef{}, 0, 1000); y.ata(id, 1); } Task t; FIKO_KONTROL(y.otomatikHedefSec(t, defter, ag) == 2); // boş node tercih } FIKO_TEST(task_ata_cerceve_round_trip) { Task t; t.taskId = 0xDEADBEEF01; t.tur = "step_donustur"; t.oncelik = 2; t.kaynakNode = 555; t.param = {{"yol", "C:\\parca.step"}, {"kalite", "yuksek"}}; const std::string w = taskproto::ataSerialize(t); Task g; FIKO_KONTROL(taskproto::ataCoz(w, g)); FIKO_KONTROL(g.taskId == 0xDEADBEEF01 && g.tur == "step_donustur"); FIKO_KONTROL(g.oncelik == 2 && g.kaynakNode == 555); FIKO_KONTROL(g.param.size() == 2 && g.param[0].second == "C:\\parca.step"); FIKO_KONTROL(g.durum == TaskDurum::Atandi); // alan node'da Atandi kurulur // taskId 0 + kırpık gövde reddi; ileri-uyum (fazla bayt yok sayılır). Task sifir = t; sifir.taskId = 0; FIKO_KONTROL(!taskproto::ataCoz(taskproto::ataSerialize(sifir), g)); FIKO_KONTROL(!taskproto::ataCoz("kisa", g)); FIKO_KONTROL(taskproto::ataCoz(w + "gelecek-alan", g)); } FIKO_TEST(task_durum_cerceve_round_trip) { const std::string w = taskproto::durumSerialize(77, TaskDurum::Calisiyor, 45); uint64_t id = 0; TaskDurum d = TaskDurum::Kuyrukta; uint8_t il = 0; FIKO_KONTROL(taskproto::durumCoz(w, id, d, il)); FIKO_KONTROL(id == 77 && d == TaskDurum::Calisiyor && il == 45); // taskId 0 + bilinmeyen durum + kırpık reddi; ilerleme clamp. FIKO_KONTROL(!taskproto::durumCoz(taskproto::durumSerialize(0, TaskDurum::Bitti, 0), id, d, il)); FIKO_KONTROL(!taskproto::durumCoz("x", id, d, il)); std::string bozuk = taskproto::durumSerialize(5, TaskDurum::Bitti, 200); bozuk[10] = (char)99; // durum baytını geçersizle FIKO_KONTROL(!taskproto::durumCoz(bozuk, id, d, il)); uint8_t il2 = 0; taskproto::durumCoz(taskproto::durumSerialize(5, TaskDurum::Bitti, 200), id, d, il2); FIKO_KONTROL(il2 == 100); // ilerleme 200→100 clamp } FIKO_TEST(task_kabul_et_id_korur) { TaskYonetici y; Task t; t.taskId = 500; t.tur = "render"; t.durum = TaskDurum::Atandi; FIKO_KONTROL(y.kabulEt(t)); FIKO_KONTROL(y.bul(500) && y.bul(500)->durum == TaskDurum::Atandi); // id korundu FIKO_KONTROL(!y.kabulEt(t)); // mükerrer atama reddi Task sifir; // taskId 0 → red FIKO_KONTROL(!y.kabulEt(sifir)); // Yerel üretim çakışmaz: sonraki olustur > 500. const uint64_t yeni = y.olustur("x", {}, TaskHedef{}, 0, 1000); FIKO_KONTROL(yeni > 500 && !y.bul(yeni)->tur.empty()); } FIKO_TEST(task_indeks_coklu_lookup) { TaskYonetici y; std::vector idler; for (int i = 0; i < 50; ++i) idler.push_back(y.olustur("x", {}, TaskHedef{}, 0, 1000)); // Hepsi O(1) index'ten doğru bulunmalı (linear scan yerine). for (uint64_t id : idler) FIKO_KONTROL(y.bul(id) && y.bul(id)->taskId == id); FIKO_KONTROL(!y.bul(9999)); // olmayan → nullptr // devret yeni task üretir → index'e girer. const uint64_t yeni = y.devret(idler[0], 2000); FIKO_KONTROL(y.bul(yeni) && y.bul(yeni)->durum == TaskDurum::Kuyrukta); } FIKO_TEST(task_atanmis_baslat) { TaskYonetici y; const uint64_t a = y.olustur("render", {}, TaskHedef{}, 0, 1000); const uint64_t b = y.olustur("cam", {}, TaskHedef{}, 0, 1001); const uint64_t c = y.olustur("step", {}, TaskHedef{}, 0, 1002); y.ata(a, 10); y.ata(b, 10); y.ata(c, 20); // başka node const auto basladi = y.atanmisBaslat(10); FIKO_KONTROL(basladi.size() == 2); // yalnız node 10'a atananlar FIKO_KONTROL(y.bul(a)->durum == TaskDurum::Calisiyor); FIKO_KONTROL(y.bul(b)->durum == TaskDurum::Calisiyor); FIKO_KONTROL(y.bul(c)->durum == TaskDurum::Atandi); // node 20 dokunulmadı // İkinci çağrı boş (zaten Çalışıyor, tekrar başlatılmaz). FIKO_KONTROL(y.atanmisBaslat(10).empty()); } FIKO_TEST(task_devret) { TaskYonetici y; const uint64_t id = y.olustur("cam", {{"op", "finis"}}, TaskHedef{HedefTipi::BelirliNode, 7, ""}, 5, 1000, 2); y.ata(id, 7); // Atandı — henüz başlamadı, devredilebilir const uint64_t yeni = y.devret(id, 2000); FIKO_KONTROL(yeni != 0 && yeni != id); FIKO_KONTROL(y.bul(id)->durum == TaskDurum::Devredildi); // eski terminal FIKO_KONTROL(y.bul(yeni)->durum == TaskDurum::Kuyrukta); // yeni taze FIKO_KONTROL(y.bul(yeni)->tur == "cam" && y.bul(yeni)->param[0].second == "finis"); FIKO_KONTROL(y.bul(yeni)->oncelik == 2 && y.bul(yeni)->kaynakNode == 5); // tarif korundu // Çalışan iş DEVREDİLEMEZ (0 döner). const uint64_t id2 = y.olustur("render", {}, oto(), 1, 3000); y.ata(id2, 1); y.durumGecir(id2, TaskDurum::Calisiyor); FIKO_KONTROL(y.devret(id2, 4000) == 0); FIKO_KONTROL(y.bul(id2)->durum == TaskDurum::Calisiyor); // dokunulmadı }