Add XSD to validate Blockly’s XML (#2633)

* Add XSD to validate Blockly’s XML

This XSD might be used in tests or other tooling.
This commit is contained in:
Neil Fraser
2019-07-12 12:05:55 -07:00
committed by GitHub
parent 588840875e
commit 3647b6e38e
6 changed files with 621 additions and 1 deletions

View File

@@ -197,7 +197,7 @@ Blockly.Events.Change.prototype.run = function(forward) {
oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
}
if (block.domToMutation) {
var dom = Blockly.Xml.textToDom(value || '<mutation></mutation>');
var dom = Blockly.Xml.textToDom(value || '<mutation/>');
block.domToMutation(dom);
}
Blockly.Events.fire(new Blockly.Events.Change(

11
tests/xml/README.txt Normal file
View File

@@ -0,0 +1,11 @@
This directory contains the schema for validating Blockly XML files:
* blockly.xsd
Also included are three sample XML files:
* workspace.xml - XML for a complicated workspace.
* toolbox.xml - XML for a complicated toolbox with categories and separators.
* invalid.xml - XML that violates the DTD.
Testing can be performed using an online tool:
https://www.corefiling.com/opensource/schemaValidate/
http://www.utilities-online.info/xsdvalidation/

178
tests/xml/blockly.xsd Normal file
View File

@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="https://developers.google.com/blockly/xml"
xmlns="https://developers.google.com/blockly/xml">
<xs:element name="xml">
<xs:complexType>
<xs:choice>
<xs:sequence>
<xs:element ref="variables" minOccurs="0"/>
<xs:element ref="comment" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="block" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="category"/>
<xs:element ref="sep"/>
</xs:choice>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="category">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="category"/>
<xs:element ref="sep"/>
<xs:element ref="label"/>
<xs:element ref="button"/>
<xs:element ref="block"/>
</xs:choice>
<xs:attribute name="name" use="required"/>
<xs:attribute name="expanded" type="xs:boolean"/>
<xs:attribute name="colour"/>
<xs:attribute name="categorystyle"/>
<xs:attribute name="custom"/>
</xs:complexType>
</xs:element>
<xs:element name="sep">
<xs:complexType>
<xs:attribute name="gap" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="label">
<xs:complexType>
<xs:attribute name="type" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="button">
<xs:complexType>
<xs:attribute name="text" use="required"/>
<xs:attribute name="callbackKey" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="variables">
<xs:complexType>
<xs:sequence>
<xs:element ref="variable" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="variable">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:NCName">
<xs:attribute name="id"/>
<xs:attribute name="type" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:complexType name="blockType">
<xs:sequence>
<xs:element ref="mutation" minOccurs="0" maxOccurs="1"/>
<xs:element ref="field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="comment" minOccurs="0" maxOccurs="1"/>
<xs:element ref="data" minOccurs="0" maxOccurs="1"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="statement"/>
<xs:element ref="value"/>
</xs:choice>
<xs:element ref="next" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="type" use="required" type="xs:NCName"/>
<xs:attribute name="id"/>
<xs:attribute name="inline" type="xs:boolean"/>
<xs:attribute name="collapsed" type="xs:boolean"/>
<xs:attribute name="disabled" type="xs:boolean"/>
<xs:attribute name="deletable" type="xs:boolean"/>
<xs:attribute name="movable" type="xs:boolean"/>
<xs:attribute name="editable" type="xs:boolean"/>
<xs:attribute name="x" type="xs:integer"/>
<xs:attribute name="y" type="xs:integer"/>
</xs:complexType>
<xs:element name="block" type="blockType"/>
<xs:element name="shadow" type="blockType"/>
<xs:element name="value">
<xs:complexType>
<xs:sequence>
<xs:element ref="shadow" minOccurs="0" maxOccurs="1"/>
<xs:element ref="block" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="name" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="statement">
<xs:complexType>
<xs:sequence>
<xs:element ref="shadow" minOccurs="0" maxOccurs="1"/>
<xs:element ref="block" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="name" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="next">
<xs:complexType>
<xs:sequence>
<xs:element ref="shadow" minOccurs="0" maxOccurs="1"/>
<xs:element ref="block" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="field">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="stringWithSpace">
<xs:attribute name="id"/>
<xs:attribute name="name" use="required" type="xs:NCName"/>
<xs:attribute name="variabletype"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="comment">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="stringWithSpace">
<xs:attribute name="id"/>
<xs:attribute name="x" type="xs:integer"/>
<xs:attribute name="y" type="xs:integer"/>
<xs:attribute name="h" type="xs:integer"/>
<xs:attribute name="pinned" type="xs:boolean"/>
<xs:attribute name="w" type="xs:integer"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="data">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="stringWithSpace"/>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:simpleType name="stringWithSpace">
<xs:restriction base="xs:string">
<xs:whiteSpace value="preserve"/>
</xs:restriction>
</xs:simpleType>
<!-- Mutators can have any attributes and any child elements, no rules. -->
<xs:element name="mutation"/>
</xs:schema>

6
tests/xml/invalid.xml Normal file
View File

@@ -0,0 +1,6 @@
<xml xmlns="https://developers.google.com/blockly/xml">
<field name="MODE">
<block type="controls_whileUntil" id="n2JLxh%0o-D9@FtJuPpH" x="187" y="163">
</block>
</field>
</xml>

311
tests/xml/toolbox.xml Normal file
View File

@@ -0,0 +1,311 @@
<xml xmlns="https://developers.google.com/blockly/xml">
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
<category name="If">
<block type="controls_if"></block>
<block type="controls_if">
<mutation else="1"></mutation>
</block>
<block type="controls_if">
<mutation elseif="1" else="1"></mutation>
</block>
</category>
<category name="Boolean" colour="%{BKY_LOGIC_HUE}">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_negate"></block>
<block type="logic_boolean"></block>
<block type="logic_null"></block>
<block type="logic_ternary"></block>
</category>
</category>
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
<block type="controls_repeat_ext">
<value name="TIMES">
<block type="math_number">
<field name="NUM">10</field>
</block>
</value>
</block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
<field name="VAR">i</field>
<value name="FROM">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="TO">
<block type="math_number">
<field name="NUM">10</field>
</block>
</value>
<value name="BY">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
</block>
<block type="controls_forEach"></block>
<block type="controls_flow_statements"></block>
</category>
<category name="Math" colour="%{BKY_MATH_HUE}">
<block type="math_number">
<field name="NUM">123</field>
</block>
<block type="math_arithmetic"></block>
<block type="math_single"></block>
<block type="math_trig"></block>
<block type="math_constant"></block>
<block type="math_number_property"></block>
<block type="math_round"></block>
<block type="math_on_list"></block>
<block type="math_modulo"></block>
<block type="math_constrain">
<value name="LOW">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="HIGH">
<block type="math_number">
<field name="NUM">100</field>
</block>
</value>
</block>
<block type="math_random_int">
<value name="FROM">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="TO">
<block type="math_number">
<field name="NUM">100</field>
</block>
</value>
</block>
<block type="math_random_float"></block>
<block type="math_atan2"></block>
</category>
<category name="Lists" colour="%{BKY_LISTS_HUE}">
<block type="lists_create_empty"></block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<block type="math_number">
<field name="NUM">5</field>
</block>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf"></block>
<block type="lists_getIndex"></block>
<block type="lists_setIndex"></block>
</category>
<sep></sep>
<category name="Variables" custom="VARIABLE" colour="%{BKY_VARIABLES_HUE}">
</category>
<category name="Functions" custom="PROCEDURE" colour="%{BKY_PROCEDURES_HUE}">
</category>
<sep></sep>
<category name="Library" expanded="true">
<category name="Randomize">
<block type="procedures_defnoreturn">
<mutation>
<arg name="list"></arg>
</mutation>
<field name="NAME">randomize</field>
<statement name="STACK">
<block type="controls_for" inline="true">
<field name="VAR">x</field>
<value name="FROM">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="TO">
<block type="lists_length" inline="false">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
</value>
<value name="BY">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<statement name="DO">
<block type="variables_set" inline="false">
<field name="VAR">y</field>
<value name="VALUE">
<block type="math_random_int" inline="true">
<value name="FROM">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="TO">
<block type="lists_length" inline="false">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
</value>
</block>
</value>
<next>
<block type="variables_set" inline="false">
<field name="VAR">temp</field>
<value name="VALUE">
<block type="lists_getIndex" inline="true">
<mutation statement="false" at="true"></mutation>
<field name="MODE">GET</field>
<field name="WHERE">FROM_START</field>
<value name="AT">
<block type="variables_get">
<field name="VAR">y</field>
</block>
</value>
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
</value>
<next>
<block type="lists_setIndex" inline="false">
<value name="AT">
<block type="variables_get">
<field name="VAR">y</field>
</block>
</value>
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
<value name="TO">
<block type="lists_getIndex" inline="true">
<mutation statement="false" at="true"></mutation>
<field name="MODE">GET</field>
<field name="WHERE">FROM_START</field>
<value name="AT">
<block type="variables_get">
<field name="VAR">x</field>
</block>
</value>
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
</value>
<next>
<block type="lists_setIndex" inline="false">
<value name="AT">
<block type="variables_get">
<field name="VAR">x</field>
</block>
</value>
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
<value name="TO">
<block type="variables_get">
<field name="VAR">temp</field>
</block>
</value>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</statement>
</block>
</statement>
</block>
</category>
<category name="Jabberwocky">
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT">'Twas brillig, and the slithy toves</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT"> Did gyre and gimble in the wabe:</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT">All mimsy were the borogroves,</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT"> And the mome raths outgrabe.</field>
</block>
</value>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT">"Beware the Jabberwock, my son!</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT"> The jaws that bite, the claws that catch!</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT">Beware the Jubjub bird, and shun</field>
</block>
</value>
<next>
<block type="text_print">
<value name="TEXT">
<block type="text">
<field name="TEXT"> The frumious Bandersnatch!"</field>
</block>
</value>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</category>
</category>
</xml>

114
tests/xml/workspace.xml Normal file
View File

@@ -0,0 +1,114 @@
<xml xmlns="https://developers.google.com/blockly/xml">
<variables>
<variable type="" id="4zqCO]S-jo+2^`maCt|K">x</variable>
<variable type="" id="K/cNaUs{[7ol-9}CP7RL">i</variable>
<variable type="" id="8ZxN/~:DTP3pX4eGrv2b">text</variable>
</variables>
<comment id="TUkxWVwnVb;#]^MfteT4" x="0" y="0" h="100" w="100">This is a workspace comment.
This is a new line on the workspace comment.
</comment>
<block type="procedures_defnoreturn" id="(i!.lvB^fBr,}8Lf_,9J" x="113" y="238">
<mutation>
<arg name="x" varid="4zqCO]S-jo+2^`maCt|K"/>
<arg name="i" varid="K/cNaUs{[7ol-9}CP7RL"/>
</mutation>
<field name="NAME">bar</field>
<comment pinned="false" h="80" w="160">Describe this function...</comment>
<statement name="STACK">
<block type="controls_if" id="K|8!{v)9/^^1=2GzXe[_">
<mutation elseif="1" else="1"/>
<value name="IF0">
<block type="logic_compare" id="#N?|av?JV]CnUOitJ4n$">
<field name="OP">LT</field>
<value name="A">
<block type="text_getSubstring" id="w,45*W^AcPera3-.x*wl" inline="false">
<mutation at1="false" at2="true"/>
<field name="WHERE1">FIRST</field>
<field name="WHERE2">FROM_START</field>
<value name="STRING">
<block type="variables_get" id="oF,qA%c[v(h6/iLJJ0Zn">
<field name="VAR" id="8ZxN/~:DTP3pX4eGrv2b" variabletype="">text</field>
</block>
</value>
</block>
</value>
<value name="B">
<block type="text_join" id="I@|f@k!fgu4yqO{s]3-W">
<mutation items="3"/>
</block>
</value>
</block>
</value>
<value name="IF1">
<block type="procedures_callreturn" id="P;p2LLz;hjJ@RE?:TVHc">
<mutation name="foo"/>
</block>
</value>
<statement name="DO1">
<block type="text_print" id="1~S=odLNrF|T(O.|Z2Gn">
<value name="TEXT">
<shadow type="text" id="|KS=0M,=!+@H1V/cuHSW">
<field name="TEXT">&lt;html&gt;</field>
</shadow>
</value>
</block>
</statement>
<statement name="ELSE">
<block type="math_change" id="KiMt-oQI`=,FYKY5ZK=:">
<field name="VAR" id="K/cNaUs{[7ol-9}CP7RL" variabletype="">i</field>
<value name="DELTA">
<shadow type="math_number" id="-O]V4./`ipovlf]!:v![">
<field name="NUM">1</field>
</shadow>
</value>
</block>
</statement>
</block>
</statement>
</block>
<block type="logic_ternary" id="VhRm*A*n,#pZJdysUqQh" x="537" y="462"/>
<block type="procedures_defreturn" id="lpPefvcBM.yRI=OpBQ?A" x="213" y="637">
<field name="NAME">foo</field>
<comment pinned="false" h="80" w="160">This function does...
...nothing!
</comment>
<statement name="STACK">
<block type="procedures_ifreturn" id="?{hnO}N?G,L*XBAno{w6">
<mutation value="1"/>
<value name="CONDITION">
<block type="logic_boolean" id="jd[*W1IM5BX?g%BO+e;L">
<field name="BOOL">TRUE</field>
</block>
</value>
<value name="VALUE">
<block type="colour_rgb" id="|$B_?vvc2K?w89t!P,U8">
<value name="RED">
<shadow type="math_number" id="z}r5j$awNvVsBvyF50Ly">
<field name="NUM">100</field>
</shadow>
</value>
<value name="GREEN">
<shadow type="math_number" id="4%j`]!d#xCkG[_*p,7H2">
<field name="NUM">50</field>
</shadow>
</value>
<value name="BLUE">
<shadow type="math_number" id="~j/fzD+7+hB$6K[}#Ak/">
<field name="NUM">0</field>
</shadow>
</value>
</block>
</value>
<next>
<block type="procedures_callnoreturn" id="~3R~eK(7]~pbGFW$Idc-">
<mutation name="bar">
<arg name="x"/>
<arg name="i"/>
</mutation>
</block>
</next>
</block>
</statement>
</block>
</xml>