#include "fiko_test.hpp" #include "fiko/cekirdek/olay.hpp" #include #include #include #include using namespace fiko; namespace { // Test için sabit geçici günlük yolu (her testin başında silinir). std::string testGunlukYolu() { #if defined(_MSC_VER) char* t = nullptr; std::size_t n = 0; std::string dizin = (_dupenv_s(&t, &n, "TEMP") == 0 && t) ? t : "."; std::free(t); #else const char* t = std::getenv("TEMP"); std::string dizin = (t && *t) ? t : "."; #endif return dizin + "\\fiko_olay_test.fikolog"; } // Dosyadaki toplam (boş olmayan) satır sayısı; dosya yoksa 0. int satirSay(const std::string& yol) { std::ifstream in(yol); if (!in) return 0; int n = 0; std::string satir; while (std::getline(in, satir)) { if (!satir.empty()) ++n; } return n; } // Dosyada verilen alt dizgeyi içeren satır var mı? bool satirIceriyor(const std::string& yol, const std::string& parca) { std::ifstream in(yol); if (!in) return false; std::string satir; while (std::getline(in, satir)) { if (satir.find(parca) != std::string::npos) return true; } return false; } } // namespace FIKO_TEST(olay_abone_yayinla_payload) { OlayGunlugu g; u32 gelen = 0; int cagriSayisi = 0; g.abone([&](const VarlikEklendi& o) { gelen = o.varlik; ++cagriSayisi; }); g.yayinla(VarlikEklendi{42}); FIKO_KONTROL(cagriSayisi == 1); FIKO_KONTROL(gelen == 42); } FIKO_TEST(olay_tip_guvenligi) { OlayGunlugu g; int eklendiCagri = 0; int silindiCagri = 0; g.abone([&](const VarlikEklendi&) { ++eklendiCagri; }); g.abone([&](const VarlikSilindi&) { ++silindiCagri; }); // Sadece VarlikSilindi yayınla — VarlikEklendi handler ASLA çağrılmamalı. g.yayinla(VarlikSilindi{7}); FIKO_KONTROL(silindiCagri == 1); FIKO_KONTROL(eklendiCagri == 0); // Şimdi VarlikEklendi yayınla — bu kez silindi çağrılmamalı. g.yayinla(VarlikEklendi{7}); FIKO_KONTROL(eklendiCagri == 1); FIKO_KONTROL(silindiCagri == 1); } FIKO_TEST(olay_sira_monoton) { OlayGunlugu g; FIKO_KONTROL(g.sonrakiSira() == 0); g.yayinla(VarlikEklendi{1}); g.yayinla(DonusumDegisti{1}); g.yayinla(GorunurlukDegisti{1, false}); const auto& kayitlar = g.kayitlar(); FIKO_KONTROL(kayitlar.size() == 3); FIKO_KONTROL(kayitlar[0].sira == 0); FIKO_KONTROL(kayitlar[1].sira == 1); FIKO_KONTROL(kayitlar[2].sira == 2); FIKO_KONTROL(g.sonrakiSira() == 3); // Monoton artış: her kayıt bir öncekinden büyük sıra. bool monoton = true; for (std::size_t i = 1; i < kayitlar.size(); ++i) { if (kayitlar[i].sira <= kayitlar[i - 1].sira) monoton = false; } FIKO_KONTROL(monoton); } FIKO_TEST(olay_kayit_sayisi_ve_alanlar) { OlayGunlugu g; g.zamanAyarla(1.5); g.yayinla(RenkDegisti{9}); const auto& kayitlar = g.kayitlar(); FIKO_KONTROL(kayitlar.size() == 1); FIKO_KONTROL(kayitlar[0].tur == OlayTuru::RenkDegisti); FIKO_KONTROL(kayitlar[0].varlik == 9); FIKO_YAKIN(kayitlar[0].zaman, 1.5, 1e-9); // Özet verilmezse tür adı kopyalanır. FIKO_KONTROL(std::string(kayitlar[0].ozet) == "RenkDegisti"); } FIKO_TEST(olay_ozel_ozet) { OlayGunlugu g; g.yayinla(KipDegisti{3}, "Kesim kipine gecildi"); const auto& kayitlar = g.kayitlar(); FIKO_KONTROL(kayitlar.size() == 1); FIKO_KONTROL(std::string(kayitlar[0].ozet) == "Kesim kipine gecildi"); } FIKO_TEST(olay_varliksiz_kayit) { // SecimDegisti'nin varlik alanı yok → kayıt varlik=0 olmalı. OlayGunlugu g; bool cagrildi = false; g.abone([&](const SecimDegisti&) { cagrildi = true; }); g.yayinla(SecimDegisti{}); FIKO_KONTROL(cagrildi); FIKO_KONTROL(g.kayitlar().size() == 1); FIKO_KONTROL(g.kayitlar()[0].varlik == 0); } FIKO_TEST(olay_coklu_abone) { OlayGunlugu g; int a = 0, b = 0; g.abone([&](const BrepDegisti&) { ++a; }); g.abone([&](const BrepDegisti&) { ++b; }); g.yayinla(BrepDegisti{5}); FIKO_KONTROL(a == 1); FIKO_KONTROL(b == 1); } FIKO_TEST(olay_diske_artimli_yazim) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; g.yayinla(VarlikEklendi{1}); g.yayinla(DonusumDegisti{1}); g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 2); // Tekrar diskeYaz: yeni kayıt yok → mükerrer eklenmemeli. g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 2); // Yeni olaylar + tekrar yaz: yalnız yeniler eklenir (toplam 4). g.yayinla(RenkDegisti{2}); g.yayinla(BrepDegisti{3}); g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 4); std::remove(yol.c_str()); } FIKO_TEST(olay_diske_filtreli_yazim) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; // Varsayılan: SecimDegisti kalıcı DEĞİL, VarlikEklendi kalıcı. FIKO_KONTROL(g.kalici(OlayTuru::SecimDegisti) == false); FIKO_KONTROL(g.kalici(OlayTuru::VarlikEklendi) == true); g.yayinla(SecimDegisti{}); g.yayinla(VarlikEklendi{7}); g.diskeYaz(yol); // SecimDegisti diske YAZILMAZ; VarlikEklendi yazılır → tek satır. FIKO_KONTROL(satirSay(yol) == 1); FIKO_KONTROL(satirIceriyor(yol, "SecimDegisti") == false); FIKO_KONTROL(satirIceriyor(yol, "VarlikEklendi") == true); std::remove(yol.c_str()); } FIKO_TEST(olay_vurgu_degisti_kalici_degil) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; FIKO_KONTROL(g.kalici(OlayTuru::VurguDegisti) == false); u32 varlik = 0, alt = 0, eleman = 0; g.abone([&](const VurguDegisti& o) { varlik = o.varlik; alt = o.alt; eleman = o.eleman; }); g.yayinla(VurguDegisti{5, 2, 1}); FIKO_KONTROL(varlik == 5); FIKO_KONTROL(alt == 2); FIKO_KONTROL(eleman == 1); // Halka tampona duser ama diske ASLA yazilmaz. FIKO_KONTROL(g.kayitlar().size() == 1); g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 0); FIKO_KONTROL(satirIceriyor(yol, "VurguDegisti") == false); std::remove(yol.c_str()); } FIKO_TEST(olay_diske_sifirla) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; g.yayinla(VarlikEklendi{1}); g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 1); // Sıfırla: imleç başa döner → aynı kayıtlar yeniden yazılır (dosyaya eklenir). g.diskeSifirla(); g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 2); std::remove(yol.c_str()); } // ---- Platform servisleri Dalga 1 — olay zenginleştirme testleri ---- FIKO_TEST(olay_meta_siddet_kaynak) { OlayGunlugu g; g.yayinla(RenderHatasi{3}, "gpu reset", OlaySiddet::Hata, OlayKaynak::Makro); const auto& k = g.kayitlar(); FIKO_KONTROL(k.size() == 1); FIKO_KONTROL(k[0].tur == OlayTuru::RenderHatasi); FIKO_KONTROL(k[0].siddet == OlaySiddet::Hata); FIKO_KONTROL(k[0].kaynak == OlayKaynak::Makro); FIKO_KONTROL(std::string(k[0].ozet) == "gpu reset"); // Varsayılan meta: siddet=Bilgi, kaynak=Sistem. g.yayinla(VarlikEklendi{1}); FIKO_KONTROL(g.kayitlar()[1].siddet == OlaySiddet::Bilgi); FIKO_KONTROL(g.kayitlar()[1].kaynak == OlayKaynak::Sistem); } FIKO_TEST(olay_korelasyon_kapsami) { OlayGunlugu g; const u64 id = g.korelasyonBaslat(); FIKO_KONTROL(id != 0); FIKO_KONTROL(g.korelasyonGuncel() == id); g.yayinla(VarlikEklendi{1}); g.yayinla(DonusumDegisti{1}); g.korelasyonBitir(); g.yayinla(RenkDegisti{1}); const auto& k = g.kayitlar(); FIKO_KONTROL(k[0].korelasyon == id); FIKO_KONTROL(k[1].korelasyon == id); // aynı zincir FIKO_KONTROL(k[2].korelasyon == 0); // kapsam dışı } FIKO_TEST(olay_filtreli_abone_siddet) { OlayGunlugu g; int hataAdet = 0; const u64 id = g.aboneFiltreli( [](const OlayKaydi& k) { return k.siddet >= OlaySiddet::Hata; }, [&](const OlayKaydi&) { ++hataAdet; }); g.yayinla(VarlikEklendi{1}); // Bilgi → atla g.yayinla(RenderHatasi{2}, nullptr, OlaySiddet::Hata); // eşleşir g.yayinla(DosyaHatasi{1}, nullptr, OlaySiddet::Kritik); // eşleşir FIKO_KONTROL(hataAdet == 2); g.aboneIptal(id); // filtreli abone iptali g.yayinla(DosyaHatasi{9}, nullptr, OlaySiddet::Kritik); FIKO_KONTROL(hataAdet == 2); } FIKO_TEST(olay_coklu_tur_abone) { OlayGunlugu g; int adet = 0; g.abone({OlayTuru::DosyaAcildi, OlayTuru::DosyaKaydedildi}, [&](const OlayKaydi&) { ++adet; }); g.yayinla(DosyaAcildi{1}); g.yayinla(DosyaKaydedildi{1}); g.yayinla(RenkDegisti{1}); // kümede yok FIKO_KONTROL(adet == 2); } FIKO_TEST(olay_siddet_esigi_disk) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; g.siddetEsigi(OlaySiddet::Uyari); // Bilgi/Iz altı diske girmez g.yayinla(VarlikEklendi{1}, nullptr, OlaySiddet::Bilgi); // eşik altı → atla g.yayinla(DosyaHatasi{2}, nullptr, OlaySiddet::Hata); // eşik üstü → yaz g.diskeYaz(yol); FIKO_KONTROL(satirSay(yol) == 1); FIKO_KONTROL(satirIceriyor(yol, "DosyaHatasi") == true); FIKO_KONTROL(satirIceriyor(yol, "VarlikEklendi") == false); std::remove(yol.c_str()); } FIKO_TEST(olay_audit_zincir_dogrula) { const std::string yol = testGunlukYolu(); std::remove(yol.c_str()); OlayGunlugu g; g.yayinla(VarlikEklendi{1}); g.yayinla(DonusumDegisti{2}); g.yayinla(RenkDegisti{3}); g.auditYaz(yol); FIKO_KONTROL(satirSay(yol) == 3); // Bozulmamış zincir doğrulanır. FIKO_KONTROL(OlayGunlugu::auditDogrula(yol) == true); // Tahrif: sahte hash'li bir satır ekle → zincir doğrulaması bozulur. { std::ofstream out(yol, std::ios::app); out << "999\t0.000000\tVarlikEklendi\t7\tBilgi\tsahte\tdeadbeefdeadbeef\n"; } FIKO_KONTROL(OlayGunlugu::auditDogrula(yol) == false); std::remove(yol.c_str()); } FIKO_TEST(olay_otomasyon_kural) { OlayGunlugu g; int tetik = 0; const u64 id = g.kuralEkle( [](const OlayKaydi& k) { return k.tur == OlayTuru::DosyaKaydedildi; }, [&]() { ++tetik; }); FIKO_KONTROL(g.kuralSayisi() == 1); g.yayinla(DosyaAcildi{1}); // eşleşmez g.yayinla(DosyaKaydedildi{1}); // tetikler FIKO_KONTROL(tetik == 1); g.aboneIptal(id); // kural da aynı kimlik havuzundan iptal edilir FIKO_KONTROL(g.kuralSayisi() == 0); g.yayinla(DosyaKaydedildi{2}); FIKO_KONTROL(tetik == 1); } FIKO_TEST(olay_otomasyon_dongu_guard) { OlayGunlugu g; int adet = 0; // Kural kendi türünü tekrar yayınlar → derinlik guard sonsuz döngüyü keser (çökme yok). g.kuralEkle( [](const OlayKaydi& k) { return k.tur == OlayTuru::VarlikEklendi; }, [&]() { ++adet; if (adet < 1000) g.yayinla(VarlikEklendi{0}); }); g.yayinla(VarlikEklendi{1}); // Sonlu + sınırlı (guard ~derinlik). Sonsuz olsaydı test asılırdı. FIKO_KONTROL(adet >= 1 && adet < 1000); }