Общие сведения

ВступлениеПодготовка к запускуАрхитектура платформы TestoПорядок запускаПолитика запуска тестов

Обучающие материалы по Testo для Hyper-V

Часть 1. Самый первый тестЧасть 2. Устанавливаем Ubuntu ServerЧасть 3. Доступ в Интернет из виртуальной машиныЧасть 4. Гостевые дополненияЧасть 5. ПараметрыЧасть 6. КешированиеЧасть 7. Связываем две машины по сетиЧасть 8. ФлешкиЧасть 9. МакросыЧасть 10. Конструкция ifЧасть 11. No snapshotsЧасть 12. Управление мышкойЧасть 13. Импортирование жёстких дисковЧасть 14. JS-селекторыЧасть 15. Циклы

Обучающие материалы по Testo для QEMU

Часть 1. Самый первый тестЧасть 2. Устанавливаем Ubuntu ServerЧасть 3. Гостевые дополненияЧасть 4. ПараметрыЧасть 5. КешированиеЧасть 6. Доступ в Интернет из виртуальной машиныЧасть 7. Связываем две машины по сетиЧасть 8. ФлешкиЧасть 9. МакросыЧасть 10. Конструкция ifЧасть 11. No snapshotsЧасть 12. Управление мышкойЧасть 13. Импортирование жёстких дисковЧасть 14. JS-селекторыЧасть 15. ЦиклыЧасть 16. Макросы с объявлениями

Спецификация языка

Общая структура скриптовых файловБазовые конструкции языкаOбъявление виртуальной машиныОбъявление виртуального флеш-накопителяОбъявление виртуальной сетиПараметрыОбъявление тестовМакросыДействия с виртуальными машинамиДействия с мышкойПоиск изображений на экранеДействия с виртуальными флеш-накопителямиУсловияЦиклыСписок идентификаторов клавиш

Запросы на языке Javascript

Общая концепция построения JS-селекторовВстроенные глобальные функции JavascriptИсключенияКласс TextTensorКласс ImgTensorКласс Point

Часть 11. No snapshots

С чем Вы познакомитесь

В этой части вы познакомитесь с тестами без снепшотов гипервизора в платформе Testo, которые могут позволить сэкономить вам очень много места на диске.

Начальные условия

  1. Платформа Testo установлена.
  2. Установлен менеджер виртуальных машин virt-manager.
  3. На хостовой машине имеется доступ в Интернет
  4. Имеется установочный образ Ubuntu server 16.04 с расположением /opt/iso/ubuntu_server.iso. Местоположение и название установочного файла может быть другим, в этом случае нужно будет соответствующим образом поправить параметр ISO_DIR, передаваемый через командную строку во время запуска тестов.
  5. Имеется образ с гостевыми дополнениями Testo в одной папке с установочным образом Ubuntu.
  6. (Рекомендовано) Настроена подсветка синтаксиса Testo-lang в Sublime Text 3.
  7. (Рекомендовано) Проделаны шаги из десятой части.

Вступление

Как, наверное, вы уже прочуствовали, в платформе Testo большое значение имеет кешируемость тестов, что позволяет не прогонять с нуля каждый раз все тесты, а отталкиваться от уже успешно пройденных (конечно, если успешно пройденные тесты имеют актуальный кеш). В первую очередь, такое поведение становится возможным благодаря возможности создавать снепшоты виртуальных машин и флешек, а затем возвращаться к сохраненным состояниям при необходимости прогнать производные тесты.

Но такое поведение рано или поздно может привести к проблемам: каждый снепшот (как машин, так и флешек) занимает немало места на диске, и это самое место может очень быстро закончиться. Ситуация усугубляется тем, что в конце успешного теста создаются снепшоты всех сущностей, участвующих в тесте. Если, например, тест включает в себя пять виртуальных машин и две виртуальных флешки, то в конце теста вы получите пять снепшотов виртуальных машин и два снепшота для флешек.

Поэтому в языке Testo-lang существует механизм, который позволяет не создавать снепшоты гипервизора для виртуальных сущностей, а ограничиваться фиксацией тестов в виде метаданных. Грамотное использование этого механизма позволит вам сэкономить очень много места на диске без особого ущерба, и именно с этим механизмом мы познакомимся в этом уроке.

С чего начать?

