İçeriğe geç
academia.sh

Ders 19 / 22

Senaryo Temelli Uçtan Uca Doğrulama

İki sınama katmanının yakaladığı kusur kümelerinin yan yana sayılması: yirmi dört senaryoluk uçtan uca akış sınaması, araç düzeyi sınamanın hiç göremediği dört tanım kusurunun üçünü yakalıyor ve toplamda on bir kusurun beşini düşürüyor. Araç düzeyi katman yediyi yakalıyor, iki katmanın kesişimi iki, birleşimi on bir kusurda on yani 0,9091. Kaçırmanın iki ayrı nedeni ateşleme sayısıyla ayrılıyor: D2, D4 ve D11 döngü boyunca bir kez bile ateşlemiyor, D5, D6 ve D7 ateşliyor ama senaryo sonucu değişmiyor. Kalıntı D11, işlevi doğru olduğu hâlde tanımında kullanılmayan bir alan bildiren kusurdur ve hiçbir koşum onu göremez. Kusursuz tabanda düşen senaryo iki koşumda G23 ve G15.

İçindekiler

Bir önceki ders araç düzeyi sınamanın sınırını çizdi: on bir kusurun yedisi yakalandı, dört tanım kusurunun hiçbiri görünmedi. Görünmemeleri onları zararsız yapmaz; işyeri abonelerinin bedeli yanlış hesaplanıyor. Bu kusurlar ancak araçlar birbirine bağlıyken ortaya çıkar.

Bu ders ikinci katmanı kurar. Uçtan uca akış sınaması aracı değil senaryoyu koşturur ve sonunda hem diziyi hem yanıtı denetler. Ölçülen şey katman değil, iki katmanın kusur kümeleri arasındaki ilişkidir.

  • KL12. Kusur kümesi bir önceki dersle aynıdır; kusurlar tablonun bir kopyasına konur. Araç düzeyi sütunu o dersin ölçümüdür, sınama kümesi burada tekrarlanmaz.
  • KL13. Uçtan uca akış sınaması: yirmi dört senaryo sekiz adımlık bütçeyle koşulur. Senaryo ancak dizi de yanıt da doğruysa geçer.
  • KL14. Kusursuz tabloda tabanı 24’te 23’tür. Bir kusur, düşen senaryo kümesi bu tabandan iki koşumda da farklıysa akış katmanınca yakalanmış sayılır.
  • KL15. Ateşleme sayısı: her çağrıda aynı girdi kusursuz tabloya da verilir ve iki sonuç ayrıldığında sayaç artar. Sıfır ateşleme, kusurun döngüde bir kez bile görünmediğidir.
  • KL16. Tohumlar 20260218 ve 20260219; iki koşum farkının altındaki fark ölçülmemiş sayılır.

Kurgu Ortam ve Ajan Döngüsü

Her senaryonun bilinen bir araç dizisi ve bilinen bir doğru yanıtı vardır; sınamanın kâhini bu ikilidir.

# 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(f"kurgu ortam {len(ABONE)} abone , {len(ARAC)} arac , {len(GOREV)} senaryo")
kurgu ortam 60 abone , 5 arac , 24 senaryo

İki Katman Yan Yana

Akış sınamasının gördüğü tek şey döngünün ürettiği dizi ile yanıttır. Ateşleme sayacı yanına bir sütun koyar: kusur o koşumda kaç çağrıda kusursuz tablodan ayrıldı. Araç düzeyi sütunu bir önceki dersten girdi olarak gelir.

KUSUR = {
    "D1": ("sayac_oku", "deger",
           lambda abone=None, **_: ({"hata": "abone yok"} if abone not in ABONE
                                    else dict(ABONE[abone], simdiki=ABONE[abone]["onceki"]))),
    "D2": ("tuketim", "deger",
           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",
           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",
           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",
           lambda onceki=None, simdiki=None, **_: t_tuketim(onceki or 0, simdiki or 0)),
    "D6": ("tarife", "sozlesme",
           lambda m3=None, tip="mesken", **_: t_tarife(m3 or 0, tip)),
    "D7": ("kayit_yaz", "sozlesme",
           lambda abone=None, sutun=None, deger=None, **_: {
               "yazildi": True, "abone": abone, "sutun": sutun, "deger": deger}),
    "D8": ("tarife", "tanim", ("m3",)),                  # tip alanini bildirmiyor
    "D9": ("kayit_yaz", "tanim", ("abone", "sutun")),     # deger alanini bildirmiyor
    "D10": ("belge_ara", "tanim", ("abone",)),            # anahtar yerine abone
    "D11": ("sayac_oku", "tanim", ("abone", "sutun")),    # islevin kullanmadigi alan
}
TEMIZ = {a: dict(v) for a, v in ARAC.items()}


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, p = KUSUR[kod][0], KUSUR[kod][2]
    t[ad]["alan" if isinstance(p, tuple) else "islev"] = p
    return t


