---
title: 'Mobil Otomasyonu'
source: 'https://academia.sh/tr/kurslar/tumlesim-testi/mobil-otomasyonu'
course: 'Tümleşim, Sözleşme ve Uçtan Uca Test'
language: tr
updated: '2026-08-17T18:10:51+00:00'
license: 'CC BY-SA 4.0'
---

# Mobil Otomasyonu

Aygıt ve sürüm eksenlerinin çarpımıyla büyüyen koşum matrisinin ölçülmesi; kullanıcı payına göre ve kapsamaya göre seçilen alt kümelerin kapsadığı pay ile yakaladıkları kusur sınıflarının karşılaştırılması ve emülatörün taşıdığı boyutun sınırı.

Önceki dersin bütün ölçümleri tek bir sayfa modeli üzerindeydi: tek ağaç, tek zaman
çizelgesi. Ödünç akışının mobil arayüzü ise tek bir yerde koşmaz. Aynı ekran bir avuç
aygıt sınıfında ve birden çok işletim sistemi sürümünde açılır, ve testin sorusu artık
"hangi seçici dayanıklı" değil, "hangi aygıtlarda koşacağım" olur.

Bu ders gerçek aygıt ya da emülatör kurmaz. Ölçülen şey aygıtın kendisi değil **seçim
kararıdır**: matrisin ne kadar hızlı büyüdüğü, seçilen alt kümenin kaç kullanıcıyı temsil
ettiği ve kapsam dışında kalan kusur sınıfının ne olduğu. Üçü de sayılabilir.

## Matris ve İki Seçim Ölçütü

Model girdisi açıkça yazılır. **MB1 — altı aygıt sınıfı ve beş işletim sistemi sürümü;
paylar üye oturumlarından ölçülmüş kabul edilir ve her eksende toplamları yüzdür; iki
eksen bağımsız varsayılır.** Bağımsızlık varsayımı gerçek kurulumlarda geçerli değildir
(eski sürümler eski aygıtlarda yoğunlaşır); orada hücre payı doğrudan ölçülür ve aşağıdaki
hesap aynı biçimde çalışır.

```js
// matris.mjs — aygit-surum matrisi ve iki alt kume secimi
export const aygitlar = [
  ['kucuk', 22], ['orta', 26], ['buyuk', 18], ['katlanir', 6], ['tablet', 12], ['eski', 16],
];
export const surumler = [['S-6', 4], ['S-7', 9], ['S-8', 17], ['S-9', 31], ['S-10', 39]];

export const hucreler = aygitlar.flatMap(([aygit, ap]) =>
  surumler.map(([surum, sp]) => ({ aygit, surum, pay: (ap * sp) / 100 })));

export const payaGore = (k) => [...hucreler].sort((a, b) => b.pay - a.pay).slice(0, k);

export function kapsamayaGore(k) {
  const secilen = [];
  const gorulen = { aygit: new Set(), surum: new Set() };
  while (secilen.length < k) {
    const kalan = hucreler.filter((h) => secilen.includes(h) === false);
    const kazanc = (h) => (gorulen.aygit.has(h.aygit) ? 0 : 1) + (gorulen.surum.has(h.surum) ? 0 : 1);
    kalan.sort((a, b) => kazanc(b) - kazanc(a) || b.pay - a.pay);
    const secim = kalan[0];
    gorulen.aygit.add(secim.aygit);
    gorulen.surum.add(secim.surum);
    secilen.push(secim);
  }
  return secilen;
}

export const altKumeler = { paya: payaGore(6), kapsamaya: kapsamayaGore(6) };
```

İki ölçüt farklı şeyi eniyiler. Paya göre seçim en çok kullanıcının bulunduğu hücreleri
alır. Kapsamaya göre seçim her adımda henüz görülmemiş bir aygıt sınıfı ya da sürüm
kazandıran hücreyi tercih eder, eşitlikte paya bakar. Altışar hücre seçilir ve üç kusur
sınıfı için yakalama sayılır: sürüme bağlı kusur alt kümede o sürüm varsa, aygıt sınıfına
bağlı kusur o sınıf varsa, tek hücreye özgü kusur ise yalnız o hücre seçildiyse yakalanır.

```js
// kapsama.mjs — iki secimin kullanici payi ve kusur yakalama orani
import { aygitlar, surumler, hucreler, altKumeler } from './matris.mjs';

const oran = (secilen) => {
  const aygit = new Set(secilen.map((h) => h.aygit));
  const surum = new Set(secilen.map((h) => h.surum));
  return {
    pay: secilen.reduce((t, h) => t + h.pay, 0),
    surumKusuru: `${surum.size}/${surumler.length}`,
    aygitKusuru: `${aygit.size}/${aygitlar.length}`,
    hucreKusuru: `${secilen.length}/${hucreler.length}`,
  };
};

const s = (n, g) => String(n).padStart(g);
console.log(`${'secim'.padEnd(16)}${s('hucre', 6)}${s('kullanici payi', 16)}${s('surum kusuru', 14)}${s('aygit kusuru', 14)}${s('hucre kusuru', 14)}`);
for (const [ad, secilen] of [['paya gore', altKumeler.paya], ['kapsamaya gore', altKumeler.kapsamaya]]) {
  const o = oran(secilen);
  console.log(`${ad.padEnd(16)}${s(secilen.length, 6)}${s(`%${o.pay.toFixed(1)}`, 16)}${s(o.surumKusuru, 14)}${s(o.aygitKusuru, 14)}${s(o.hucreKusuru, 14)}`);
}
console.log(`matris ${aygitlar.length} aygit x ${surumler.length} surum = ${hucreler.length} hucre;`
  + ` bir surum eklenirse ${hucreler.length + aygitlar.length}, bir aygit eklenirse ${hucreler.length + surumler.length}`);
console.log('paya gore secilen:', altKumeler.paya.map((h) => `${h.aygit}/${h.surum}`).join(' '));
console.log('kapsamaya gore secilen:', altKumeler.kapsamaya.map((h) => `${h.aygit}/${h.surum}`).join(' '));
```

```
secim            hucre  kullanici payi  surum kusuru  aygit kusuru  hucre kusuru
paya gore            6           %46.9           2/5           4/6          6/30
kapsamaya gore       6           %24.3           5/5           6/6          6/30
matris 6 aygit x 5 surum = 30 hucre; bir surum eklenirse 36, bir aygit eklenirse 35
paya gore secilen: orta/S-10 kucuk/S-10 orta/S-9 buyuk/S-10 kucuk/S-9 eski/S-10
kapsamaya gore secilen: orta/S-10 kucuk/S-9 buyuk/S-8 eski/S-7 tablet/S-6 katlanir/S-10
```

Sayılar takası açık ediyor. Paya göre seçim kullanıcıların yüzde kırk altı virgül dokuzunu
temsil ediyor, ama beş sürümün yalnız ikisine ve altı aygıt sınıfının dördüne dokunuyor;
seçtiği altı hücrenin üçü aynı sürümde. Kapsamaya göre seçim temsil ettiği payı yarıya
indiriyor, buna karşılık her sürüme ve her aygıt sınıfına bir kez giriyor. Hücreye özgü
kusurda ikisi eşit ve ikisi de zayıftır: otuz hücrenin altısı, yani beşte biri.

Matrisin büyümesi bu kararı her yıl yeniden açar. Yeni bir sürüm çıktığında matris otuzdan
otuz altıya, yeni bir aygıt sınıfı girdiğinde otuz beşe gider; alt küme altı hücrede
kalırsa hücre kapsaması beşte birden altıda bire düşer. Koşum bütçesi doğrusal artarken
matris çarpımsal büyür, ve bu yüzden mobil koşumun sorusu hiçbir zaman "hepsini koşalım"
olamaz.

## Emülatörün Taşıdığı Boyut

Kapsama tablosu bir varsayım taşıyor: seçilen hücrenin gerçekten o aygıt sınıfında koştuğu.
**Emülatör** (emulator) bu varsayımı yalnız yarısında karşılar. Emülatör işletim sistemi
sürümünü gerçekten çalıştırır — sürüme bağlı kusurları taşır. Aygıt sınıfına bağlı olan
şeyi ise taşımaz: dokunma yüzeyinin gerçek boyutu, bellek sınırı, ağ kesintisi, kamera ve
sensör, üreticinin arayüz katmanı.

Bunun aritmetiği doğrudan tabloya işler. Kapsamaya göre seçilen altı hücrenin tamamı
emülatörde koşulursa aygıt sınıfına bağlı kusur yakalaması altıda altıdan sıfıra iner;
sürüme bağlı yakalama beşte beş kalır. İki hücre bir **aygıt havuzunda** (device farm)
gerçek donanıma ayrılırsa aygıt yakalaması altıda ikiye çıkar. Karar bu yüzden "emülatör mü
gerçek aygıt mı" değildir; hangi kusur sınıfının hangi hücrede aranacağıdır.

## Yakaladığı Kusur: Sürüme Bağlı Ayrıştırma

Ödünç akışının sonunda üyeye bir iade tarihi gösterilir. Tarih metnini üreten taraf
uygulamadır, okuyan taraf ise platformun tarih ayrıştırıcısıdır ve bu ayrıştırıcının
davranışı sürüme göre değişir: eski sürümler yalnız sıralı biçimi kabul eder.

```js
// platform.mjs — tarih ayristirmanin surume gore degisen davranisi
const AYRISTIRICI_GENIS = new Set(['S-9', 'S-10']);

export function tarihAyristir(surum, metin) {
  if (/^\d{4}-\d{2}-\d{2}$/.test(metin)) return metin;
  if (AYRISTIRICI_GENIS.has(surum) && /^\d{2}\.\d{2}\.\d{4}$/.test(metin)) {
    const [gun, ay, yil] = metin.split('.');
    return `${yil}-${ay}-${gun}`;
  }
  return null;
}
```

```js
// iade.mjs — surum 1: iade tarihi yerel bicimde uretiliyor
export function iadeTarihi(oduncTarihi, gun = 14) {
  const t = new Date(`${oduncTarihi}T00:00:00Z`);
  t.setUTCDate(t.getUTCDate() + gun);
  const iki = (n) => String(n).padStart(2, '0');
  return `${iki(t.getUTCDate())}.${iki(t.getUTCMonth() + 1)}.${t.getUTCFullYear()}`;
}
```

Aynı akış iki alt kümede de koşturulur; her hücre için üretilen tarihin o sürümde okunup
okunmadığı sınanır.

```js
// aygit.test.mjs — iade tarihi akisi secilen alt kumelerde kosar
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { altKumeler } from './matris.mjs';
import { tarihAyristir } from './platform.mjs';
import { iadeTarihi } from './iade.mjs';

const kos = (secilen) => {
  for (const hucre of secilen) {
    const metin = iadeTarihi('2026-03-04');
    assert.equal(tarihAyristir(hucre.surum, metin), '2026-03-18',
      `${hucre.aygit}/${hucre.surum} iade tarihini okuyamadi: ${metin}`);
  }
};

test('paya gore secilen alt kumede iade tarihi okunur', () => kos(altKumeler.paya));

test('kapsamaya gore secilen alt kumede iade tarihi okunur', () => kos(altKumeler.kapsamaya));
```

```bash
node --test --test-reporter=tap aygit.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
```

```
ok 1 - paya gore secilen alt kumede iade tarihi okunur
not ok 2 - kapsamaya gore secilen alt kumede iade tarihi okunur
# tests 2
# pass 1
# fail 1
```

Kusur birdir, alt küme ikidir ve sonuç ikiye ayrılır. Kapsamaya göre seçilen alt küme
S-6, S-7 ve S-8 hücrelerini taşıdığı için kırmızıya döndü. Paya göre seçilen alt küme
**aynı kusurla yeşil kaldı**: içinde yalnız S-9 ve S-10 var, ikisi de yerel biçimi kabul
ediyor. Kaçan sınıf burada bir test biçiminin değil, bir **seçimin** kaçırdığı sınıftır ve
kaçırılan kullanıcılar da sayılabilir: S-6 ile S-8 arası üç sürüm, sürüm ekseninin yüzde
otuzudur.

Düzeltme, tarih metnini platformdan bağımsız biçimde üretmektir.

```js
// iade.mjs — surum 2: iade tarihi platformdan bagimsiz bicimde uretiliyor
export function iadeTarihi(oduncTarihi, gun = 14) {
  const t = new Date(`${oduncTarihi}T00:00:00Z`);
  t.setUTCDate(t.getUTCDate() + gun);
  return t.toISOString().slice(0, 10);
}
```

```bash
node --test --test-reporter=tap aygit.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
```

```
ok 1 - paya gore secilen alt kumede iade tarihi okunur
ok 2 - kapsamaya gore secilen alt kumede iade tarihi okunur
# tests 2
# pass 2
# fail 0
```

İki alt kümenin de kaçırdığı bir sınıf da vardır: yalnız tek bir hücrede görülen kusur —
belirli bir aygıt sınıfının belirli bir sürümdeki arayüz katmanı. Tabloda karşılığı otuzda
altıdır; hücreye özgü kusurların beşte dördü her iki seçimde de kapsam dışındadır ve bu
oran alt küme büyütülmeden düzelmez.

Bedelin koşuma bağlı yanı bu koşumda küçüktür — iki test 31,1 milisaniye sürdü, çünkü
burada koşan şey model. Gerçek koşumda bedeli belirleyen sayı tablodadır: on iki hücre
koşumu, yani iki alt küme için altışar aygıt oturumu. Her oturum bir kurulum, bir uygulama
yüklemesi ve bir yıkım demektir; alt küme otuz hücrenin tamamına çıkarıldığında bu sayı beş
katına çıkar, süre de aynı oranda artar.

## Özet

- Aygıt ve sürüm eksenleri çarpılır: altı aygıt ve beş sürüm otuz hücre eder, yeni bir
  sürüm altı, yeni bir aygıt sınıfı beş hücre ekler.
- Paya göre seçilen altı hücre kullanıcıların yüzde kırk altı virgül dokuzunu temsil etti
  ama beş sürümün ikisine, altı aygıt sınıfının dördüne dokundu.
- Kapsamaya göre seçilen altı hücre payı yüzde yirmi dört virgül üçe indirdi, buna karşılık
  her sürüme ve her aygıt sınıfına girdi.
- Sürüme bağlı tarih ayrıştırma kusurunu yalnız kapsamaya göre seçilen alt küme yakaladı;
  aynı kusurla paya göre seçilen alt küme yeşil kaldı.
- Emülatör sürüm boyutunu taşır, aygıt sınıfı boyutunu taşımaz; altı hücrenin tamamı
  emülatörde koşulursa aygıt kusuru yakalaması altıda altıdan sıfıra iner.

## Sonraki Adım

Buraya kadar bütün iddialar bir değer üzerineydi: bir durum kodu, bir metin, bir tarih.
Arayüzün asıl çıktısı ise bir görüntüdür ve onun hakkında "eşit mi" diye sorulduğunda soru
birdenbire zorlaşır — iki ekran görüntüsü hiçbir zaman tam olarak aynı piksellerden
oluşmaz. Sonraki ders bu karşılaştırmayı bir eşiğe bağlar ve eşiği tarayarak ölçer: eşik
düştükçe kaç yanlış alarm üretiliyor, eşik yükseldikçe kaç gerçek gerileme kaçıyor.