Давайте посмотрим на наше дерево тестов, которое у нас накопилось на текущий момент.

Всего у нас имеется 10 тестов, и в конце каждого теста сейчас создаются снепшоты, которые уже занимают довольно места на нашем диске. Конечно, мы хотели бы немного поправить эту ситуацию.

Давайте подумаем, а зачем вообще нужны снепшоты в конце каждого успешного теста? В первую очередь это необходимо для того, чтобы платформа Testo могла бы вернуться к сохранённым снепшотам в случае, если нужно прогнать тесты-потоки закешированного теста. Например, если бы в нашем дереве тестов тест test_ping потерял бы целостность, то нам нужно было бы восстановить виртуальные машины в состояния server_prepare (для машины server) и client_prepare (для машины client) для того, чтобы заново прогнать этот тест.

Но зачем нужны снепшоты в конце тестов test_ping и exchange_files_with_flash? Ведь эти тесты являются конечными в нашей иерархии, и возвращаться к состоянию виртуальных машин на момент их успешного окончания просто бесмыссленно. Поэтому давайте попробуем убрать в этих двух тестах снепшоты (пожалуйста, убедитесь что все тесты проведены успешно и закешированы прежде чем изменять тестовые сценарии):

[no_snapshots: true]
test test_ping: client_prepare, server_prepare {
    client exec bash "ping 192.168.1.2 -c5"
    server exec bash "ping 192.168.1.1 -c5"
}

[no_snapshots: true]
test exchange_files_with_flash: client_prepare, server_prepare {
    client exec bash "echo \"Hello from client!\" > /tmp/copy_me_to_server.txt"
    copy_file_with_flash("client", "server", "exchange_flash", "/tmp/copy_me_to_server.txt", "/tmp/copy_me_to_server.txt")
    server exec bash "cat /tmp/copy_me_to_server.txt"
}

Здесь мы воспользовались ещё одной новой для нас языковой конструкцией - атрибутами тестов. Всего существует два возможных атрибута тестов: no_snapshots и description. Атрибут description не такой интересный и позволяет лишь добавить текстоое описание теста, которое затем будет сохранено в отчете (если конечно проинструктировать Testo создать такой отчет с помощью аргумента командной строки --report_folder). Атрибут no_snapshots гораздо более значимый и нам необходимо задать ему значение true.

Попробуем запустить тесты:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes
UP-TO-DATE TESTS:
server_install_ubuntu
server_install_guest_additions
server_unplug_nat
server_prepare
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
client_prepare
TESTS TO RUN:
test_ping
exchange_files_with_flash
[ 80%] Preparing the environment for test test_ping
[ 80%] Restoring snapshot server_prepare for virtual machine server
[ 80%] Restoring snapshot client_prepare for flash drive exchange_flash
[ 80%] Restoring snapshot client_prepare for virtual machine client
[ 80%] Running test test_ping
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ ping 192.168.1.2 -c5
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.057 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.037 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.043 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=0.044 ms
64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=0.046 ms

--- 192.168.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3998ms
rtt min/avg/max/mdev = 0.037/0.045/0.057/0.008 ms
[ 80%] Executing bash command in virtual machine server with timeout 10m
+ ping 192.168.1.1 -c5
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.038 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.036 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.046 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.044 ms

