İçeriğe geç
academia.sh

Ders 18 / 26

Üretilen Kodun İncelenmesi

Kurgu bir kod tabanına gelen yirmi dört üretilmiş yamanın üç savunma denetiminden geçirilmesi: kabul sınaması altı davranış kusurunun beşini yakalıyor, örüntü taraması sekiz bulgunun sekizini, bağımlılık varlık denetimi dokuz uydurma adın dokuzunu. Yirmi sekiz kusurun yirmi altısı yakalanıyor, ikisi biçim hatası olarak kaçıyor ve kaçanlardan biri hiçbir uyarı almadan kod tabanına giriyor.

İçindekiler

Önceki ders çok adımlı bir devirde denetim noktasının nereye konacağını ölçtü: kaç adımda bir durulduğunda hata yayılmadan yakalanıyor ve durmanın bedeli ne. Denetim noktası bir yerdir. Orada ne yapılacağı söylenmedi. Duran insanın önünde bir yama durur ve o yamaya bakmanın kendi kapsamı, kendi maliyeti ve kendi kör noktası vardır.

Bu ders o bakışı üç ayrı denetime böler ve her birinin eriştiği kusur sınıfını sayar: kod tabanının sınama koşumu, üretilen metnin örüntü taraması ve alınan bağımlılıkların varlık denetimi. Kod incelemesinin genel yordamı Yazılım Kalitesi ve Test müfredatında, güvenlik denetiminin yordamı Siber Güvenlik ve Uygulama Güvenliği müfredatlarında ölçüldü; burada tekrarlanmaz. Eklenen tek şey üreticinin kimliğidir: incelenen yama bir insandan değil belirsiz bir bileşenden geliyor, bu yüzden kusurların dağılımı başkadır ve denetimlerin kapsadığı alanın sınırı sayılmalıdır.

  • YG31. Kod tabanı kurgudur: ölçüm ağının kendi yardımcı betikleri. Hiçbir gerçek depo, paket ya da ürün değildir. Beş modül, 53 satır. Tohum 20260218.
  • YG32. Bu ders bir saldırı dersi değildir. Koşan kod her zaman savunma tarafındadır: sınama, tarama, sayım, doğrulama. Hiçbir sömürü kodu, yük ya da atlatma yordamı yazılmaz.
  • YG33. Üretilen yama iki parçadır: değişen bir modül ve yeni bir yardımcı dosya. Yardımcı dosyayı hiçbir sınama çağırmaz; kod tabanına henüz bağlanmamıştır.
  • YG34. Kod tabanına konan altı davranış kusuru kaynak metinde tek dizge değişimidir; altıncısı bir biçim kusurudur ve çıktının hizasını bozar.
  • YG35. Kabul sınaması on maddedir ve ders içinde yazılır. Kod tabanının kendi sınamasıdır, üretilen bir sınama değildir.
  • YG36. Örüntü taraması dört dizge arar. Bulgu bir kanıt değil bir işarettir; insanın bakması gereken satırı gösterir.
  • YG37. Varlık denetimi: bir import satırındaki ad kod tabanının modülleri arasında ya da izinli standart kitaplık adları arasında değilse uydurmadır. Uydurma adlar kurgudur.
  • YG38. Sayılan birim kusurdur, aday değildir. Bir aday birden çok kusur taşıyabilir; işaretli aday en az bir denetimden uyarı alandır.

Kurgu Kod Tabanı ve İncelemenin Konusu

İnceleme, üzerinde durulacak bir gövde ister. Gövde şudur: ölçüm ağının okuma, tarife hesabı, kayıt denetimi, bölge özeti ve çıktı biçimlendirmesi için yazılmış beş küçük modülü. Modüller metin olarak tutulur ve çalıştırılmak istendiğinde ayrı bir ad alanına yüklenir; bu, aynı kod tabanının bozulmuş bir sürümünü yan yana koşturmayı ucuzlatır.

# KURGU kod tabani: olcum aginin kendi yardimci betikleri. Gercek bir depo,
# paket ya da urun degildir. Bes modul, 53 satir. Tohum 20260218.
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 izno(m):
    h = 2166136261
    for c in m:
        h = ((h ^ ord(c)) * 16777619) & M32
    return h


