Ders 18 / 22
Her Aracın Döngüden Ayrı Sınanması
Beş araca konan on bir kurgu kusurun araç düzeyi sınamayla ne kadarının yakalandığının sayılması: on bir durumluk sınama kümesi değer kusurlarının dördünü de, sözleşme kusurlarının üçünü de düşürüyor, ama tanım kusurlarının dördünde de tertemiz geçiyor. Toplam yakalama 0,6364, sınıf bazında 1,0000 ve 1,0000 ile 0,0000. Görünmeyen sınıfın mekanizması yazılıdır: sınama kümesi aracın işlevini doğrudan çağırır, döngü ise aracın bildirdiği alan listesinden geçer ve tanım kusuru yalnız o listede durur. Katmanın karşılığı koşumdan bağımsızlıktır: aynı görev kümesinde döngünün düşen senaryosu tohum değişince G23'ten G15'e kayarken, sınama kümesinin düşen durum kümesi iki koşumda da boş ve aynıdır.
İçindekiler
Bir önceki ders bir ajan koşumunu iki sütuna ayırdı: dizinin doğruluğu ve yanıtın doğruluğu. İkisi de araçların doğru çalıştığını varsayar. Varsayım kaldırıldığı anda dört durum tablosu yetersiz kalır: bir görevin düştüğünü söyler, hangi aracın bozuk olduğunu söylemez.
Bu ders araçları döngüden söker ve tek tek sınar. Sınama seviyeleri M21 Yazılım Kalitesi ve Test müfredatının Kalite ve Test Temelleri kursunda sınıflandırıldı, birim sınamanın anatomisi Birim Testi ve Test Odaklı Geliştirme kursunda kuruldu; yordam burada tekrarlanmaz. Soru ölçüye aittir: beş araca konan kusurların kaçı bu katmanda görünüyor ve hangi sınıfı bu katman hiç göremiyor.
- KL7. Kusurlar kurgudur ve araç tablosunun bir kopyasına konur; asıl tablo değişmez.
- KL8. Üç kusur sınıfı vardır. Değer kusuru: işlev yanlış sonuç verir. Sözleşme kusuru: işlev, hata bildirmesi gereken yerde bildirmez. Tanım kusuru: işlev doğrudur, bildirilen alan listesi yanlıştır.
- KL9. Araç düzeyi sınama aracı döngünün dışında, doğrudan işlevini çağırarak sınar; beklenen çıktı elle yazılmıştır.
- KL10. Bir kusur, sınama kümesinden en az bir durum düşürüyorsa yakalanmış sayılır.
- KL11. Sınama kümesi tohum kullanmaz; döngü ölçüsü sekiz adımlık bütçeyle ve iki tohumla koşulur.
Ortam, Araç Tanımları ve Ajan Döngüsü
Bir araç iki parçadan oluşur: işlev hesabı yapar, tanım o işlevin hangi alanları alacağını bildirir ve döngü çağrıyı yalnız bildirilen alanlarla kurar.
# KURGUDUR. Abone kayitlari, belge dagarcigi, arac kumesi, gorev kumesi ve ajan # dongusu ders icinde uretilir; gercek bir uc nokta ya da arac sunucusu cagrilmaz. TOHUM, M32 = 20260218, 0xFFFFFFFF def uretec(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def sonraki(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return sonraki def ayrik(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 def belirtecle(m): b = [] for s in str(m).lower().replace(",", " , ").split(): while len(s) > 5: b.append(s[:5]) s = s[5:] b.append(s) return b def izno(m): h = 2166136261 for c in str(m): h = ((h ^ ord(c)) * 16777619) & M32 return h BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"] TIP = ["mesken", "isyeri"] ABONE = {} for i in range(60): r = uretec(TOHUM + 31 * i) onceki = 10000 + int(r() * 80000) ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)], "onceki": onceki, "simdiki": onceki + int(r() * 70)} BASAMAK, USTU = ((10, 4), (25, 7), (40, 11)), 16 ISYERI_KAT = 2 BELGE = {"TR01": "tarife basamaklari 10 25 40 metrekup ve ustu", "TR02": "isyeri abonesinde birim ucret iki katidir", "SY01": "endeks farki tuketimi verir sayac donusu 100000 eklenir", "AR01": "ariza kaydi acildiginda saha ekibi iki gun icinde gider", "KS01": "planli kesinti duyurusu bir hafta once yayimlanir"} CAGRI = [0] # arac cagri sayaci def _bedel(m3, tip): kalan, alt, top = m3, 0, 0 for ust, birim in BASAMAK: dilim = min(kalan, ust - alt) if dilim <= 0: break top += dilim * birim kalan -= dilim alt = ust top += kalan * USTU return top * (ISYERI_KAT if tip == "isyeri" else 1) def t_sayac_oku(abone=None, **_): a = ABONE.get(abone) if a is None: return {"hata": "abone yok"} return {"onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"], "bolge": a["bolge"]} def t_tuketim(onceki=None, simdiki=None, **_): if onceki is None or simdiki is None: return {"hata": "eksik alan"} f = simdiki - onceki return {"m3": f + 100000 if f < 0 else f} def t_tarife(m3=None, tip="mesken", **_): if m3 is None: return {"hata": "eksik alan"} return {"tutar": _bedel(m3, tip)} def t_belge(anahtar=None, **_): if not anahtar: return {"hata": "eksik alan"} ab = set(belirtecle(anahtar)) en, kod = -1, None for k, m in BELGE.items(): o = len(ab & set(belirtecle(m))) if o > en: en, kod = o, k return {"kod": kod, "metin": BELGE[kod]} if en > 0 else {"hata": "bulunamadi"} def t_kayit(abone=None, sutun=None, deger=None, **_): if not (abone and sutun and deger is not None): return {"hata": "eksik alan"} return {"yazildi": True, "abone": abone, "sutun": sutun, "deger": deger} ARAC = { "sayac_oku": {"islev": t_sayac_oku, "alan": ("abone",), "yan_etki": False}, "tuketim": {"islev": t_tuketim, "alan": ("onceki", "simdiki"), "yan_etki": False}, "tarife": {"islev": t_tarife, "alan": ("m3", "tip"), "yan_etki": False}, "belge_ara": {"islev": t_belge, "alan": ("anahtar",), "yan_etki": False}, "kayit_yaz": {"islev": t_kayit, "alan": ("abone", "sutun", "deger"), "yan_etki": True}, } ARAC_ADI = list(ARAC) def cagir_arac(ad, durum): """Sema disindaki alanlar suzulur; eksik alan aracin kendi hatasidir.""" CAGRI[0] += 1 if ad not in ARAC: return {"hata": "arac yok"} kw = {a: durum[a] for a in ARAC[ad]["alan"] if a in durum} return ARAC[ad]["islev"](**kw) GOREV = [] for i in range(24): r = uretec(TOHUM + 9973 * i) ab = f"A{1 + int(r() * 60):03d}" t = ayrik(r(), [0.30, 0.22, 0.20, 0.28]) if t == 0: GOREV.append({"ad": f"G{i + 1:02d}", "tur": "bedel", "abone": ab, "istek": f"{ab} abonesinin bu donem bedeli nedir", "dizi": ("sayac_oku", "tuketim", "tarife")}) elif t == 1: GOREV.append({"ad": f"G{i + 1:02d}", "tur": "tuketim", "abone": ab, "istek": f"{ab} abonesinin bu donem tuketimi kac metrekup", "dizi": ("sayac_oku", "tuketim")}) elif t == 2: GOREV.append({"ad": f"G{i + 1:02d}", "tur": "belge", "abone": ab, "istek": "sayac donusunde endeks farki nasil hesaplanir", "dizi": ("belge_ara",)}) else: GOREV.append({"ad": f"G{i + 1:02d}", "tur": "kayit", "abone": ab, "istek": f"{ab} abonesinin bedelini hesapla ve kayda yaz", "dizi": ("sayac_oku", "tuketim", "tarife", "kayit_yaz")}) def dogru_yanit(g): a = ABONE[g["abone"]] f = a["simdiki"] - a["onceki"] m3 = f + 100000 if f < 0 else f if g["tur"] == "tuketim": return m3 if g["tur"] == "belge": return "SY01" return _bedel(m3, a["tip"]) def gorme(d): return max(0.34, 0.80 - 0.09 * (d - 1)) # dogru araci secme payi, derinlikle duser def ajan(g, en_cok_adim=8, tohum=TOHUM, oz_elestiri=False, onay=False): """KURGU ajan dongusu. Gercek bir model cagrilmaz; arac secimi gorevin dizisine ve derinlige bagli tek bir cekilisten gelir.""" r = uretec(tohum ^ izno(g["ad"])) alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"} iz, adim, bel, hata = [], 0, 0, 0 dizi, k = g["dizi"], 0 while adim < en_cok_adim: adim += 1 bel += 60 + 22 * len(iz) hedef = dizi[k] if k < len(dizi) else None if hedef is None: # is bitti , ajan durmali if r() < 0.88: return {"bitti": True, "adim": adim, "iz": iz, "belirtec": bel, "hata": hata, "yanit": alan.get("sonuc"), "onay": alan.get("onay", 0)} sec = ARAC_ADI[int(r() * len(ARAC_ADI))] else: sec = hedef if r() < gorme(k + 1) else ARAC_ADI[int(r() * len(ARAC_ADI))] if ARAC[sec]["yan_etki"] and onay: alan["onay"] = alan.get("onay", 0) + 1 if sec == "kayit_yaz": alan["deger"] = alan.get("sonuc") s = cagir_arac(sec, alan) iz.append((sec, "hata" in s)) bel += 30 if "hata" in s: hata += 1 if oz_elestiri: bel += 40 continue alan.update({kk: vv for kk, vv in s.items() if kk != "hata"}) if sec == "tuketim": alan["sonuc"] = s["m3"] if sec == "tarife": alan["sonuc"] = s["tutar"] if sec == "belge_ara": alan["sonuc"] = s["kod"] if sec == hedef: k += 1 return {"bitti": False, "adim": adim, "iz": iz, "belirtec": bel, "hata": hata, "yanit": alan.get("sonuc"), "onay": alan.get("onay", 0)} print("arac bildirilen alanlar yan etki") for ad, v in ARAC.items(): print(f"{ad:<10} {', '.join(v['alan']):<24} {v['yan_etki']}")
arac bildirilen alanlar yan etki sayac_oku abone False tuketim onceki, simdiki False tarife m3, tip False belge_ara anahtar False kayit_yaz abone, sutun, deger True
Kusur Kümesi ve Sınama Kümesi
On bir kusur beş araca dağıtılır. Değer ve sözleşme kusurları işlevi değiştirir; tanım kusurları işleve dokunmaz, yalnız bildirilen alan listesini oynatır. Sınama kümesi her araç için bir olağan çağrı ve bir sınır durumu taşır.
KUSUR = { # arac , sinif , aciklama , kusurlu islev ya da alan listesi "D1": ("sayac_oku", "deger", "simdiki endeksi onceki ile ayni", lambda abone=None, **_: ({"hata": "abone yok"} if abone not in ABONE else dict(ABONE[abone], simdiki=ABONE[abone]["onceki"]))), "D2": ("tuketim", "deger", "sayac donusunde 10000 ekliyor", lambda onceki=None, simdiki=None, **_: ( {"hata": "eksik alan"} if onceki is None or simdiki is None else {"m3": simdiki - onceki + (10000 if simdiki < onceki else 0)})), "D3": ("tarife", "deger", "ust dilim birimi 16 yerine 11", lambda m3=None, tip="mesken", **_: ( {"hata": "eksik alan"} if m3 is None else {"tutar": _bedel(m3, tip) - max(m3 - 40, 0) * 5 * (ISYERI_KAT if tip == "isyeri" else 1)})), "D4": ("belge_ara", "deger", "en yuksek ortusme yerine ilki", lambda anahtar=None, **_: next( ({"kod": k, "metin": m} for k, m in BELGE.items() if set(belirtecle(anahtar or "")) & set(belirtecle(m))), {"hata": "eksik alan" if not anahtar else "bulunamadi"})), "D5": ("tuketim", "sozlesme", "eksik alanda hata yerine sifir", lambda onceki=None, simdiki=None, **_: t_tuketim(onceki or 0, simdiki or 0)), "D6": ("tarife", "sozlesme", "eksik alanda hata yerine sifir", lambda m3=None, tip="mesken", **_: t_tarife(m3 or 0, tip)), "D7": ("kayit_yaz", "sozlesme", "deger yokken yazildi diyor", lambda abone=None, sutun=None, deger=None, **_: { "yazildi": True, "abone": abone, "sutun": sutun, "deger": deger}), "D8": ("tarife", "tanim", "tanimi tip alanini bildirmiyor", ("m3",)), "D9": ("kayit_yaz", "tanim", "tanimi deger alanini bildirmiyor", ("abone", "sutun")), "D10": ("belge_ara", "tanim", "tanimi anahtar yerine abone bildiriyor", ("abone",)), "D11": ("sayac_oku", "tanim", "tanimi kullanilmayan alan bildiriyor", ("abone", "sutun")), } SINIF = ("deger", "sozlesme", "tanim") def kusurlu(kod): """Kusur konmus tablo kopyasi: dizi verilmisse bildirilen alan listesi, islev verilmisse aracin islevi degistirilir.""" t = {a: dict(v) for a, v in ARAC.items()} ad, kusur = KUSUR[kod][0], KUSUR[kod][3] t[ad]["alan" if isinstance(kusur, tuple) else "islev"] = kusur return t A1 = "A001" SINAMA = [ ("sayac_oku", {"abone": A1}, {"onceki": ABONE[A1]["onceki"], "simdiki": ABONE[A1]["simdiki"]}), ("sayac_oku", {"abone": "A999"}, {"hata": "abone yok"}), ("tuketim", {"onceki": 100, "simdiki": 150}, {"m3": 50}), ("tuketim", {"onceki": 99980, "simdiki": 30}, {"m3": 50}), ("tuketim", {"simdiki": 150}, {"hata": "eksik alan"}), ("tarife", {"m3": 50, "tip": "isyeri"}, {"tutar": 940}), ("tarife", {}, {"hata": "eksik alan"}), ("belge_ara", {"anahtar": "isyeri abonesinde birim ucret"}, {"kod": "TR02"}), ("belge_ara", {"anahtar": ""}, {"hata": "eksik alan"}), ("kayit_yaz", {"abone": A1, "sutun": "bedel", "deger": 500}, {"yazildi": True}), ("kayit_yaz", {"abone": A1, "sutun": "bedel"}, {"hata": "eksik alan"}), ] def arac_sinamasi(tablo): """Her araci dongunun disinda, dogrudan islevini cagirarak sinar.""" dusen = [] for ad, girdi, beklenen in SINAMA: c = tablo[ad]["islev"](**girdi) if ("hata" in c) != ("hata" in beklenen) or any( c.get(k) != v for k, v in beklenen.items()): dusen.append(ad) return dusen def dongu_basarim(tablo, tohum=TOHUM): global ARAC asil, ARAC = ARAC, tablo d = [g["ad"] for g in GOREV if ajan(g, en_cok_adim=8, tohum=tohum)["yanit"] != dogru_yanit(g)] ARAC = asil return d print(f"sinama kumesi {len(SINAMA)} durum , kusursuz tabloda dusen " f"{len(arac_sinamasi(ARAC))}") print(f"kusur kumesi {len(KUSUR)} kusur , {len(ARAC)} arac , {len(SINIF)} sinif\n") print(f"{'kod':<5} {'arac':<11} {'sinif':<9} {'dusen':>5} aciklama") yak = {s: [0, 0] for s in SINIF} for kod, (ad, sinif, acik, kusur) in KUSUR.items(): d = arac_sinamasi(kusurlu(kod)) yak[sinif][1] += 1 yak[sinif][0] += 1 if d else 0 print(f"{kod:<5} {ad:<11} {sinif:<9} {len(d):>5} {acik}") print("\nsinif kusur yakalanan pay") for s in SINIF: a, b = yak[s] print(f"{s:<10} {b:>5} {a:>10} {a / b:>7.4f}") tp = sum(a for a, _ in yak.values()) print(f"{'toplam':<10} {len(KUSUR):>5} {tp:>10} {tp / len(KUSUR):>7.4f}") print("\niki kosum , kusursuz tablo") print(f"arac duzeyi sinama , dusen durum {arac_sinamasi(ARAC)} ve {arac_sinamasi(ARAC)}") for t in (TOHUM, 20260219): d = dongu_basarim(ARAC, t) print(f"dongu tohum {t} , basarim {1 - len(d) / 24:.4f} , dusen gorev {d}")
sinama kumesi 11 durum , kusursuz tabloda dusen 0 kusur kumesi 11 kusur , 5 arac , 3 sinif kod arac sinif dusen aciklama D1 sayac_oku deger 1 simdiki endeksi onceki ile ayni D2 tuketim deger 1 sayac donusunde 10000 ekliyor D3 tarife deger 1 ust dilim birimi 16 yerine 11 D4 belge_ara deger 1 en yuksek ortusme yerine ilki D5 tuketim sozlesme 1 eksik alanda hata yerine sifir D6 tarife sozlesme 1 eksik alanda hata yerine sifir D7 kayit_yaz sozlesme 1 deger yokken yazildi diyor D8 tarife tanim 0 tanimi tip alanini bildirmiyor D9 kayit_yaz tanim 0 tanimi deger alanini bildirmiyor D10 belge_ara tanim 0 tanimi anahtar yerine abone bildiriyor D11 sayac_oku tanim 0 tanimi kullanilmayan alan bildiriyor sinif kusur yakalanan pay deger 4 4 1.0000 sozlesme 3 3 1.0000 tanim 4 0 0.0000 toplam 11 7 0.6364 iki kosum , kusursuz tablo arac duzeyi sinama , dusen durum [] ve [] dongu tohum 20260218 , basarim 0.9583 , dusen gorev ['G23'] dongu tohum 20260219 , basarim 0.9583 , dusen gorev ['G15']
Yakalanan Yedi, Görünmeyen Dört
Katmanın gücü ilk iki sınıfta tamdır: dört değer kusurunun dördü de, üç sözleşme kusurunun üçü de sınama kümesinden bir durum düşürüyor. Düşen durum sayısının birer olması da bir bilgidir; kusur kendi aracına yazılmış tek bir durumda görünür ve düşen durumun adı doğrudan bozuk aracı verir. Dört durum tablosunun söyleyemediği şey burada bir satırla söylenir.
Görünmeyen sınıf tesadüf değil, çağrı yolunun sonucudur. Sınama kümesi aracın işlevini
doğrudan çağırır ve bildirilen alan listesine hiç bakmaz. Döngü ise çağrıyı cagir_arac
üzerinden kurar ve durumdan yalnız bildirilen alanları süzer. Tanım kusuru tam bu süzgecin
içindedir: tarife işlevi işyeri katsayısını doğru uygular, ama tanımı tip alanını bildirmediği
için döngüde o alan hiç geçmez ve her işyeri abonesi mesken tarifesinden hesaplanır. İşlevi çağıran
bir sınama bunu göremez, çünkü işlev doğrudur. Dört tanım kusuru için de sonuç 0,0000.
Toplam yakalama 0,6364. Bu sayı bu kusur kümesine özgüdür; taşınan şey oran değil, oranın nasıl bölündüğüdür. İşlevi sınayan bir katman, işlev ile tanım arasındaki uyumsuzluğa kördür ve bu körlük durum ekleyerek kapanmaz.
Koşumdan Bağımsız Katman
Katmanın karşılığı son bloktadır. Kusursuz araç tablosunda döngü iki tohumda da 0,9583 veriyor,
ama düşen görev değişiyor: ilk koşumda G23, ikincide G15. Araç düzeyi sınama aynı tabloda
iki kez koşuldu ve düşen durum kümesi iki kez de boş ve aynı çıktı, çünkü bu katman tohum
kullanmaz.
Araç düzeyi sınamanın yargısı yinelenebilir: bir kusur ya her koşumda görünür ya da hiç görünmez. Döngü ölçüsünün yargısı bantlıdır: iki koşum farkı tek bir görevdir ve bandın altındaki hiçbir işaret bulgu sayılmaz.
Özet
- Bir araç işlev ile tanımın birleşimidir; araç düzeyi sınama işlevi doğrudan çağırır, tanımı hiç kullanmaz.
- On bir durumluk sınama kümesi dört değer kusurunun dördünü ve üç sözleşme kusurunun üçünü yakalar; her kusur tek bir durumda görünür ve düşen durum bozuk aracı adıyla verir.
- Dört tanım kusurunun hiçbiri yakalanmaz: pay 0,0000, toplam yakalama 0,6364.
- Körlüğün nedeni çağrı yoludur: döngü yalnız bildirilen alanları süzer, sınama kümesi işlevi doğrudan çağırıp o süzgeci atlar.
- Katmanın karşılığı koşumdan bağımsızlıktır: döngünün düşen görevi tohum değişince G23’ten G15’e kayarken, sınama kümesinin düşen durum kümesi iki koşumda da boştur.
Sonraki Adım
Bu ders bir katmanın sınırını sayıyla çizdi: araç düzeyi sınama, aracın kendi sözünü tutup tutmadığını ucuza ve kesin söyler, ama aracın döngüye nasıl bağlandığını hiç görmez. Görülmeyen dört kusur bir yerde ortaya çıkmak zorundadır, çünkü ajan işyeri abonelerine yanlış bedel yazar. Bir sonraki ders senaryoları uçtan uca koşturur ve iki soruyu birlikte sorar: akış sınaması araç düzeyinin kaçırdığı kusurların kaçını yakalıyor, ve iki katmanın da göremediği kusur hangisidir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.