--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3997ms
rtt min/avg/max/mdev = 0.036/0.041/0.046/0.003 ms
[ 80%] Taking snapshot test_ping for virtual machine client
[ 80%] Taking snapshot test_ping for virtual machine server
[ 80%] Taking snapshot test_ping for flash drive exchange_flash
[ 90%] Test test_ping PASSED in 0h:0m:10s
[ 90%] Preparing the environment for test exchange_files_with_flash
[ 90%] Restoring snapshot server_prepare for virtual machine server
[ 90%] Restoring snapshot client_prepare for flash drive exchange_flash
[ 90%] Restoring snapshot client_prepare for virtual machine client
[ 90%] Running test exchange_files_with_flash
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ echo 'Hello from client!'
[ 90%] Calling macro process_flash(flash_name="exchange_flash", command="cp /tmp/copy_me_to_server.txt /media") in virtual machine client
[ 90%] Plugging flash drive exchange_flash into virtual machine client
[ 90%] Sleeping in virtual machine client for 5s
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ mount /dev/sdb1 /media
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ cp /tmp/copy_me_to_server.txt /media
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ umount /media
[ 90%] Unplugging flash drive exchange_flash from virtual machine client
[ 90%] Calling macro process_flash(flash_name="exchange_flash", command="cp /media/copy_me_to_server.txt /tmp") in virtual machine server
[ 90%] Plugging flash drive exchange_flash into virtual machine server
[ 90%] Sleeping in virtual machine server for 5s
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ mount /dev/sdb1 /media
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ cp /media/copy_me_to_server.txt /tmp
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ umount /media
[ 90%] Unplugging flash drive exchange_flash from virtual machine server
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ cat /tmp/copy_me_to_server.txt
Hello from client!
[ 90%] Taking snapshot exchange_files_with_flash for virtual machine client
[ 90%] Taking snapshot exchange_files_with_flash for virtual machine server
[ 90%] Taking snapshot exchange_files_with_flash for flash drive exchange_flash
[100%] Test exchange_files_with_flash PASSED in 0h:0m:18s
PROCESSED TOTAL 10 TESTS IN 0h:0m:28s
UP-TO-DATE: 8
RUN SUCCESSFULLY: 2
FAILED: 0
user$

Мы видим, что оба наших изменённых теста потеряли целостность, и их пришлось прогонять заново. Это произошло потому, что атрибуты теста также входят в подсчёт целостности кеша.

Но что же теперь? Теперь в конце наших тестов не создаются снепшоты, и можно было бы подумать, что теперь эти тесты никогда не будут кешироваться, и они будут прогняться каждый раз, так? На самом деле, это совсем не так! Давайте запустим наши тесты ещё раз.

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes
UP-TO-DATE TESTS:
server_install_ubuntu
server_install_guest_additions
server_unplug_nat
server_prepare
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
client_prepare
test_ping
exchange_files_with_flash
PROCESSED TOTAL 10 TESTS IN 0h:0m:0s
UP-TO-DATE: 10
RUN SUCCESSFULLY: 0
FAILED: 0
user$

Что же мы видим? Все тесты закешированы, и ничего не пришлось прогонять. И при этом два последних теста имеют атрибут no_snapshots, то есть у виртуальных машин и флешек не создаются снепшоты гипервизора (в этом кстати можно легко убедиться, посмотрев свойства виртуальных машин):

No snapshots

Почему же так происходит? Давайте разбираться.

Дело в том, что в платформе Testo существуют два типа снепшотов, которые работают одновременно никак не мешают друг другу:

  1. Снепшоты на уровне метаданных. Это различные служебные данные в виде небольших текстовых файликов, которые создаёт Testo для своих целей, и которые участвуют, в том числе, в контроли целостности кеша тестов. Эти снепшоты создаются всегда, и вы не можете на них повлиять. Если вы приглядитесь на вывод последнего прогона тестов, вы можете увидеть, что в выводе всё-равно присутствют надписи Taking snapshot... - это как раз и отражает создание новых метаданных.
  2. Снепшоты гипервизора. Это и есть снепшоты в привычном нам понимании. Эти снепшоты создаются только в том случае, если у теста нет атрибута no_snapshots. Т.к. сейчас мы включили этот атрибут, то снепшоты гипервизора не были созданы.

Если кратко просуммировать вышесказанное, то получится очень важное заключение:

Атрибут no_snapshots не влияет на кешируемость тестов, и к таким тестам применяются точно такие же законы целостности кеша, как и к обычным тестам. Наличие такого атрибута не означает, что тест не будет кешироваться и будет всегда запускаться.

Получается, что мы сэкономили немного места на диске и при этом совершенно ничего не потеряли - ведь снепшоты тестов test_ping и exchange_files_with_flash нам и так никогда бы не пригодились. Отсюда следует ещё одно важное заключение:

Для "листовых" тестов (тестов, не имеющих потомков) можно и даже рекомендуется указывать атрибут no_snapshots, т.к. особой пользы от наличия снепшотов в таких тестах всё равно практически нет.

no_snapshots в промежуточных тестах

Может возникнуть ощущение, что если атрибут no_snapshots экономит место на диске и не влияет на кешируемость тестов, то, может быть, имеет смысл вообще указывать этот атрибут во всех тестах подряд? Такое ощущение будет не совсем верным.