# Bir onceki dersin katmani girdidir: on bir durumluk sinama kumesi orada
# kuruldu ve dusurdugu kusurlar sunlardi. Hesap burada tekrarlanmaz.
ARAC_DUZEYI = {"D1", "D2", "D3", "D4", "D5", "D6", "D7"}

ATES = [0]
_cagir = cagir_arac


def cagir_arac(ad, durum):
    """Cagriyi yururlukteki tabloyla kurar, ayni girdiyi kusursuz tabloya da
    verir ve iki sonuc ayrildiginda kusurun atesledigini sayar."""
    b = _cagir(ad, durum)
    kw = {a: durum[a] for a in TEMIZ[ad]["alan"] if a in durum}
    ATES[0] += b != TEMIZ[ad]["islev"](**kw)
    return b


def dizi_dogru(g, iz):
    k = 0
    for ad, hatali in iz:
        if k < len(g["dizi"]) and ad == g["dizi"][k] and not hatali:
            k += 1
    return k == len(g["dizi"])


def akis_sinamasi(tablo, tohum=TOHUM):
    """Uctan uca senaryo: dizi de yanit da dogruysa gecer."""
    global ARAC
    asil, ARAC, ATES[0] = ARAC, tablo, 0
    d = []
    for g in GOREV:
        s = ajan(g, en_cok_adim=8, tohum=tohum)
        if not (dizi_dogru(g, s["iz"]) and s["yanit"] == dogru_yanit(g)):
            d.append(g["ad"])
    ARAC = asil
    return d, ATES[0]


taban = [akis_sinamasi(TEMIZ, t) for t in (TOHUM, 20260219)]
print(f"akis sinamasi {len(GOREV)} senaryo , kusursuz tabloda dusen senaryo "
      f"{taban[0][0]} ve {taban[1][0]}")
print("\nkod   arac        sinif      arac-duzeyi  akis-1  akis-2  ates-1  ates-2")
AD, AK = set(), set()
for kod, (ad, sinif, p) in KUSUR.items():
    t, ar = kusurlu(kod), kod in ARAC_DUZEYI
    (d1, a1), (d2, a2) = akis_sinamasi(t, TOHUM), akis_sinamasi(t, 20260219)
    if ar:
        AD.add(kod)
    if d1 != taban[0][0] and d2 != taban[1][0]:
        AK.add(kod)
    print(f"{kod:<5} {ad:<11} {sinif:<9} {'+' if ar else '-':>11} {len(d1):>7} {len(d2):>7}"
          f" {a1:>7} {a2:>7}")

n = len(KUSUR)
print()
for et, s in (("ikisi birden", AD & AK), ("yalniz akis", AK - AD),
              ("yalniz arac duzeyi", AD - AK), ("kalinti", set(KUSUR) - AD - AK)):
    print(f"{et:<19} {' '.join(k for k in KUSUR if k in s)}")
print(f"arac duzeyi {len(AD)}/{n} = {len(AD) / n:.4f} , akis {len(AK)}/{n} = "
      f"{len(AK) / n:.4f} , birlesim {len(AD | AK)}/{n} = {len(AD | AK) / n:.4f}")
akis sinamasi 24 senaryo , kusursuz tabloda dusen senaryo ['G23'] ve ['G15']

kod   arac        sinif      arac-duzeyi  akis-1  akis-2  ates-1  ates-2
D1    sayac_oku   deger               +      17      17      18      20
D2    tuketim     deger               +       1       1       0       0
D3    tarife      deger               +       7       7       9       8
D4    belge_ara   deger               +       1       1       0       0
D5    tuketim     sozlesme            +       1       1       2       1
D6    tarife      sozlesme            +       1       1       4       5
D7    kayit_yaz   sozlesme            +       1       1       2       1
D8    tarife      tanim               -       5       6       6       5
D9    kayit_yaz   tanim               -       3       3       7      14
D10   belge_ara   tanim               -       7       8      44      49
D11   sayac_oku   tanim               -       1       1       0       0

