כשמשתמשים מגדירים מכשיר חדש או מתקינים מחדש את האפליקציה שלכם, מערכת Android יכולה לשחזר את נתוני האפליקציה מגיבוי בענן או מהעברה ממכשיר למכשיר. אם כללי הגיבוי לא כוללים נתונים חשובים, או אם האפליקציה לא יכולה להשתמש בנתונים שהיא משחזרת, המשתמשים מאבדים את ההגדרות וההתקדמות שלהם בלי לראות שגיאה.
בדיקות אוטומטיות של גיבוי ושחזור עוזרות לכם לזהות את הבעיות האלה לפני שאתם משיקים את האפליקציה. אלה בדיקות JUnit 5 שפועלות במחשב הפיתוח שלכם ושולטות במכשירים מחוברים דרך Android Debug Bridge (adb). כל בדיקה מזינה נתוני אפליקציה במכשיר, מגבה ומשחזרת את האפליקציה באמצעות מסגרת הגיבוי של Android, ואז בודקת שהנתונים תקינים. מגדירים את הבדיקות כחבילת מקרים לבדיקה של פלאגין של Android Gradle (AGP) בקובץ ה-build של המודול.
איך זה עובד
חבילת מקרים לבדיקה לגיבוי ושחזור כוללת שני חלקים שפועלים יחד:
- בדיקות מארח הן בדיקות JUnit 5 שפועלות במחשב הפיתוח. הם משתמשים ב
BackupRestoreControllerלכל מכשיר כדי להזין נתונים, לגבות ולשחזר את האפליקציה ולבדוק את התוצאות. - פעולות במכשיר הן מחלקות שמיישמות את
BackupDeviceAction. הן פועלות בתהליך של האפליקציה במכשיר, ולכן הן יכולות לקרוא ולכתוב נתונים באמצעות הקוד של האפליקציה. מארח בודק פעולות במכשיר באמצעות שם הכיתה.
כשמריצים את חבילת הבדיקות, AGP יוצר את האפליקציה וקובץ APK לבדיקה שמכיל את פעולות המכשיר. לפני כל בדיקה, מסגרת הבדיקה מתקינה את שני קובצי ה-APK בכל מכשיר שמשמש לבדיקה, ואז מוחקת את נתוני האפליקציה.
סוגי אחסון מובנים
בטבלה הבאה מפורטות מחלקות המשנה של StorageDomain שמאפשרות לכם להזין ולאמת נתונים בלי לכתוב פעולות במכשיר:
| דומיין האחסון | נתונים |
|---|---|
StorageDomain.Preference |
מפתח בקובץ SharedPreferences. |
StorageDomain.Database |
שורה בטבלת מסד נתונים של SQLite. |
StorageDomain.TextFile |
קובץ טקסט באחסון הפנימי של האפליקציה. |
StorageDomain.BinaryFile |
קובץ בינארי באחסון הפנימי של האפליקציה. |
כדי לבדוק נתונים אחרים, כמו קבצים של DataStore, כותבים פעולות במכשיר.
אמצעי תחבורה
כל גיבוי משתמש בערך BackupTransportMode. בטבלה הבאה מפורטים אמצעי התחבורה הנתמכים:
| מצב | תרחיש |
|---|---|
LOCAL |
גיבוי לטרנספורט הגיבוי המקומי של המכשיר. נתוני הגיבוי נשארים במכשיר. |
CLOUD_ENCRYPTED |
גיבוי בענן, כמו גיבוי שנשמר בחשבון Google של המשתמש ממכשיר עם הצפנה בצד הלקוח. |
CLOUD_UNENCRYPTED |
גיבוי בענן ממכשיר שלא מוגדרת בו הצפנה מצד הלקוח. |
DEVICE_TO_DEVICE |
העברה ממכשיר למכשיר, למשל באמצעות כבל או Wi-Fi. |
LOCAL פועל בכל מכשיר נתמך. מצבי הענן והמכשיר-למכשיר יוצרים את אותם גיבויים כמו הפעולה גיבוי נתוני האפליקציה ב-Android Studio: הגיבוי נשמר במכונת הפיתוח במקום להעלות או לשלוח אותו למכשיר אחר. כדי להשתמש במצבים "ענן" ו"ממכשיר למכשיר", צריך להתקין את Google Play Services במכשיר ולהשתמש בגרסה של האפליקציה שניתן לבצע בה ניפוי באגים. אם כללי הגיבוי שלכם תלויים בהצפנה בצד הלקוח, צריך לבדוק את שני מצבי הענן.
דרישות
כדי להשתמש בגיבוי אוטומטי ובבדיקת שחזור, צריך לוודא שהסביבה שלכם עומדת בדרישות הבאות:
- Gradle מגרסה 9.6.0 ואילך.
- JDK מגרסה 17 ואילך.
- מכשיר או אמולטור עם Android מגרסה 12 (רמת API 31) ומעלה שמחובר דרך adb. כדי לבדוק העברות בין שני מכשירים, צריך לחבר שני מכשירים.
תאימות
בטבלה הבאה מפורטות הגרסאות המינימליות והמומלצות לבדיקת גיבוי ושחזור אוטומטיים:
| רכיב | גרסת המינימום | הגרסה המומלצת |
|---|---|---|
| Android Studio | ארנב 2 קנרית 1 | Rabbit 2 Canary 4 ואילך |
| פלאגין של Android Gradle (AGP) | 9.5.0-alpha06 | 9.5.0-alpha09 ואילך |
androidx.test.backup ספריות |
1.0.0-alpha01 | 1.0.0-alpha02 ואילך |
כל גרסת טרום-השקה של AGP דורשת את גרסת טרום-ההשקה התואמת של Android Studio. מידע נוסף זמין במאמר תצוגות מקדימות של AGP.
הגדרת הפרויקט
כדי להוסיף חבילת מקרים לבדיקה של גיבוי ושחזור למודול האפליקציה:
הפעלת דגלים ניסיוניים
בקובץ gradle.properties של הפרויקט, מפעילים את חבילות מקרים לבדיקה ואת בדיקות הגיבוי:
android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true
הצהרה על חבילת המקרים לבדיקה
בקובץ build.gradle.kts של מודול האפליקציה, מצהירים על חבילת מקרים לבדיקה לגיבוי בבלוק testOptions:
android {
testOptions {
backupTests {
create("backupTest")
}
}
}
שם החבילה, backupTest בדוגמה הזו, קובע את ספריות קבוצת המקורות ואת שמות המשימות של Gradle. אפשר להתאים אישית את חבילת הבדיקות באמצעות המאפיינים האופציונליים הבאים:
- משתמשים ב-
backupTestLibraryVersionכדי להגדיר את הגרסה של ספריותandroidx.test.backupש-AGP מוסיף לחבילה, כמו"1.0.0-alpha02". המאפיין הזה הוא אופציונלי. אם לא תציינו את הגרסה, AGP יחזור לגרסת ברירת המחדל של הספרייה בהתאם לגרסת AGP. - משתמשים ב-
targetVariantsכדי לציין את וריאנט ה-build שחבילת הבדיקות בודקת, למשלtargetVariants.add("demoDebug"). המאפיין הזה הוא אופציונלי. אם לא תציינו את האפשרות הזו, חבילת הבדיקות תבדוק את הווריאציות שלtestBuildTypeשל המודול, שהיאdebugכברירת מחדל. מומלץ לבדוק גרסה שניתן לנפות בה באגים, כי מצבי ההעברה בענן וממכשיר למכשיר דורשים אפליקציה שניתן לנפות בה באגים.
AGP מוסיף את ספריית המארח, androidx.test.backup:backup-host, ואת JUnit 5 לבדיקות המארח. הוא מוסיף את ספריית המכשירים, androidx.test.backup:backup, לקובץ ה-APK לבדיקה ומגדיר את כלי ההרצה של המכשור של קובץ ה-APK לבדיקה.
כדי להוסיף יחסי תלות אחרים, משתמשים בבלוקים hostJar ו-testApk של החבילה.
לדוגמה, כדי להשתמש ב-Truth בבדיקות המארח, מוסיפים אותו לבלוק hostJar:
create("backupTest") {
hostJar {
dependencies {
implementation("com.google.truth:truth:1.4.2")
}
}
}
יצירת קבוצות המקור
יוצרים ספריות של קבוצות מקור שתואמות לשם החבילה:
- ממקמים את בדיקות המארח ב-
src/{suiteName}/test/kotlin/. - הפעולות במכשיר צריכות להיות בתוך
src/{suiteName}/androidTest/kotlin/.
לדוגמה, חבילה בשם backupTest משתמשת במבנה הספרייה הבא:
app/src/backupTest/
├── androidTest/
│ └── kotlin/com/example/myapp/ThemeActions.kt
└── test/
└── kotlin/com/example/myapp/SettingsBackupTest.kt
בדיקת הגדרות הגיבוי
הבדיקות מגבות ומשחזרות את הנתונים שכלולים בהגדרות הגיבוי של האפליקציה. מוודאים שהגיבוי מופעל באפליקציה ושהנתונים שאתם בודקים נכללים בכללי הגיבוי.
כתיבת בדיקות גיבוי ושחזור
כותבים בדיקות מארח בחבילת test המקורות. כל מחלקת בדיקה צריכה שתי הערות:
-
@ExtendWith(BackupRestoreExtension::class)מספק מכשירים מחוברים לבדיקות. -
@BackupRestoreConfig(applicationId = "...")מגדיר את מזהה האפליקציה של האפליקציה שנבדקת. משתמשים במזהה האפליקציה של הווריאציה שבודקים, כולל כלapplicationIdSuffix.
בכל שיטת בדיקה מצהירים על פרמטר BackupRestoreController לכל מכשיר שמשתמשים בו.
בדיקת סוגי האחסון המובנים
כדי לבדוק סוגי אחסון מובנים, קוראים ל-runBackupRestoreFlow עם הנתונים לאתחול, ספרייה לקובץ הגיבוי ומצב העברה. השיטה מפעילה את השלבים הבאים במכשיר:
- מזין נתונים לכל דומיין אחסון.
- האפליקציה תפסיק לפעול.
- מגבה את האפליקציה.
- מנקה את נתוני האפליקציה.
- משחזר את הגיבוי.
- מוודא שלכל דומיין אחסון יש את ערך הבסיס.
אם שלב מסוים נכשל, השיטה דוחה חריג והבדיקה נכשלת. בדוגמה הבאה נבדקים מפתח SharedPreferences, שורה ב-SQLite וקובץ פנימי באמצעות תעבורת הגיבוי המקומית:
package com.example.myapp
import androidx.test.backup.host.BackupRestoreConfig
import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.StorageDomain
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir
@ExtendWith(BackupRestoreExtension::class)
@BackupRestoreConfig(applicationId = "com.example.myapp")
class SettingsBackupTest {
@Test
fun settingsSurviveBackupAndRestore(
device: BackupRestoreController,
@TempDir outputDir: Path,
): Unit = runBlocking {
device.runBackupRestoreFlow(
storages = listOf(
StorageDomain.Preference(
prefName = "settings",
key = "theme",
value = "dark",
),
StorageDomain.Database(
dbName = "notes.db",
table = "notes",
primaryKeyCol = "id",
primaryKeyVal = 1,
columnValues = mapOf("title" to "Groceries"),
),
StorageDomain.TextFile(
path = "drafts/note.txt",
content = "Buy milk",
),
),
outputDir = outputDir,
mode = BackupTransportMode.LOCAL,
)
}
}
כשמגדירים דומיינים לאחסון, חשוב לזכור את הדברים הבאים:
Preferenceמקבל את השם של קובץSharedPreferencesבלי הסיומת.xml.-
TextFileו-BinaryFileפותרים נתיבים יחסיים בהתאם ל-filesDirשל האפליקציה. -
Databaseכותב את השורה באמצעות SQL, ויוצר את הטבלה עםTEXTעמודות אם הטבלה לא קיימת. אם האפליקציה שלכם משתמשת ב-Room, מומלץ לבדוק את תוכן מסד הנתונים באמצעות פעולות במכשיר שמשתמשות ב-DAO שלכם, כדי לוודא שלמסד הנתונים יש את הסכימה ש-Room מצפה לה.
פעולות כתיבה במכשיר
כדי להזין או לאמת נתונים שלא נכללים בסוגי האחסון המובנים, כותבים פעולות במכשיר בערכת המקורות androidTest של החבילה. פעולה במכשיר מטמיעה את BackupDeviceAction ומבטלת שני איברים:
- מגדירים את הערך
phaseל-BackupDeviceAction.PHASE_POPULATEעבור פעולה שיוצרת נתונים, או ל-BackupDeviceAction.PHASE_VERIFYעבור פעולה שבודקת נתונים. - מטמיעים את
executeכדי להריץ את הפעולה עםContextשל האפליקציה והארגומנטים מבדיקת המארח. מחזירים תוצאה שנוצרה באמצעות השיטהBackupDeviceActionResult.successאוBackupDeviceActionResult.failure, ומעבירים הודעת שגיאה אלfailure.
השיטה execute פועלת בתהליך של האפליקציה בשרשור ברקע, כך שהיא יכולה לחסום עד לסיום פעולות הקריאה והכתיבה.
בדוגמה הבאה מוסיפים הגדרה של עיצוב ומאמתים אותה. ההגדרה מאוחסנת באפליקציה באמצעות Preferences DataStore. האפליקציה מגדירה את DataStore בקבוצת המקורות הראשית שלה:
// In app/src/main/kotlin/com/example/myapp/Settings.kt
package com.example.myapp
import android.content.Context
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
val Context.settingsDataStore by preferencesDataStore(name = "settings")
val THEME_KEY = stringPreferencesKey("theme")
הפעולות במכשיר מבוססות על אותן הגדרות:
// In app/src/backupTest/androidTest/kotlin/com/example/myapp/ThemeActions.kt
package com.example.myapp
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.test.backup.BackupDeviceAction
import androidx.test.backup.BackupDeviceActionArgs
import androidx.test.backup.BackupDeviceActionResult
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
class SeedThemeAction : BackupDeviceAction {
override val phase = BackupDeviceAction.PHASE_POPULATE
override fun execute(
context: Context,
args: BackupDeviceActionArgs,
): BackupDeviceActionResult {
val theme = args["theme"]
?: return BackupDeviceActionResult.failure("Missing theme argument")
runBlocking {
context.settingsDataStore.edit { it[THEME_KEY] = theme }
}
return BackupDeviceActionResult.success()
}
}
class VerifyThemeAction : BackupDeviceAction {
override val phase = BackupDeviceAction.PHASE_VERIFY
override fun execute(
context: Context,
args: BackupDeviceActionArgs,
): BackupDeviceActionResult {
val expected = args["theme"]
val actual = runBlocking {
context.settingsDataStore.data.first()[THEME_KEY]
}
return if (actual == expected) {
BackupDeviceActionResult.success()
} else {
BackupDeviceActionResult.failure("Expected $expected, found $actual")
}
}
}
כדי להחזיר נתונים לבדיקה המארחת, מעבירים מפה ל-method success. בדיקת המארח קוראת את המפה מ-BackupActionResult.Success.data.
הרצת כל שלב בתהליך
כדי להשתמש בפעולות במכשיר או להתאים אישית את התהליך, צריך להפעיל כל שלב בעצמכם. בדיקת המארח הבאה מריצה את פעולות המכשיר מהקטע הקודם סביב גיבוי ושחזור בענן:
package com.example.myapp
import androidx.test.backup.host.BackupActionResult
import androidx.test.backup.host.BackupRestoreConfig
import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import org.junit.jupiter.api.io.TempDir
const val SEED_THEME = "com.example.myapp.SeedThemeAction"
const val VERIFY_THEME = "com.example.myapp.VerifyThemeAction"
@ExtendWith(BackupRestoreExtension::class)
@BackupRestoreConfig(applicationId = "com.example.myapp")
class ThemeBackupTest {
@Test
fun themeSurvivesCloudBackup(
device: BackupRestoreController,
@TempDir outputDir: Path,
): Unit = runBlocking {
val args = mapOf("theme" to "dark")
device.runOnDevice(SEED_THEME, args).assertSuccess()
device.stopApp()
val backupFile = device.performBackup(
BackupTransportMode.CLOUD_ENCRYPTED,
outputDir,
)
device.clearAppData()
device.performRestore(backupFile)
device.runOnDevice(VERIFY_THEME, args).assertSuccess()
}
}
fun BackupActionResult.assertSuccess() {
if (this is BackupActionResult.Failure) fail(errorMessage)
}
אם פעולה במכשיר נכשלת, runOnDevice מחזירה את הערך BackupActionResult.Failure במקום להחזיר חריגה, לכן צריך לבדוק את התוצאה. אם השלבים האחרים נכשלים, מוצגת חריגה.
בדיקת העברות בין מכשירים
כדי לבדוק העברה בין שני מכשירים, צריך להצהיר על פרמטר BackupRestoreController לכל מכשיר. מגבים את האפליקציה במכשיר המקור, ואז משחזרים את קובץ הגיבוי במכשיר היעד. בדוגמה הבאה נעשה שימוש חוזר בקבועים ובפונקציית העזר assertSuccess מהדוגמה הקודמת:
package com.example.myapp
import androidx.test.backup.host.BackupRestoreConfig
import androidx.test.backup.host.BackupRestoreController
import androidx.test.backup.host.BackupRestoreExtension
import androidx.test.backup.host.BackupTransportMode
import androidx.test.backup.host.Device
import java.nio.file.Path
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir
@ExtendWith(BackupRestoreExtension::class)
@BackupRestoreConfig(applicationId = "com.example.myapp")
class ThemeTransferTest {
@Test
fun themeTransfersToNewDevice(
@Device(role = "source") source: BackupRestoreController,
@Device(role = "target") target: BackupRestoreController,
@TempDir outputDir: Path,
): Unit = runBlocking {
val args = mapOf("theme" to "dark")
source.runOnDevice(SEED_THEME, args).assertSuccess()
source.stopApp()
val backupFile = source.performBackup(
BackupTransportMode.DEVICE_TO_DEVICE,
outputDir,
)
target.clearAppData()
target.performRestore(backupFile)
target.runOnDevice(VERIFY_THEME, args).assertSuccess()
}
}
הערך role של @Device הוא תווית שמאפשרת לקרוא את הבדיקה בקלות רבה יותר. הוא לא משפיע על המכשיר שאליו מועבר הפרמטר. אם רק מכשיר אחד מחובר, שני הפרמטרים משתמשים במכשיר הזה, והבדיקה מדמה את ההעברה במכשיר אחד. ניקוי הנתונים של היעד מבטיח שהנתונים המשוחזרים יגיעו מהגיבוי.
בחירת מכשירים
כברירת מחדל, מסגרת הבדיקה ממיינת את המכשירים אונליין לפי המספר הסידורי שלהם ומקצה אותם לפרמטרים של BackupRestoreController לפי המיקום. לדוגמה, הפרמטר הראשון של שיטת הבדיקה מקבל את המכשיר הראשון, והפרמטר השני מקבל את המכשיר השני. אם אין מכשיר במיקום מסוים, הפרמטר מקבל את המכשיר הראשון. המיקומים סופרים את כל הפרמטרים של השיטה,
לכן צריך להצהיר על פרמטרים של מכשירים לפני פרמטרים אחרים, כמו @TempDir. משתנה הסביבה ANDROID_SERIAL לא משפיע על הקצאת המכשירים.
כדי לבחור מכשירים, מגדירים את הערכים serial או api של @Device, או מגדירים את מאפייני המערכת של JVM. מאפייני המערכת מקבלים עדיפות על פני ערכי @Device. בטבלה הבאה מפורטים מאפייני המערכת הנתמכים:
| מאפיין מערכת | תיאור |
|---|---|
androidx.test.backup.device.serial |
המספר הסידורי של המכשיר לכל הפרמטרים של המכשיר. |
androidx.test.backup.device.serials |
רשימה מופרדת בפסיקים של מספרים סידוריים, שמוקצים לפרמטרים לפי מיקום. |
androidx.test.backup.device.serial.{name} |
המספר הסידורי של פרמטרים שמוערים באמצעות @Device(serial = "{name}"). |
androidx.test.backup.device.api |
רמת ה-API של המכשיר לכל הפרמטרים של המכשיר. |
androidx.test.backup.device.apis |
רשימה מופרדת בפסיקים של רמות API, שמוקצות לפרמטרים לפי מיקום. |
מעבירים את המאפיינים האלה באמצעות -D כשמריצים את הבדיקות משורת הפקודה.
המאפיינים חלים על כל בדיקה שמופעלת, לכן צריך להשתמש באפשרות --tests כדי להפעיל רק את הבדיקות שצריכות אותם. לדוגמה, הפקודה הבאה מריצה את ThemeTransferTest עם emulator-5554 כמכשיר המקור ו-emulator-5556 כמכשיר היעד:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeTransferTest" -Dandroidx.test.backup.device.serials=emulator-5554,emulator-5556שמירת נתוני האפליקציה בין בדיקות
לפני כל בדיקה, מסגרת הבדיקה מוחקת את נתוני האפליקציה בכל מכשיר שבו מתבצעת הבדיקה. כדי להשבית את האפשרות הזו עבור מחלקה או שיטה לבדיקה, מוסיפים לה הערה עם @Isolation(IsolationPolicy.MANUAL).
כתיבת בדיקות ב-Java
השיטות של BackupRestoreController הן פונקציות השעיה של Kotlin. אם אתם כותבים בדיקות מארח ב-Java, השתמשו בווריאנט Async של כל שיטה, כמו runBackupRestoreFlowAsync, שמחזירה ListenableFuture.
הפעלת בדיקות של גיבוי ושחזור
ה-AGP יוצר משימת Gradle לכל וריאנט שחבילת הבדיקות בודקת, בשם test{Variant}{SuiteName}DefaultTestSuite. כדי להריץ את חבילת backupTest בגרסה debug, מחברים את המכשירים ומריצים את המשימה:
./gradlew :app:testDebugBackupTestDefaultTestSuiteהמשימות check, test ו-connectedCheck לא מריצות חבילות של בדיקות גיבוי ושחזור, לכן צריך להריץ את המשימה של החבילה באופן מפורש.
כדי להריץ מחלקה או שיטה של בדיקה אחת, משתמשים באפשרות --tests:
./gradlew :app:testDebugBackupTestDefaultTestSuite --tests "com.example.myapp.ThemeBackupTest"בדיקת דוחות הבדיקה
אחרי שסיימתם את הרצת הבדיקה, AGP יוצר דוח בדיקה בפורמט HTML בנתיב {module}/build/reports/tests/{taskName}/index.html, למשל app/build/reports/tests/testDebugBackupTestDefaultTestSuite/index.html.
בעיות ידועות ומגבלות
יש בעיות ומגבלות ידועות בבדיקות אוטומטיות של גיבוי ושחזור:
- עדיין אין תמיכה בחבילות מקרים לבדיקה ב-Android Studio. יכול להיות שבעורך הקוד יופיעו שגיאות של הפניות לא פתורות בקובצי המקור של חבילת הבדיקות, גם אם הפרויקט מוגדר בצורה נכונה. במקום זאת, אפשר ליצור ולהריץ בדיקות גיבוי ושחזור משורת הפקודה באמצעות Gradle.
- הבדיקות פועלות רק במכשירים ובאמולטורים שמחוברים למחשב פיתוח באמצעות adb. אי אפשר להשתמש ב-Firebase Test Lab.
- הבדיקות מוחקות את נתוני האפליקציה ומשנות את הגדרות הגיבוי במכשירים שבהם הן מבוצעות. מומלץ להשתמש במכשירי בדיקה ייעודיים.
בנתוני הגרסה של בדיקת הגיבוי והשחזור מפורטים השינויים בכל גרסה.