Да, с одной стороны такой атрибут не влияет на кешируемость тестов, но это не значит, что его наличие не будет иметь никаких отрицательных эффектов. Давайте продемонстрируем это на примере и добавим этот атрибут в тест client_unplug_nat:

[no_snapshots: true]
test client_unplug_nat: client_install_guest_additions {
    client unplug_nic("${client_hostname}", "${client_login}", "nat", "1111")
}

Давайте прогоним именно этот тест и ничего больше:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes --test_spec client_unplug_nat
UP-TO-DATE TESTS:
client_install_ubuntu
client_install_guest_additions
TESTS TO RUN:
client_unplug_nat
[ 67%] Preparing the environment for test client_unplug_nat
[ 67%] Restoring snapshot client_install_guest_additions for virtual machine client
[ 67%] Running test client_unplug_nat
[ 67%] Calling macro unplug_nic(hostname="client", login="client-login", nic_name="nat", password="1111") in virtual machine client
[ 67%] Shutting down virtual machine client with timeout 1m
[ 67%] Unplugging nic nat from virtual machine client
[ 67%] Starting virtual machine client
[ 67%] Waiting "client login:" for 2m with interval 1s in virtual machine client
[ 67%] Typing "client-login" with interval 30ms in virtual machine client
[ 67%] Pressing key ENTER in virtual machine client
[ 67%] Waiting "Password:" for 1m with interval 1s in virtual machine client
[ 67%] Typing "1111" with interval 30ms in virtual machine client
[ 67%] Pressing key ENTER in virtual machine client
[ 67%] Waiting "Welcome to Ubuntu" for 1m with interval 1s in virtual machine client
[ 67%] Taking snapshot client_unplug_nat for virtual machine client
[100%] Test client_unplug_nat PASSED in 0h:0m:25s
PROCESSED TOTAL 3 TESTS IN 0h:0m:25s
UP-TO-DATE: 2
RUN SUCCESSFULLY: 1
FAILED: 0
user$

Убедимся также, что этот тест закеширован, несмотря на no_snapshots: true:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes --test_spec client_unplug_nat
UP-TO-DATE TESTS:
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
PROCESSED TOTAL 3 TESTS IN 0h:0m:0s
UP-TO-DATE: 3
RUN SUCCESSFULLY: 0
FAILED: 0
user$

А теперь попытаемся прогнать тест client_prepare, который зависит от client_unplug_nat:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes --test_spec client_prepare
UP-TO-DATE TESTS:
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
TESTS TO RUN:
client_unplug_nat
client_prepare
[ 60%] Preparing the environment for test client_unplug_nat
[ 60%] Restoring snapshot client_install_guest_additions for virtual machine client
[ 60%] Running test client_unplug_nat
[ 60%] Calling macro unplug_nic(hostname="client", login="client-login", nic_name="nat", password="1111") in virtual machine client
[ 60%] Shutting down virtual machine client with timeout 1m
[ 60%] Unplugging nic nat from virtual machine client
[ 60%] Starting virtual machine client
[ 60%] Waiting "client login:" for 2m with interval 1s in virtual machine client
[ 60%] Typing "client-login" with interval 30ms in virtual machine client
[ 60%] Pressing key ENTER in virtual machine client
[ 60%] Waiting "Password:" for 1m with interval 1s in virtual machine client
[ 60%] Typing "1111" with interval 30ms in virtual machine client
[ 60%] Pressing key ENTER in virtual machine client
[ 60%] Waiting "Welcome to Ubuntu" for 1m with interval 1s in virtual machine client
[ 80%] Test client_unplug_nat PASSED in 0h:0m:25s
[ 80%] Preparing the environment for test client_prepare
[ 80%] Restoring snapshot initial for flash drive exchange_flash
[ 80%] Running test client_prepare
[ 80%] Calling macro process_flash(flash_name="exchange_flash", command="cp /media/rename_net.sh /opt/rename_net.sh") in virtual machine client
[ 80%] Plugging flash drive exchange_flash into virtual machine client
[ 80%] Sleeping in virtual machine client for 5s
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ mount /dev/sdb1 /media
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ cp /media/rename_net.sh /opt/rename_net.sh
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ umount /media
[ 80%] Unplugging flash drive exchange_flash from virtual machine client
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ chmod +x /opt/rename_net.sh
+ /opt/rename_net.sh 52:54:00:00:00:aa server_side
Renaming success
+ ip a a 192.168.1.2/24 dev server_side
+ ip l s server_side up
+ ip ad
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: server_side: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:00:00:aa brd ff:ff:ff:ff:ff:ff
inet 192.168.1.2/24 scope global server_side
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe00:aa/64 scope link tentative
valid_lft forever preferred_lft forever
[ 80%] Taking snapshot client_prepare for virtual machine client
[ 80%] Taking snapshot client_prepare for flash drive exchange_flash
[100%] Test client_prepare PASSED in 0h:0m:8s
PROCESSED TOTAL 4 TESTS IN 0h:0m:34s
UP-TO-DATE: 3
RUN SUCCESSFULLY: 1
FAILED: 0
user$

И мы с вами увидим очень интересную картину: тест client_unplug_nat одновременно помечен и как UP-TO-DATE и как TEST TO RUN. Давайте разберёмся, почему так происходит.

Когда Testo сканирует дерево тестов чтобы определить, какие тесты необходимо выполнить, каждый тест анализируется отдельно. Т.к. мы хотим запустить тест client_prepare, то предварительно анализируются на предмет закешированности все его предки: client_install_ubuntu, client_install_guest_additions и client_unplug_nat. Все эти тесты имеют валидный кеш, поэтому они и помечаются как UP-TO-DATE, что мы и видим.

Затем приходит черед проанализировать закешированность самого теста client_prepare. Кеш этого теста недействителен (потому что мы ранее меняли в предыдущем шаге client_unplug_nat), а значит его необходимо выполнить. Остаётся вопрос - а как его выполнить?

Если бы тест client_unplug_nat не был бы помечен, как no_snapshots, то мы могли бы откатиться к снепшотам виртуальных машин на момент окончания client_unplug_nat. Но у этого теста нет снепшотов гипервизора, поэтому и откатиться нам некуда. Возникает вопрос - а как вернуть виртуальную машину client в состояние на момент окончания client_unplug_nat? В этом случае платформа Testo начинает идти вверх по иерархии тестов в попытке найти хоть один снепшот со снепшотами гипервизора, за который можно было бы "зацепиться". В нашем случае - это тест client_install_guest_additions.

В итоге мы откатываемся к снепшоту client_install_guest_additions и начинаем заново прогонять тест client_unplug_nat чтобы вернуть виртуальную машину client в нужное состояние (в состояние на момент окончания теста client_unplug_nat). Именно поэтому мы видим client_unplug_nat в списке TESTS TO RUN.

После того, как мы вернули машину в нужное состояние, мы можем, наконец, запустить сам тест client_prepare. Этот процесс можно визуализировать примерно так:

Если бы тест client_install_guest_additions был бы тоже помечен как no_snapshots, то итоговый план выполнения тестов выглядел бы так: client_install_guest_additions->client_unplug_nat->client_prepare

После этого давайте попробуем запустить все тесты целиком:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --assume_yes
UP-TO-DATE TESTS:
server_install_ubuntu
server_install_guest_additions
server_unplug_nat
server_prepare
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
client_prepare
TESTS TO RUN:
test_ping
exchange_files_with_flash
[ 80%] Preparing the environment for test test_ping
[ 80%] Restoring snapshot server_prepare for virtual machine server
[ 80%] Restoring snapshot client_prepare for flash drive exchange_flash
[ 80%] Restoring snapshot client_prepare for virtual machine client
[ 80%] Running test test_ping
[ 80%] Executing bash command in virtual machine client with timeout 10m
+ ping 192.168.1.2 -c5
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.057 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.037 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.043 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=0.044 ms
64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=0.046 ms

--- 192.168.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3998ms
rtt min/avg/max/mdev = 0.037/0.045/0.057/0.008 ms
[ 80%] Executing bash command in virtual machine server with timeout 10m
+ ping 192.168.1.1 -c5
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.038 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.036 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.046 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.044 ms

--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3997ms
rtt min/avg/max/mdev = 0.036/0.041/0.046/0.003 ms
[ 80%] Taking snapshot test_ping for virtual machine client
[ 80%] Taking snapshot test_ping for virtual machine server
[ 80%] Taking snapshot test_ping for flash drive exchange_flash
[ 90%] Test test_ping PASSED in 0h:0m:10s
[ 90%] Preparing the environment for test exchange_files_with_flash
[ 90%] Restoring snapshot server_prepare for virtual machine server
[ 90%] Restoring snapshot client_prepare for flash drive exchange_flash
[ 90%] Restoring snapshot client_prepare for virtual machine client
[ 90%] Running test exchange_files_with_flash
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ echo 'Hello from client!'
[ 90%] Calling macro process_flash(flash_name="exchange_flash", command="cp /tmp/copy_me_to_server.txt /media") in virtual machine client
[ 90%] Plugging flash drive exchange_flash into virtual machine client
[ 90%] Sleeping in virtual machine client for 5s
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ mount /dev/sdb1 /media
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ cp /tmp/copy_me_to_server.txt /media
[ 90%] Executing bash command in virtual machine client with timeout 10m
+ umount /media
[ 90%] Unplugging flash drive exchange_flash from virtual machine client
[ 90%] Calling macro process_flash(flash_name="exchange_flash", command="cp /media/copy_me_to_server.txt /tmp") in virtual machine server
[ 90%] Plugging flash drive exchange_flash into virtual machine server
[ 90%] Sleeping in virtual machine server for 5s
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ mount /dev/sdb1 /media
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ cp /media/copy_me_to_server.txt /tmp
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ umount /media
[ 90%] Unplugging flash drive exchange_flash from virtual machine server
[ 90%] Executing bash command in virtual machine server with timeout 10m
+ cat /tmp/copy_me_to_server.txt
Hello from client!
[ 90%] Taking snapshot exchange_files_with_flash for virtual machine client
[ 90%] Taking snapshot exchange_files_with_flash for virtual machine server
[ 90%] Taking snapshot exchange_files_with_flash for flash drive exchange_flash
[100%] Test exchange_files_with_flash PASSED in 0h:0m:18s
PROCESSED TOTAL 10 TESTS IN 0h:0m:28s
UP-TO-DATE: 8
RUN SUCCESSFULLY: 2
FAILED: 0
user$

Что же мы видим? Мы видим, что несмотря на то, что наш тест client_unplug_nat получил атрибут no_snapshots, наше время прогона ЛИСТОВЫХ тестов не пострадало: ведь мы всё еще можем использовать снепшоты от теста client_prepare.

Получается, что применение атрибута no_snapshots в промежуточных тестах может сэкономить место на диске, но в некоторых случаях в ущерб времени прогона тестов.

Попробуйте самостоятельно добавить атрибут no_snapshots в тест server_unplug_nat и внимательно поизучайте какие тесты в каких случаях будут запускаться.

Теперь давайте рассмотрим ещё один момент, после которого мы сформулируем несколько правил относительно того, где стоит применять no_snapshots, а где не стоит.

no_snapshots в опорных тестах - плохая идея

Перед дальнейшим шагом убедитесь, что тесты client_unplug_nic, server_unplug_nic, test_ping и exchange_files_with_flash имеют атрибут no_snapshots, все тесты должны быть прогнаны и закешированы.

При таком раскладе получается, что мы сэкономили довольно много места, и при условии, что мы не будем трогать тесты client_prepare и server_prepare тесты test_ping и exchange_files_with_flash прогоняются так же быстро, как если бы все тесты имели полноценные снепшоты. Мы достигли определённого баланса - места гораздо меньше, и неудобства от увеличенного времени прогона тестов пока не сильно ощущаются.

Но давайте продемонстрируем, что иногда очень важно бывает вовремя остановиться в попытках сэкономить используемое место.

Давайте добавим no_snapshots в тесты client_prepare и server_prepare и попробуем прогнать все тесты:

user$ sudo testo run ./ --stop_on_fail --param ISO_DIR /opt/iso --test_spec test_ping --assume_yes
UP-TO-DATE TESTS:
server_install_ubuntu
server_install_guest_additions
server_unplug_nat
client_install_ubuntu
client_install_guest_additions
client_unplug_nat
TESTS TO RUN:
server_unplug_nat
server_prepare
client_unplug_nat
client_prepare
test_ping
client_unplug_nat
client_prepare
server_unplug_nat
server_prepare
exchange_files_with_flash
...
user$

Посмотрите насколько выросла очередь TESTS TO RUN. Мы видим, что тесты server_unplug_nat, client_unplug_nat, server_prepare и client_prepare планируются к выполнению аж два раза! Давайте проанализируем, почему так происходит:

  1. Нам необходимо выполнить два листовых теста: test_ping и exchange_files_with_flash, которые оба полагаются на опорные тесты client_prepare и server_prepare;
  2. Т.к. тесты client_prepare и server_prepare теперь не имеют снепшотов гипервизора, платформе Testo ничего не остаётся, кроме как выполнить поиск ближайших тестов, к результатам которых можно откатиться;
  3. Сначала такой поиск происходит для теста test_ping, в результате появляется путь выполнения server_unplug_nat->server_prepare->client_unplug_nat->client_prepare, который берет свое начало с момента успешного окончания тестов client_install_guest_additions и server_install_guest_additions;
  4. Такой же поиск приходится проделать и для теста exchange_files_with_flash! Ведь отсутствие снепшотов никто не отменял. В итоге некоторые тесты запланированы к выполнению аж два раза!

В данном случае мы, конечно, ещё сэкономили место на диске, но с другой стороны явно перегнули палку с точки зрения времени выполнения тестов. Наше решение никак нельзя назвать эффективным: неудобства значительно превысили пользу.

В связи с этим возникает вопрос, а какое же распредение атрибутов no_snapshots в тестах можно назвать оптимальным (в большинстве случаев)? Мы предлагаем такой алгоритм:

  1. Все листовые (не имеющие потомков) тесты можно безболезненно помечать атрибутом no_snapshots, поэтому следует это сделать;
  2. Промежуточные тесты следует помечать атрибутом no_snapshots, если эти тесты не являются опорными, то есть если к результатам их выполнения не будет происходить откатов (по крайней мере часто);
  3. Тесты, имеющие несколько потомков, не следует помечать атрибутом no_snapshots.

Если попытаться применить этот алгоритм к нашему дереву тестов, то получается следующая картина:

  1. Тесты test_ping и exchange_files_flash являются листовыми, поэтому им следует назначить атрибут no_snapshots;
  2. Тесты client_prepare и server_prepare явно не должны иметь атрибут no_snapshots, т.к. от этих тестов зависит более одного теста;
  3. Тесты client_unplug_nat и server_unplug_nat следует пометить атрибутом no_snapshots в том случае, если тесты client_prepare и server_prepare будут закешированы большую часть времени. Если эти тесты будут постоянно терять целостность кеша, то лучше оставить тесты client_unplug_nat и server_unplug_nat в первоначальном виде;
  4. Тесты install_ubuntu достаточно трудоемкие и выполняются долго. Их можно оставить в изначальном виде, даже с учётом, что к ним редко будет происходить откат, просто чтобы сэкономить себе время на лишней установке ОС в том случае в случае непредвиденных обстоятельств;
  5. Тесты install_guest_additions можно пометить атрибутом no_snapshots без особых проблем и рисков.

Проведя такую оптимизацию, мы достигнем неплохого баланса между экономией места на диске и скоростью прогона тестов. Многие подготовительные тесты имеют атрибут no_snapshots, потому что мы предполагаем, что подготовка будет происходить редко (в идеале - ровно один раз), после чего мы фиксируем итоги подготовки в тестах client_prepare и server_prepare. Производные сложные тесты, котрые предположительно будут прогоняться часто, всегда смогут положиться на результат тестов prepare, поэтому время их прогона не пострадает.

Этот алгоритм не является панацеей и универсальным решением. Конечно, в разных случаях могут быть свои нюансы, которые потребуют принимать во внимание другие факторы. Не бойтесь экспериментировать!

Итоги

Механизм no_snapshots в языке Testo-lang позволяет экономить место на диске, потенциально в ущерб времени прогона тестов. Впрочем, если грамотно распоряжаться этим механизмом, то потеря времени на прогонах тестов может оказаться очень несущественной или вовсе отсутствовать. Поэтому при достижении определенного количества тестов определенно следует остановиться и подумать, какие тесты будут выполняться часто, а какие редко - и в каких тестах можно безболезненно поставить атрибут no_snapshots.

Готовые скрипты можно найти здесь