Спецификация и тестирование DOM API

27
Спецификация и тестирование DOM API В. Кулямин

description

Спецификация и тестирование DOM API. В. Кулямин. План. Стандарт DOM API Что такое DOM Зачем нужен стандарт Основные элементы DOM API Примеры Формализация требований стандарта Как она выглядит Зачем она нужна Разработка тестов DOM API Какие тесты есть и почему их недостаточно - PowerPoint PPT Presentation

Transcript of Спецификация и тестирование DOM API

Page 1: Спецификация и тестирование  DOM API

Спецификация и тестирование DOM API

В. Кулямин

Page 2: Спецификация и тестирование  DOM API

План• Стандарт DOM API

– Что такое DOM– Зачем нужен стандарт– Основные элементы DOM API– Примеры

• Формализация требований стандарта– Как она выглядит– Зачем она нужна

• Разработка тестов DOM API– Какие тесты есть и почему их недостаточно– Техническая основа – JsUnit– Разработка тестов на основе формальных требований– Примеры– Приглашение к сотрудничеству

2

Page 3: Спецификация и тестирование  DOM API

Web приложения

Браузер Web-сервер

Серверные скрипты

Клиентские скрипты

HTML CSS

SVG

HTTP

Объекты DOM – Document Object Model

ECMAScript

SilverlightFlexJavaFX

3

скрипты

Page 4: Спецификация и тестирование  DOM API

Пример использования DOM

4

Page 5: Спецификация и тестирование  DOM API

Стандарт DOM• http://www.w3.org/DOM/DOMTR• Основные документы

– Core http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407– HTML http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109– Style (StyleSheets + CSS) http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113– Traversal and Range http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113– Load-Save http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407– Validation http://www.w3.org/TR/2004/REC-DOM-Level-3-Val-20040127– Element Traversal http://www.w3.org/TR/2008/REC-ElementTraversal-20081222– Xpath http://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226– Views and Formatting http://www.w3.org/TR/2004/NOTE-DOM-Level-3-Views-20040226– Events http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908

http://www.w3.org/TR/2009/WD-eventsource-20091222/• Дополнительные части

– DOM Events http://www.w3.org/TR/#tr_DOM_events– MathML http://www.w3.org/TR/#tr_MathML– SMIL http://www.w3.org/TR/#tr_SMIL– SVG http://www.w3.org/TR/#tr_SVG

http://www.w3.org/TR/#tr_SVG_Tiny

5

Page 6: Спецификация и тестирование  DOM API

Основные модули DOM

Модуль Типы Константы Методы Атрибуты R/W Атрибуты

Core 34 64 89 75 10

HTML 56 36 293 255

Events 15 29 35 45

Views 25 13 32 67 21

Style Sheets 5 4 12 2

CSS 22 37 22 158 132

Traversal 4 16 13 9 1

Range 3 8 19 7

Load-Save 15 15 14 27 15

Validation 5 14 22 13 1

XPath 6 15 7 9

Всего 190 211 283 715 437

6

Page 7: Спецификация и тестирование  DOM API

7

Развитие DOMLevel Core HTML Events Views StSh CSS Trav Rng L-S Val XPath Всего

1

(2000)

Тип 22 55 77

Мет 35 34 69

Атрб 33 289 322

RWA 4 254 258

2

(2000-2003)

Нов Тип 2 1 8 2 5 22 4 3 47

Мет 22 2 11 4 22 13 19 93

Атрб 8 4 25 2 12 158 9 7 225

RWA 1 1 2 132 1 137

Мод Тип 7 13 20

Мет 1 6 7

Атрб 1 21 22

RWA 1 16 17

3

(2004-2010)

Нов Тип 10 7 23 15 5 6 66

Мет 32 24 32 14 22 7 131

Атрб 35 20 65 27 13 9 169

RWA 5 21 15 1 42

Мод Тип 7 6 13

Мет 5 1 6

Атрб 1 1

RWA

Page 8: Спецификация и тестирование  DOM API

Поддержка DOM

8

Page 9: Спецификация и тестирование  DOM API

Переносимость Web-приложений

9

Internet Explorer

Web-сервер

Серверные скрипты

Клиентские скрипты

Mozilla Firefox

Клиентские скрипты

Opera

Клиентские скрипты

Интернет

Page 10: Спецификация и тестирование  DOM API

Пример проблемы переносимости

10

Реализация Element.setAttribute() в IE не удовлетворяет стандарту

Page 11: Спецификация и тестирование  DOM API

Что делать?

• Систематизировать требования стандарта• Добиться однозначности и согласованности

требований• Сделать тестовый набор, проверяющий

соответствие им• Сделать его общедоступным и известным• Постоянно использовать этот набор при

разработке, развитии и оценке браузеров

11

Page 12: Спецификация и тестирование  DOM API

Уже имеющиеся тестыW3C DOM Conformance Test Suitehttp://www.w3.org/DOM/Test/http://dev.w3.org/cvsweb/2001/DOM-Test-Suite/• Последние изменения – май 2004• Покрыты

– Core L1, L2, L3– HTML L1, L2– Events L2, L3 (очень слабо)– Load-Save L3– Validation L3

• Не покрыто все остальное и все изменения с 2004 • Нет детальной прослеживаемости тестов к требованиям• Требования не проанализированы на однозначность, полноту и

согласованность

12

Page 13: Спецификация и тестирование  DOM API

Прослеживаемость к требованиям

13

Node insertBefore (Node newChild, Node refChild) Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the end of the list of children. If newChild is a DocumentFragment [p.40] object, all of its children are inserted, in the same order, before refChild. If the newChild is already in the tree, it is first removed.Note: Inserting a node before itself is implementation dependent.Parameters:newChild of type Node – The node to insert.

refChild of type Node – The reference node, i.e., the node before which the new node must be inserted.

Return Value: The node being inserted.Exceptions: DOMExceptionHIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the newChild node, or if the node to insert is one of this node’s ancestors or this node itself, or if this node is of type Document [p.41] and the DOM application attempts to insert a second DocumentType [p.115] or Element [p.85] node.WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that created this node.NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly or if the parent of the node being inserted is readonly. NOT_FOUND_ERR: Raised if refChild is not a child of this node.NOT_SUPPORTED_ERR: if this node is of type Document [p.41] , this exception might be raised if the DOM implementation doesn’t support the insertion of a DocumentType [p.115] or Element [p.85] node.

General, N1 N2

N3N4

N6 E1 E2 E3 E4 E5 E6

E7 E8 E9 E10 E11

N5

Page 14: Спецификация и тестирование  DOM API

Неоднозначности и неполнота

14

Что имеется в виду под «the node being inserted»?DocumentFragment или его элемент?

• Что будет при вставке null?• При вставке DocumentFragment

– Что будет результатом?– Как изменится список «детей» при некорректном «ребенке» в конце?– Можно ли добавить пустой DocumentFragment, если добавлять «детей»

нельзя?• Как определить, поддерживается ли вставка DocumentType и Element?• Почему нет ограничений на порядок «детей» в Document?

Page 15: Спецификация и тестирование  DOM API

Формализация стандарта

• Обеспечение большей совместимости (основной цели стандартов)– Выявление неоднозначностей– Выявление рассогласований, альтернатив– Выявление неполноты

• Возможность строгого контроля соответствия– Для тестов и анализа:

правила корректного поведения– Для тестов:

классы ситуаций, в которых поведение различимо

15

Page 16: Спецификация и тестирование  DOM API

Пример Node.insertBefore()

16

Node insertBefore (Node newChild, Node refChild) Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the end of the list of children. If newChild is a DocumentFragment [p.40] object, all of its children are inserted, in the same order, before refChild. If the newChild is already in the tree, it is first removed.Note: Inserting a node before itself is implementation dependent.Parameters:newChild of type Node – The node to insert.

refChild of type Node – The reference node, i.e., the node before which the new node must be inserted.Return Value: The node being inserted.Exceptions: DOMExceptionHIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the newChild node, or if the node to insert is one of this node’s ancestors or this node itself, or if this node is of type Document [p.41] and the DOM application attempts to insert a second DocumentType [p.115] or Element [p.85] node.WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that created this node.NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly or if the parent of the node being inserted is readonly. NOT_FOUND_ERR: Raised if refChild is not a child of this node.NOT_SUPPORTED_ERR: if this node is of type Document [p.41] , this exception might be raised if the DOM implementation doesn’t support the insertion of a DocumentType [p.115] or Element [p.85] node.

General, N1 N2

N3N4

N6 E1 E2 E3 E4 E5 E6

E7 E8 E9 E10 E11

N5

Page 17: Спецификация и тестирование  DOM API

Пример: спецификация исключений

17

Node Node.InsertBefore(Node newChild, Node refChild)

{

Contract.Requires(newChild != null); // What if newChild is null? DOM specification says nothing

Contract.EnsuresOnThrow<DomException> (

// If this node is of a type that does not allow children of the type of the newChild node

!IsAllowedChild(newChild) && !(newChild is DocumentFragment)

// -- in particular, if inserted DocumentFragment has a prohibited child for this node

|| newChild is DocumentFragment && newChild.Children.Count > 0

&& Contract.Exists(0, newChild.Children.Count, i => !IsAllowedChild(newChild.Children[i]))

// or this node is of type Document and newChild is a second DocumentType or Element node

|| this is Document && newChild is DocumentType && HasDifferentChildOfType(typeof(DocumentType), newChild)

|| this is Document && newChild is Element && HasDifferentChildOfType(typeof(Element), newChild)

// -- in part. if inserted DocumentFragment has a DocumetType or an Element child, and this node has one too

|| this is Document && newChild is DocumentFragment && HasDifferentChildOfType(typeof(DocumentType), null)

&& newChild.HasDifferentChildOfType(typeof(DocumentType), null)

|| this is Document && newChild is DocumentFragment && HasDifferentChildOfType(typeof(Element), null)

&& newChild.HasDifferentChildOfType(typeof(Element), null)

// if newChild is this node itself or if newChild is one of this node's ancestors

|| newChild == this || Ancestors.Contains(newChild)

// if newChild was created from a different document than the one that created this node

|| !(this is Document) && OwnerDocument != newChild.OwnerDocument

// -- (refinement) owner document for Document is null, whether newChild owner is other document

|| this is Document && this != newChild.OwnerDocument

// if this node is readonly or if the parent of the node being inserted is readonly

|| IsReadOnly || newChild.Parent != null && newChild.Parent.IsReadOnly

// if refChild is not a child of this node

|| refChild != null && !Children.Contains(refChild) );

E1E4

E5

E2

E3

E6E7

E8

E9

Если мы вставляем обычный узел (не DocumentFragment), узлы такого типа должны быть разрешены в качестве «детей»

Если мы вставляем DocumentFragment, его «дети» должны быть разрешены в качестве «детей» текущего узла

Если мы вставляем DocumentType в Document, не должно быть других «детей» типа DocumentType

Нельзя в качестве «ребенка» вставлять «предка» текущего узла

Нельзя вставлять узел, построенный в рамках другого документа

Page 18: Спецификация и тестирование  DOM API

Пример: спецификация нормы

18

// If the newChild is already in the tree, it is first removed Contract.Ensures( Contract.OldValue<Node>(newChild.Parent) == null || Contract.OldValue<Node>(newChild.Parent) == this || !Contract.OldValue<Node>(newChild.Parent).Children.Contains(newChild) ); Contract.Ensures( !(newChild is DocumentFragment) || newChild.Children.Count == 0 ); // If refChild is null and newChild is not DocumentFragment, insert newChild at the end of the list of children Contract.Ensures( !(refChild == null && !(newChild is DocumentFragment) && !Contract.OldValue<bool>(Children.Contains(newChild))) || Children.Count == Contract.OldValue<int>(Children.Count) + 1 && Children[Children.Count - 1] == newChild && Children.GetRange(0, Children.Count - 1).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.Count))) ); Contract.Ensures( !(refChild == null && !(newChild is DocumentFragment) && Contract.OldValue<bool>(Children.Contains(newChild))) || Children.Count == Contract.OldValue<int>(Children.Count) && Children[Children.Count - 1] == newChild && Children.GetRange(0, Contract.OldValue<int>(Children.IndexOf(newChild))).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.IndexOf(newChild)))) && Children.GetRange( Contract.OldValue<int>(Children.IndexOf(newChild)) , Children.Count - Contract.OldValue<int>(Children.IndexOf(newChild)) - 1) .Equals(Contract.OldValue<List<Node>>( Children.GetRange(Children.IndexOf(newChild) + 1, Children.Count - Children.IndexOf(newChild) - 1)))); // If refChild isn't null and newChild is not DocumentFragment, insert newChild before the existing child node refChild Contract.Ensures( !(refChild != null && !(newChild is DocumentFragment) && !Contract.OldValue<bool>(Children.Contains(newChild))) || Children.Count == Contract.OldValue<int>(Children.Count) + 1 && Children.IndexOf(newChild) == Contract.OldValue<int>(Children.IndexOf(refChild)) && Children.GetRange(0, Children.IndexOf(newChild)).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.IndexOf(refChild)))) && Children.GetRange(Children.IndexOf(newChild) + 1, Children.Count - Children.IndexOf(newChild) - 1) .Equals(Contract.OldValue<List<Node>>( Children.GetRange(Children.IndexOf(refChild), Children.Count - Children.IndexOf(refChild)))) ); Contract.Ensures( !( refChild != null && !(newChild is DocumentFragment) && Contract.OldValue<bool>(Children.Contains(newChild)) && newChild == refChild ) || Children.Count == Contract.OldValue<int>(Children.Count) && Children.IndexOf(newChild) == Contract.OldValue<int>(Children.IndexOf(refChild)) && Children.GetRange(0, Children.Count).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.Count))) ); ...

N4

N2

N1

При формализации приходится выделять разные случаи для одного требования

Page 19: Спецификация и тестирование  DOM API

Пример: различные ситуации IНормаo Вставка обычного узла (не DocumentFragment)

– Вставка узла, не являющегося «ребенком» текущего• Без своего «родителя»• Со своим «родителем»

– Вставка одного из «детей» текущего узла (при этом набор «детей» не меняется)• Совпадающего с refChild • Несовпадающего

Стоящего непосредственно перед refChild Стоящего перед refChild, но не сразу Стоящего после refChild

o Вставка DocumentFragment– Пустого– Из одного элемента– Из нескольких элементов

Вставка в конец (refChild == null) Вставка не в конец

– В начало– В середину

19

Page 20: Спецификация и тестирование  DOM API

Пример: различные ситуации IIИсключения • Вставляемый узел по типу не может быть «ребенком» текущего

– Document --> DocumentType, Element, ProcessingInstruction, Comment• Только один «ребенок» типа DocumentType или Element

– DocumentFragment, Element, Entity, EntityReference --> Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference

– Attr --> Text, EntityReference– DocumentType, ProcessingInstruction, Comment, Text, CDATASection, Notation --> nothing

• Вставляемый узел совпадает с текущим или с его предком• Вставляемый узел из другого документа• Текущий узел неизменяем• «Родитель» вставляемого узла неизменяем• refChild не является «ребенком» текущего узла Вставка обычного узла Вставка DocumentFragment Вставка в конец (refChild == null) Вставка не в конец

– В начало– В середину

20

Page 21: Спецификация и тестирование  DOM API

Создание тестов для браузеров

• JsUnit http://www.jsunit.net/• Тестовые страницы• Тестовые функции• Инициализация и финализация• Тестирование исключений• Тестовые наборы

21

Page 22: Спецификация и тестирование  DOM API

Пример тестовой страницы<html> <head id="header“> <script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>

<script language="JavaScript"> <!—

var mainElement;

function setUp() {

mainElement = document.documentElement;

}

function test_Document_ExistingElement_ToEnd() {

var l = document.childNodes.length;

var res = document.insertBefore(mainElement, null);

assertEquals("Result should be inserted node", mainElement, res);

assertEquals("Number of children should preserve", l, document.childNodes.length);

assertEquals("Inserted node should keep to be the last child", mainElement, document.lastChild);

assertEquals("This node should become the inserted node parent", document, mainElement.parentNode);

}

function tearDown() {}

-->

</script>

</head>

<body> Test page for DOM Core Node.insertBefore() method </body>

</html>

22

Page 23: Спецификация и тестирование  DOM API

Оценка корректности• assert([comment], booleanValue) • assertTrue([comment], booleanValue) • assertFalse([comment], booleanValue) • assertEquals([comment], value1, value2) • assertNotEquals([comment], value1, value2) • assertNull([comment], value) • assertNotNull([comment], value) • assertUndefined([comment], value) • assertNotUndefined([comment], value) • assertNaN([comment], value) • assertNotNaN([comment], value) • fail(comment) • Исключения – не поддерживаются прямо

23

Page 24: Спецификация и тестирование  DOM API

Пример теста на исключениеfunction test_Document_Text_ToEnd() { var l = doc.childNodes.length; var parent = text.parentNode; var exception = true; try { res = doc.insertBefore(text, null); exception = false; } catch(e) { assertEquals("Exception should be HIERARCHY_REQUEST_ERR", 3, e.code); assertEquals("Number of children should preserve", l, doc.childNodes.length);

assertEquals("Inserted node parent should preserve", parent, text.parentNode); } if(!exception) fail("Exception should be created");}

function test_Document_Text_ToEnd() { var l = doc.childNodes.length; var parent = text.parentNode; try { res = doc.insertBefore(text, null); fail("Exception should be created"); } catch(e) { assertEquals("Exception should be HIERARCHY_REQUEST_ERR", 3, e.code); assertEquals("Number of children should preserve", l, doc.childNodes.length);

assertEquals("Inserted node parent should preserve", parent, text.parentNode); }}

24

Page 25: Спецификация и тестирование  DOM API

Пример тестового набора<html> <head> <title>DOM Test Suite</title> <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css"> <script language="JavaScript" type="text/javascript" src="jsunit/app/jsUnitCore.js"></script> <script language="JavaScript" type="text/javascript"> <!-- function coreSuite() { var result = new top.jsUnitTestSuite(); result.addTestPage("http://localhost:8080/Node_InsertBefore_Normal.html"); result.addTestPage("http://localhost:8080/Node_InsertBefore_NodeTypes.html"); return result; }

function suite() { var newsuite = new top.jsUnitTestSuite(); newsuite.addTestSuite(coreSuite()); return newsuite; } --> </script></head>

<body><h1>DOM Test Suite</h1>

<p>This page contains a suite of tests for testing DOM.</p></body></html>

25

Page 26: Спецификация и тестирование  DOM API

Приглашение к сотрудничеству

DOM API Testing на CodePlex• http://domapitesting.codeplex.com

26

Page 27: Спецификация и тестирование  DOM API

Спасибо за внимание!