KOD = {
    "okuma.py": """
def ayristir(satir):
    p = satir.split(";")
    if len(p) != 4:
        return None
    if not p[3].lstrip("-").isdigit():
        return None
    return {"abone": p[0], "bolge": p[1], "donem": p[2], "endeks": int(p[3])}

def tuketim(onceki, simdiki):
    fark = simdiki - onceki
    if fark < 0:
        fark = fark + 100000
    return fark
""",
    "tarife.py": """
BASAMAK = ((10, 4), (25, 7), (40, 11))
USTU = 16

def bedel(m3):
    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
    return top + kalan * USTU
""",
    "denetim.py": """
GECERLI_BOLGE = ("dogu", "bati", "kuzey", "guney", "merkez")

def gecerli(kayit):
    if kayit is None:
        return False
    if kayit["endeks"] < 0:
        return False
    return kayit["bolge"] in GECERLI_BOLGE

def ayikla(kayitlar):
    return [k for k in kayitlar if gecerli(k)]
""",
    "ozet.py": """
def bolge_toplam(kayitlar, tuketimler):
    top = {}
    for k, t in zip(kayitlar, tuketimler):
        top[k["bolge"]] = top.get(k["bolge"], 0) + t
    return top

def bolge_sayi(kayitlar):
    say = {}
    for k in kayitlar:
        say[k["bolge"]] = say.get(k["bolge"], 0) + 1
    return say
""",
    "bicim.py": """
def satir(bolge, m3, tutar):
    return bolge.ljust(8) + str(m3).rjust(7) + str(tutar).rjust(9)

def rapor(baslik, satirlar):
    return "\\n".join([baslik] + satirlar)
""",
}


def yukle(kod):
    ad = {}
    for dosya, kaynak in kod.items():
        alan = {}
        exec(kaynak, alan)
        ad[dosya] = alan
    return ad


SINIF = ("tamamlama", "aciklama", "donusturme")
DOSYA = ("okuma.py", "tarife.py", "denetim.py", "ozet.py", "bicim.py")
GEREKEN = {0: ("okuma.py",), 1: ("tarife.py",), 2: ("denetim.py", "okuma.py"),
           3: ("ozet.py", "denetim.py"), 4: ("bicim.py", "ozet.py"),
           5: ("tarife.py", "okuma.py"), 6: ("ozet.py", "okuma.py", "tarife.py"),
           7: ("denetim.py", "bicim.py")}
GOREV = [{"ad": s + "-" + str(i + 1), "sinif": s, "gereken": GEREKEN[i],
          "zorluk": 0.05 * (i % 4)} for s in SINIF for i in range(8)]

BOY = {d: len(k.strip().split("\n")) for d, k in KOD.items()}
AD = yukle(KOD)
print("kod tabani:", BOY, "toplam", sum(BOY.values()), "satir")
print("gorev sayisi:", len(GOREV), "-", " , ".join(SINIF))
print("bedel(0,10,25,40,60):", [AD["tarife.py"]["bedel"](m) for m in (0, 10, 25, 40, 60)])
print("tuketim(99800,300):", AD["okuma.py"]["tuketim"](99800, 300))
kod tabani: {'okuma.py': 13, 'tarife.py': 13, 'denetim.py': 11, 'ozet.py': 11, 'bicim.py': 5} toplam 53 satir
gorev sayisi: 24 - tamamlama , aciklama , donusturme
bedel(0,10,25,40,60): [0, 40, 145, 310, 630]
tuketim(99800,300): 500

Son iki satır kod tabanının doğru davranışını sabitliyor. Kademeli tarifede altmış metreküp 630 ediyor; sayaç doksan dokuz binden üç yüze döndüğünde tüketim 500 çıkıyor. Bu iki sayı bundan sonra ölçüt olacak: bir yama bunları değiştiriyorsa yama yanlıştır. İnceleme yordamının ilk kuralı budur — doğruluk bir izlenim değil, önceden yazılmış bir beklentidir.

Yirmi dört kurgu görev üç sınıfa ayrılmıştır ve her görev belirli modülleri ilgilendirir. Bir görevin yanıtı olarak gelen yama, o modüllerden birini değiştirir ve yanına bir yardımcı dosya bırakır. Denetimlerin erişimi bu iki parça arasında bölünecektir.