ikisi birden        D1 D3
yalniz akis         D8 D9 D10
yalniz arac duzeyi  D2 D4 D5 D6 D7
kalinti             D11
arac duzeyi 7/11 = 0.6364 , akis 5/11 = 0.4545 , birlesim 10/11 = 0.9091

Kesişmeyen İki Küme

Araç düzeyi katman 7/11, akış katmanı 5/11 yakalıyor. Kesişim yalnız iki kusurdur: D1 ve D3. D1 her sayaç okumasında ateşliyor ve yirmi dört senaryonun on yedisini düşürüyor; D3 yalnız üst dilimi aşan tüketimlerde ateşliyor ve yedisini.

Asıl bilgi kesişimin dışındadır. Akış katmanı D8, D9, D10 kusurlarını, yani araç düzeyinin hiç göremediği dört tanım kusurunun üçünü yakalıyor. Neden mekanizmadadır: akış sınaması aracı işlevinden değil, döngünün kurduğu çağrıdan görür ve o çağrı bildirilen alan listesinden geçer. D10 en yüksek ateşlemeyi veriyor: 44 ve 49. Ters yönde D2, D4, D5, D6, D7 yalnız araç düzeyinde görünüyor. Birleşim 10/11 = 0,9091; hiçbir katman tek başına buraya yaklaşmıyor.

Ateşlemeyen Kusur ve Kalıntı

Akışın kaçırdığı kusurların iki ayrı nedeni vardır ve ateşleme sütunu ikisini ayırır. D2 ve D4 iki koşumda da sıfır ateşliyor: sayaç dönüşü dalına bu görev kümesinde hiç girilmiyor ve belge sorgusunda ilk örtüşen belge zaten en yüksek örtüşen belge. Kusur bir kez bile ortaya çıkmadı; akışın onu bulma şansı yoktu. D5, D6 ve D7 ise ateşliyor — sırasıyla 2, 4 ve 2 çağrıda — ama düşen senaryo kümesi değişmiyor: kusur koşuldu, sonucu bozdu ve senaryo yine de geçti. İlki bir kapsam eksiği, ikincisi bir gözlem eksiğidir.

Ateşleme sayısı bir büyüklük olarak okunmaz. D9 ilk koşumda 7, ikincide 14 ateşliyor; D1 18 ve 20. Okunan şey sıfır olup olmadığıdır.

Kalıntı tek kusurdur ve adı D11. sayac_oku işlevi doğrudur, ama tanımı işlevin hiç kullanmadığı bir alan bildirir. Araç düzeyi sınama göremez, çünkü işlev doğru çalışır. Akış sınaması göremez, çünkü fazladan bildirilen alan çağrıya girer ve sessizce yutulur: ateşleme iki koşumda da sıfır. Kusur davranışta değil sözleşmenin kendisindedir ve onu okuyacak olan, aracı çağıracak bir başkasıdır.

Özet

  • Akış sınaması senaryoyu koşturur ve yalnız dizi ile yanıtı görür; kusursuz tabanda düşen senaryo iki koşumda G23 ve G15’tir.
  • Akış katmanı 11 kusurun 5’ini, araç düzeyi katman 7’sini yakalar; kesişim yalnız D1 ve D3, birleşim 10/11 = 0,9091.
  • Akış katmanı, araç düzeyinin göremediği dört tanım kusurunun üçünü yakalar, çünkü çağrıyı bildirilen alan listesinden geçirir.
  • Kaçırmanın nedeni ikiye ayrılır: D2, D4 ve D11 hiç ateşlemez; D5, D6 ve D7 ateşler ama senaryo sonucunu değiştirmez.
  • Kalıntı D11’dir: işlev doğru, tanım fazladan bir alan bildiriyor; ateşleme sıfır olduğu için hiçbir koşum bu kusuru gösteremez.

Sonraki Adım

İki katman birlikte on bir kusurun onunu buldu, ama ikisinin ortak bir yanı var: kusuru biz koyduk ve nerede olduğunu baştan biliyorduk. Sınama, ajanın sınandığı koşumları görür; üretimde koşan bir döngü hakkında hiçbir şey söylemez. Orada düşen bir senaryo yoktur, yalnız bir kullanıcı ve yanlış bir yanıt vardır. Bir sonraki ders koşumun geriye ne bıraktığını ölçer: üç iz düzeyinde başarısız bir görevin nedeninin bulunabildiği pay ve saklanan belirteç.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat