A new RDFa Test Harness

Implementing the RDFa Test Suite as a modern Web application using Sinatra, Backbone.js and Bootstrap.js.

Recently, RDFa entered the Candidate Recommendation phase for releasing

RDFa Core 1.1, RDFa 1.1 Lite, and XHTML+RDFa 1.1 as W3C Standards.

I’ve been using RDFa for a couple of years, originally as part of the Connected Media Experience,

and lately because

I’ve become passionate about the Semantic Web. For the last 10 months, or so, this has extended to my becoming an Invited Expert

in the W3C, where I’ve worked on RDFa, HTML microdata and JSON-LD.

This is an introductory blog post on the creation of a new RDFa Test Suite. Here we discuss the use of

Sinatra, Backbone.js and Bootstrap.js to run the test suite. Later will come articles on the usefulness

of JSON-LD as a means of driving a test harness, generating test reports,

and the use of BrowserID to deal with [Distributed

Denial of Service attacks](https://plus.google.com/102122664946994504971/posts/QNFm43sbokn) that cropped up overnight.

RDFa Test Suite

Along with other RDF parsers and serializers (see sidebar), I have an RDFa parser and serializer.

In implementing the parser, and while working on new features for RDFa 1.1,

the RDFa Test Suite has been an invaluable resource. In my testing, I would use the

test manifest, describing the sets of inputs and expected outputs in the form of a SPARQL ASK query.

A basic RDFa test is a small amount of markup intended to test a single feature.

<!DOCTYPE html>
<html prefix="dc: http://purl.org/dc/elements/1.1/">
<head>
   <title>Test 0001</title>
</head>
<body>
  <p>
    This photo was taken by
    <span class="author"
          about="photo1.jpg"
          property="dc:creator">Mark Birbeck</span>.</p>
</body>
</html>

In this example, we’re testing that the @about attribute sets the subject, @property sets the property and the

text content sets the object of a single RDF statement. Rendered as Turtle, it would look like the following:

@prefix dc: "http://purl.org/dc/elements/1.1/" .
<photo1.jpg> dc:creator "Mark Birbeck" .

A query to test this looks like the following:

PREFIX dc: <http://purl.org/dc/elements/1.1/>
ASK WHERE {
    <http://rdfa.info/test-suite/test-cases/rdfa1.1/html5/photo1.jpg>
      dc:creator "Mark Birbeck" .
}

Note that the relative IRI in the @about is expanded relative to the document location, as is tested in the SPARQL query.

Using the test suite requires a publicly available endpoint, for which I released the RDF Distiller

to test my implementation.

The test suite works with a provided URL, which invokes the processor with a test document. Basically, it does the following:

  1. The Web application performs a GET on the /test-suite/check-test/:version/:suite/:num service URL along
with the processor endpoint as a query parameter.   2. The service invokes the processor endpoint passing the URL of the test document.   3. The processor then parses that document
  
and returns a result in a different RDF format (for example [Turtle](http://www.w3.org/TR/turtle/) or [RDF/XML](http://www.w3.org/TR/REC-rdf-syntax/)).   4. The processor parses the returned RDF document into a graph, and performs a SPARQL query against that graph.   5. The result is a `true` or `false` value, which determines if the test passes or not.   6. The result is formatted as JSON and returned the Web application.   7. The Web application updates the test status in the UI.   8. If running all tests, the completion event triggers the next test to run.

Sinatra

Sinatra is a great lightweight framework for deploying simple Ruby applications on the web. The needs of this application,

while requiring a lot of different libraries, were really fairly simple. Basically, return a page listing the various tests,

respond to requests for test case source documents, activate a test with a specified processor endpoint and return the results.

The basic setup of the app is fairly straight forward:

# Return the test suite driver page
get '/test-suite/' do
  haml :test_suite
end

# Return a particular test, or SPARQL query
get '/test-suite/test-cases/:version/:host_language/:num' do
  source = File.open(File.expand_path("../tests/#{num}.html"))
  case host_language
  when 'xhtml'
    # do XHTML-specific formatting of the test
  when 'html'
    # do HTML-specific formatting of the test
  when 'xml'
  when 'svg'
  end
end

# Run a test
get '/test-suite/check-test/:version/:suite/:num' do
  # Get the SPARQL query
  source = File.open(File.expand_path("../tests/#{num}.sparql"))

  # Do host-language specific modifications of the SPARQL query.
  query = SPARQL.parse(source)

  # Invoke the processor and retrieve results, parsed into an RDF graph
  graph = RDF::Graph.load(params['rdfa-extractor'] + test_path(version, suite, num, format))

  # Run the query
  result = query.execute(graph)

  # Return results as JSON
  {:status => result}.to_json
end

Backing up the Sinatra application are a number of Ruby Gems for working with Linked Data and SPARQL. In

addition to reading and writing RDFa, there are gems for managing RDF graphs, reading other formats, such as Turtle

and RDF/XML, and running the SPARQL queries.

Driving the test suite is an Web application built using Backbone.js and Bootstrap.js.

Backbone.js

Backbone is a JavaScript model-viewer-controller framework for building responsive applications in

JavaScript. It encourages building modular applications split into multiple classes with weak interdependencies. Models and

Collections are used to maintain application state, and reflect information from a server. The RDFa test suite has

two main models and a collection.

The Version model keeps track of information about what is being run. This includes the RDFa version and host language being tested

along with the current processor endpoint. It looks something like the following:

window.Version = Backbone.Model.extend({
  defaults: {
    processorURL: "http://www.w3.org/2012/pyRdfa/extract?uri=",
    processorName: "pyRdfa",
    processorDOAP: "http://www.w3.org/2012/pyRdfa",

    // List of processors
    processors: {}
  }

  // Appropriate host languages for the current version
  hostLanguages: function() {
    return {
      "rdfa1.0": ["SVG", "XHTML1"],
      "rdfa1.1": ["HTML4", "HTML5", "SVG", "XHTML1", "XHTML5", "XML"],
      "rdfa1.1-vocab": ["HTML4", "HTML5", "SVG", "XHTML1", "XHTML5", "XML"]
    }[this.get("version")];
  }
});

The Test model, uses the test manifest to instantiate a number of

Test model instances. Changing information in the Version model causes different tests to be enabled or disabled,

as appropriate for the given RDFa version and host language. It also affects URL generation for retrieving and running

different tests. In addition to instantiating tests, the Test Collection also allows the complete sequence of tests

to be run, by listening to an event for a completion event from running a test on the first test model and initiating

the test of the next.

Styling the User Interface

I’m no designer, but I like a good looking and efficient user interface. Fortunately, the people at Twitter do too,

and they released Bootstrap.js as a means of tackling common problems. I won’t go into detail here, but check out

their example page to get an idea of the things you can do with Bootstrap.

What I immediately noticed about it is that I didn’t really need to worry about layout. Note that you can even run

the Test Suite from an iPhone!

Data Driven Tests

Of course, returning the test suite HTML is just part of the problem, we also need to get details about each test to

the page, so that it can respond to requests to run specific tests. The tests are managed through a

test manifest, which is kept in Turtle format to make it easy to add

tests. A typical entry looks like the following:

<test-cases/0001> a test:TestCase;
   dc:title "Predicate establishment with @property";
   rdfatest:rdfaVersion "rdfa1.0", "rdfa1.1";
   rdfatest:hostLanguage "xml", "xhtml1", "html4", "html5", "xhtml5";
   test:classification test:required;
   test:informationResourceInput <test-cases/0001.html>;
   test:informationResourceResults <test-cases/0001.sparql> .

The basically describes an IRI for the test, in this case test-cases/0001 relative to the location of the test suite,

the title of the test, the RDFa versions and host languages it applies to and a reference to the input and result documents.

RDFa has over 200 such tests defined. This is all well and good, but requiring yet another data format is an added complication.

Better to have the tests defined in a format more appropriate for use within an Web application, such as JSON. As it happens

JSON-LD is another specification that is still underway, but proving to be quite flexible and useful for our needs. For a

peek at the JSON-LD version of the RDFa test suite manifest, look here.

More on using JSON-LD, and why it’s such a good match for RDFa in the next post.