Üç Denetimin Erişimi

Üç denetim üç ayrı yere bakar. Sınama koşumu yalnız değişen modülü görür ve yalnız sınamaların ölçtüğü davranışı görür. Örüntü taraması metne bakar, çalıştırmaz. Varlık denetimi yalnız import satırlarına bakar. Üçünün birleşimi kod tabanının tamamı değildir ve bu dersin ölçtüğü şey tam olarak aradaki boşluktur.

HATA = [("H1", "tarife.py", "USTU = 16", "USTU = 11"),
        ("H2", "okuma.py", "if fark < 0:", "if fark < -100000:"),
        ("H3", "denetim.py", 'kayit["endeks"] < 0', 'kayit["endeks"] < -1'),
        ("H4", "ozet.py", 'top.get(k["bolge"], 0) + t', 'top.get(k["bolge"], 0) + 1'),
        ("H5", "okuma.py", "len(p) != 4", "len(p) < 4"),
        ("H6", "bicim.py", "rjust(9)", "rjust(7)")]
UYDURMA = ("olcum_araclari", "tarife_yardim", "bolge_ozet", "sayac_kit")
ORUNTU = ("eval(", "except:", "== None", "open(")
EK = {"eval(": "def cevir(ifade):\n    return eval(ifade)\n",
      "except:": "def tamsayi(s):\n    try:\n        return int(s)\n    except:\n        return 0\n",
      "== None": "def bos_mu(kayit):\n    return kayit == None\n",
      "open(": "def yedek(yol):\n    return open(yol).read()\n"}
STANDART = ("math", "json")


def bozuk(kod, hata):
    _, dosya, eski, yeni = hata
    yeni_kod = dict(kod)
    yeni_kod[dosya] = kod[dosya].replace(eski, yeni, 1)
    return yeni_kod


def uret(gorev, tohum=TOHUM):
    r = uretec(tohum ^ izno(gorev["ad"]))
    u = [r() for _ in range(6)]
    hata = HATA[int(u[1] * 6)] if u[0] < 0.46 else None
    ithal = UYDURMA[int(u[3] * 4)] if u[2] < 0.30 else "math"
    oru = ORUNTU[int(u[5] * 4)] if u[4] < 0.34 else None
    return {"kod": bozuk(KOD, hata) if hata else dict(KOD),
            "hata": hata[0] if hata else None, "ithal": ithal, "oru": oru,
            "yardim": "import " + ithal + "\n\n" + (EK[oru] if oru else "")}


KAYITLAR = [{"bolge": "dogu", "endeks": 120}, {"bolge": "yildiz", "endeks": 40},
            {"bolge": "bati", "endeks": 90}]


def sinama_kos(kod):
    a = yukle(kod)
    o, t, d, z, b = (a["okuma.py"], a["tarife.py"], a["denetim.py"],
                     a["ozet.py"], a["bicim.py"])
    dusen = []
    for ad, sonuc in (
            ("ayristir-alan", o["ayristir"]("A017;dogu;2026-01;41250") ==
             {"abone": "A017", "bolge": "dogu", "donem": "2026-01", "endeks": 41250}),
            ("ayristir-fazla", o["ayristir"]("A017;dogu;2026-01;41250;ek") is None),
            ("tuketim-donus", o["tuketim"](99800, 300) == 500),
            ("bedel-basamak", [t["bedel"](m) for m in (0, 10, 25, 40, 60)] ==
             [0, 40, 145, 310, 630]),
            ("gecerli-eksi", d["gecerli"]({"bolge": "dogu", "endeks": -1}) is False),
            ("ayikla-bolge", len(d["ayikla"](KAYITLAR)) == 2),
            ("toplam-bolge", z["bolge_toplam"](KAYITLAR, [12, 5, 8]) ==
             {"dogu": 12, "yildiz": 5, "bati": 8}),
            ("sayi-bolge", z["bolge_sayi"](KAYITLAR) ==
             {"dogu": 1, "yildiz": 1, "bati": 1}),
            ("bicim-satir", "dogu" in b["satir"]("dogu", 12, 48)),
            ("bicim-rapor", b["rapor"]("bas", ["a", "b"]).count("\n") == 2)):
        if not sonuc:
            dusen.append(ad)
    return dusen


def oruntu_tara(metin):
    return [o for o in ORUNTU if o in metin]


def bagimlilik_dene(metin, kod):
    yok = []
    for s in metin.split("\n"):
        if s.startswith("import "):
            a = s[7:].strip()
            if a + ".py" not in kod and a not in STANDART:
                yok.append(a)
    return yok


def olc(tohum=TOHUM):
    kusur = {"davranis": 0, "oruntu": 0, "uydurma": 0}
    yakalanan = {"sinama": 0, "oruntu": 0, "bagimlilik": 0}
    kacan, sessiz, isaretli = [], 0, 0
    for g in GOREV:
        a = uret(g, tohum)
        bayrak = 0
        if a["oru"]:
            kusur["oruntu"] += 1
            if oruntu_tara(a["yardim"]):
                yakalanan["oruntu"] += 1
                bayrak += 1
        if a["ithal"] in UYDURMA:
            kusur["uydurma"] += 1
            if bagimlilik_dene(a["yardim"], a["kod"]):
                yakalanan["bagimlilik"] += 1
                bayrak += 1
        if a["hata"]:
            kusur["davranis"] += 1
            if sinama_kos(a["kod"]):
                yakalanan["sinama"] += 1
                bayrak += 1
            else:
                kacan.append((g["ad"], a["hata"],
                              "isaretli" if bayrak else "temiz"))
                sessiz += bayrak == 0
        isaretli += bayrak > 0
    return kusur, yakalanan, kacan, sessiz, isaretli


print("taban ORUNTU bulgusu:", oruntu_tara("".join(KOD.values())) or "yok")
print("kusur  dosya        dusen sinama")
for h in HATA:
    print("%-6s %-12s %s" % (h[0], h[1], sinama_kos(bozuk(KOD, h)) or "yok"))
print()
print("tohum     | davranis oruntu uydurma | sinama oruntu bagimlilik"
      " | isaretli kacan sessiz")
sonuc = {}
for tohum in (TOHUM, TOHUM + 1):
    k, y, kc, ss, isr = olc(tohum)
    sonuc[tohum] = kc
    print("%d  | %8d %6d %7d | %6d %6d %10d | %8d %5d %6d"
          % (tohum, k["davranis"], k["oruntu"], k["uydurma"], y["sinama"],
             y["oruntu"], y["bagimlilik"], isr, len(kc), ss))
print("kacan kusur, tohum 20260218:", sonuc[TOHUM])
print("kacan kusur, tohum 20260219:", sonuc[TOHUM + 1] or "yok")
taban ORUNTU bulgusu: yok
kusur  dosya        dusen sinama
H1     tarife.py    ['bedel-basamak']
H2     okuma.py     ['tuketim-donus']
H3     denetim.py   ['gecerli-eksi']
H4     ozet.py      ['toplam-bolge']
H5     okuma.py     ['ayristir-fazla']
H6     bicim.py     yok

tohum     | davranis oruntu uydurma | sinama oruntu bagimlilik | isaretli kacan sessiz
20260218  |       11      8       9 |      9      8          9 |       16     2      1
20260219  |        6      9      12 |      6      9         12 |       17     0      0
kacan kusur, tohum 20260218: [('tamamlama-4', 'H6', 'isaretli'), ('aciklama-7', 'H6', 'temiz')]
kacan kusur, tohum 20260219: yok

İlk satır taramanın temiz bir taban üzerinde çalıştığını söylüyor: kod tabanının kendisinde aranan dört dizgeden hiçbiri yok. Bulgu çıkarsa üretilen koddan çıkacak demektir; bu, tarama sonuçlarını yorumlanabilir kılan önkoşuldur.

İkinci tablo bu dersin koşumdan bağımsız ölçüsüdür. Altı davranış kusuru tek tek kod tabanına konur ve kabul sınaması koşturulur. Beşi tek bir sınamayı düşürüyor: kademeli bedel, sayaç dönüşü, eksi endeks denetimi, bölge toplamı ve fazla alanlı satır. Altıncısı hiçbir sınamayı düşürmüyor. Sebep sınamanın kendisindedir: biçim modülünü ölçen iki madde çıktının içinde bölge adının geçmesine ve satır sayısına bakıyor, sütun genişliğine bakmıyor. Kusur oradadır ve sınama oraya bakmaz. Bu sonuç tohumdan, görev dağılımından ve üretimden bağımsızdır.

Kalıntı ve İşaretin Yanıltıcılığı

Üçüncü tablo iki koşumu yan yana koyuyor. İlk koşumda yirmi dört yamada 11 davranış kusuru, 8 örüntü bulgusu ve 9 uydurma bağımlılık var; toplam 28 kusur. Örüntü taraması sekizin sekizini, varlık denetimi dokuzun dokuzunu yakalıyor. Sınama koşumu on birin dokuzunu yakalıyor. Toplamda 28 kusurun 26’sı görülüyor, 2’si kaçıyor ve ikisi de biçim kusuru.

İkinci koşumda kaçan yoktur ve bu, denetimin iyileştiği anlamına gelmez. O koşumda hiç biçim kusuru üretilmemiştir; üretilen altı davranış kusurunun altısı da sınamanın baktığı yerdedir. Kaçan sayısı kusurun dağılımına bağlıdır, denetimin kapsamına değil. Kapsamı okuyan sayı ikinci tablodadır ve orada altıda bir kusur örtüsüz durur. Bir denetimin gücü, iyi bir koşumda verdiği sıfırla değil, erişemediği sınıfın adıyla ölçülür.

Son satır işaretin kendisini sorguluyor. Kaçan iki yamadan biri (tamamlama-4) aslında işaretlidir: uydurma bir bağımlılık almış ve yardımcı dosyasında bir örüntü bulgusu var. Bu yamaya bakan insan iki uyarı görür, ikisini de giderir ve yamayı kabul eder. Giderilenler kusurdur, ama kusurun tamamı değildir; biçim hatası kabulle birlikte kod tabanına girer. İkincisi (aciklama-7) hiç uyarı almaz ve doğrudan girer. Bedel sütunu da buradadır: ilk koşumda 16 aday işaretlenir, yani insanın bakması gereken yama sayısı yirmi dörtte on altıdır; işaretsiz geçen sekiz yamanın biri kusurludur. İkinci koşumda işaretli sayı 17’ye çıkar ve elde edilen kazanç sıfırdır, çünkü o koşumda zaten kaçan yoktu. İnceleme yükü kusur sayısıyla değil, işaret sayısıyla büyür.

Özet

  • Kurgu kod tabanı beş modül ve 53 satırdır; doğru davranışı iki sayıyla sabitlenir, kademeli bedelde 630 ve sayaç dönüşünde 500.
  • Üç denetim üç ayrı yere bakar: sınama koşumu değişen modülün davranışına, örüntü taraması metne, varlık denetimi yalnız içe aktarma satırlarına.
  • Koşumdan bağımsız ölçü: altı davranış kusurunun beşi kabul sınamasını düşürür, biçim kusuru hiçbirini düşürmez, çünkü sınama sütun genişliğine bakmaz.
  • İlk koşumda 28 kusurun 26’sı yakalanır, 2’si kaçar; ikinci koşumda kaçan sıfırdır ama bunun nedeni kapsamın değil kusur dağılımının değişmesidir.
  • İşaretli aday sayısı yakalanan kusur sayısı değildir: kaçan iki yamadan biri başka bir kusur yüzünden işaretlidir ve uyarıları giderilerek kabul edilir.
  • İnceleme bedeli işaret sayısıyla ölçülür; ilk koşumda 24 yamanın 16’sı insana gider ve işaretsiz geçen sekizden biri kusur taşır.

Sonraki Adım

Bu derste kabul sınaması elle yazılmıştı ve kapsamı bilinen bir büyüklüktü: on madde, beş modül, bilinen bir kör nokta. Sınamayı yazmak da bir iştir ve aynı yardımcıya devredilebilecek görevlerin en çok önerileni budur. Sonraki ders sınamayı üretilen tarafa geçirir ve aynı soruyu sınamanın kendisine sorar: üretilen sınamalar kod tabanına konan altı kusurun kaçını yakalıyor, kaç sınama bütün sürümlerde aynı sonucu verip hiçbir şey ayırt etmiyor, ve üretilen belgede kaç iddia makinece doğrulanabiliyor.

İ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