You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2014/03/18 13:29:25 UTC

svn commit: r1578842 - in /jena/trunk/jena-arq/src: main/java/com/hp/hpl/jena/sparql/engine/ref/ main/java/com/hp/hpl/jena/sparql/resultset/ main/java/org/apache/jena/atlas/web/auth/ test/java/com/hp/hpl/jena/sparql/resultset/

Author: rvesse
Date: Tue Mar 18 12:29:24 2014
New Revision: 1578842

URL: http://svn.apache.org/r1578842
Log:
Allow arbitrary extra keys at top level of SPARQL JSON results per specification (JENA-655)

Also some minor tweaks to javadoc in other classes

Modified:
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/ref/QueryEngineRef.java
    jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/resultset/JSONInput.java
    jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/PreemptiveBasicAuthenticator.java
    jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/resultset/TestResultSetFormat2.java

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/ref/QueryEngineRef.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/ref/QueryEngineRef.java?rev=1578842&r1=1578841&r2=1578842&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/ref/QueryEngineRef.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/engine/ref/QueryEngineRef.java Tue Mar 18 12:29:24 2014
@@ -40,7 +40,7 @@ import com.hp.hpl.jena.sparql.util.Conte
 
 /** "Reference" query engine - this simply executes the algebra expression as-is
  *  using a simple (non-scalable) execution strategy that follows the definition
- *  of SPARQL as closely as possible.  The referenc query engine does provide the
+ *  of SPARQL as closely as possible.  The reference query engine does provide the
  *  algebra extensions. 
  */
 public class QueryEngineRef extends QueryEngineBase

Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/resultset/JSONInput.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/resultset/JSONInput.java?rev=1578842&r1=1578841&r2=1578842&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/resultset/JSONInput.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/resultset/JSONInput.java Tue Mar 18 12:29:24 2014
@@ -126,7 +126,7 @@ public class JSONInput extends SPARQLRes
         
         if ( obj.hasKey(kBoolean) )
         {
-            checkContains(obj, kHead, kBoolean) ;
+            checkContains(obj, true, kHead, kBoolean) ;
             booleanResult = obj.get(kBoolean).getAsBoolean().value() ;
             rows = null ;
             return ;
@@ -134,7 +134,7 @@ public class JSONInput extends SPARQLRes
         
         rows = new ArrayList<Binding>(1000) ;
         
-        checkContains(obj, kHead, kResults) ;
+        checkContains(obj, true, kHead, kResults) ;
 
         if ( ! obj.hasKey(kHead) )    throw new ResultSetException("No 'head' for results") ;
         if ( ! obj.hasKey(kResults) ) throw new ResultSetException("No 'results' for results") ;
@@ -221,14 +221,14 @@ public class JSONInput extends SPARQLRes
     LabelToNode labelMap = SyntaxLabels.createLabelToNode() ;
     private Node parseOneTerm(JsonObject term)
     {
-        checkContains(term, kType, kValue, kXmlLang, kDatatype) ;
+        checkContains(term, false, kType, kValue, kXmlLang, kDatatype) ;
         
         String type = stringOrNull(term, kType) ;
         String v = stringOrNull(term, kValue) ;
         
         if ( kUri.equals(type) )
         {
-            checkContains(term, kType, kValue) ;
+            checkContains(term, false, kType, kValue) ;
             String uri = v ;
             Node n = NodeFactory.createURI(v) ;
             return n ;
@@ -260,12 +260,12 @@ public class JSONInput extends SPARQLRes
         
     }
     
-    private static void checkContains(JsonObject term, String...keys)
+    private static void checkContains(JsonObject term, boolean allowUndefinedKeys, String...keys)
     {
         List<String> x = Arrays.asList(keys) ;
         for ( String k : term.keys() )
         {
-            if ( !x.contains(k) )
+            if ( !x.contains(k) && !allowUndefinedKeys )
                 throw new ResultSetException("Expected only object keys "+Arrays.asList(keys)+" but encountered '"+k+"'") ; 
         }
     }

Modified: jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/PreemptiveBasicAuthenticator.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/PreemptiveBasicAuthenticator.java?rev=1578842&r1=1578841&r2=1578842&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/PreemptiveBasicAuthenticator.java (original)
+++ jena/trunk/jena-arq/src/main/java/org/apache/jena/atlas/web/auth/PreemptiveBasicAuthenticator.java Tue Mar 18 12:29:24 2014
@@ -32,16 +32,29 @@ import org.apache.http.protocol.HttpCont
  * A decorator for other authenticators that may be used to enable preemptive
  * basic authentication.
  * <p>
+ * This can <strong>only</strong> be used with servers that support Basic HTTP
+ * authentication.  For any other authentication scheme the use of this
+ * authenticator will result in authentication failures.
+ * </p>
+ * <h3>Security Concerns</h3>
+ * <p>
  * It is <strong>important</strong> to note that preemptive basic authentication
  * is less secure because it can expose credentials to servers that do not
  * require them.
  * </p>
+ * <h3>Standard vs Proxy Authentication</h3>
  * <p>
  * Doing preemptive authentication requires knowing in advance whether you will
  * be doing standard or proxy authentication i.e. whether the remote server will
- * challenge with 401 or 407.  If you need both you can take advantage of this
+ * challenge with 401 or 407. If you need both you can take advantage of this
  * being a decorator and simply layer multiple instances of this.
  * </p>
+ * <p>
+ * However you must remember that this <strong>only</strong> works for Basic
+ * HTTP authentication, any other authentication scheme cannot be done
+ * preemptively because it requires a more complex and secure challenge response
+ * process.
+ * </p>
  */
 public class PreemptiveBasicAuthenticator implements HttpAuthenticator {
 
@@ -82,11 +95,12 @@ public class PreemptiveBasicAuthenticato
         if (authCache == null)
             authCache = new BasicAuthCache();
         BasicScheme basicAuth = new BasicScheme(this.isProxy ? ChallengeState.PROXY : ChallengeState.TARGET);
-        // TODO It is possible that this overwrites existing cached credentials so potentially not ideal.
+        // TODO It is possible that this overwrites existing cached credentials
+        // so potentially not ideal.
         authCache.put(new HttpHost(target.getHost(), target.getPort()), basicAuth);
         httpContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
     }
-    
+
     @Override
     public void invalidate() {
         // Does nothing

Modified: jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/resultset/TestResultSetFormat2.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/resultset/TestResultSetFormat2.java?rev=1578842&r1=1578841&r2=1578842&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/resultset/TestResultSetFormat2.java (original)
+++ jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/resultset/TestResultSetFormat2.java Tue Mar 18 12:29:24 2014
@@ -18,344 +18,329 @@
 
 package com.hp.hpl.jena.sparql.resultset;
 
-import java.io.ByteArrayInputStream ;
+import java.io.ByteArrayInputStream;
 
 import org.junit.Assert;
 
-import org.apache.jena.atlas.lib.StrUtils ;
-import org.junit.Test ;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.junit.Test;
 
-import com.hp.hpl.jena.query.ResultSet ;
-import com.hp.hpl.jena.query.ResultSetFactory ;
+import com.hp.hpl.jena.query.ResultSet;
+import com.hp.hpl.jena.query.ResultSetFactory;
 import com.hp.hpl.jena.sparql.ARQException;
 
-public class TestResultSetFormat2
-{
+public class TestResultSetFormat2 {
     @Test
-    public void resultset_tsv_01()
-    {
+    public void resultset_tsv_01() {
         // Empty Header Row (no variables), no rows.
         parseTSV("\n");
     }
-    
-    @Test 
-    public void resultset_tsv_02()
-    {
+
+    @Test
+    public void resultset_tsv_02() {
         // No vars, one row.
-        String x = "\n\n" ;
+        String x = "\n\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_03()
-    {
+    public void resultset_tsv_03() {
         // One var, one row empty (unbound)
         String x = "?x\n\n";
         parseTSV(x);
     }
 
-    @Test 
-    public void resultset_tsv_04()
-    {
+    @Test
+    public void resultset_tsv_04() {
         // One var, no rows.
-        String x = "?x\n" ;
+        String x = "?x\n";
         parseTSV(x);
     }
 
-    @Test 
-    public void resultset_tsv_05()
-    {
+    @Test
+    public void resultset_tsv_05() {
         // One var, one rows.
-        String x = "?x\n'a'\n" ;
+        String x = "?x\n'a'\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_06()
-    {
-    	// Two vars, one row empty other than the tab separator which is required
-    	// when two or more variables are present
-    	String x = "?x\t?y\n\t\n";
-    	parseTSV(x);
+    public void resultset_tsv_06() {
+        // Two vars, one row empty other than the tab separator which is
+        // required
+        // when two or more variables are present
+        String x = "?x\t?y\n\t\n";
+        parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_07()
-    {
-    	//Three vars, one row of no values
-    	String x = "?x\t?y\t?z\n\t\t";
-    	parseTSV(x);
+    public void resultset_tsv_07() {
+        // Three vars, one row of no values
+        String x = "?x\t?y\t?z\n\t\t";
+        parseTSV(x);
     }
-    
+
     // various values
-    
+
     @Test
-    public void resultset_tsv_08()
-    {
+    public void resultset_tsv_08() {
         String x = "?x\n<http://example/foo>\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_09()
-    {
+    public void resultset_tsv_09() {
         String x = "?x\n_:abc\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_11()
-    {
+    public void resultset_tsv_11() {
         String x = "?x\n123\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_12()
-    {
+    public void resultset_tsv_12() {
         // We allow leading white space.
         String x = "?x\n  123\n";
         parseTSV(x);
     }
-    
+
     @Test
-    public void resultset_tsv_13()
-    {
+    public void resultset_tsv_13() {
         // We allow trailing white space.
         String x = "?x\n123   \n";
         parseTSV(x);
     }
-        
+
     @Test
-    public void resultset_tsv_14()
-    {
+    public void resultset_tsv_14() {
         // We allow trailing white space.
         String x = "?x\n<http://example/>    \n";
         parseTSV(x);
     }
-    
+
+    @Test
+    public void resultset_tsv_boolean_01() {
+        // true is valid
+        String x = "?_askResult\ntrue";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_02() {
+        // true is valid regardless of case
+        String x = "?_askResult\nTRUE";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_03() {
+        // true is valid regardless of case
+        String x = "?_askResult\ntRuE";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_04() {
+        // yes is valid
+        String x = "?_askResult\nyes";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_05() {
+        // yes is valid regardless of case
+        String x = "?_askResult\nYES";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_06() {
+        // yes is valid regardless of case
+        String x = "?_askResult\nyEs";
+        parseTSVAsBoolean(x, true);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_07() {
+        // false is valid
+        String x = "?_askResult\nfalse";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_08() {
+        // false is valid regardless of case
+        String x = "?_askResult\nFALSE";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_09() {
+        // false is valid regardless of case
+        String x = "?_askResult\nfAlSe";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_10() {
+        // no is valid
+        String x = "?_askResult\nno";
+        parseTSVAsBoolean(x, false);
+    }
+
     @Test
-    public void resultset_tsv_boolean_01()
-    {
-    	// true is valid
-    	String x = "?_askResult\ntrue";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_02()
-    {
-    	// true is valid regardless of case
-    	String x = "?_askResult\nTRUE";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_03()
-    {
-    	// true is valid regardless of case
-    	String x = "?_askResult\ntRuE";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_04()
-    {
-    	// yes is valid
-    	String x = "?_askResult\nyes";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_05()
-    {
-    	// yes is valid regardless of case
-    	String x = "?_askResult\nYES";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_06()
-    {
-    	// yes is valid regardless of case
-    	String x = "?_askResult\nyEs";
-    	parseTSVAsBoolean(x, true);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_07()
-    {
-    	// false is valid
-    	String x = "?_askResult\nfalse";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_08()
-    {
-    	// false is valid regardless of case
-    	String x = "?_askResult\nFALSE";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_09()
-    {
-    	// false is valid regardless of case
-    	String x = "?_askResult\nfAlSe";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_10()
-    {
-    	// no is valid
-    	String x = "?_askResult\nno";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_11()
-    {
-    	// no is valid regardless of case
-    	String x = "?_askResult\nNO";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test
-    public void resultset_tsv_boolean_12()
-    {
-    	// no is valid regardless of case
-    	String x = "?_askResult\nnO";
-    	parseTSVAsBoolean(x, false);
-    }
-
-    @Test (expected=ResultSetException.class) 
-    public void resultset_bad_tsv_01()
-    {
+    public void resultset_tsv_boolean_11() {
+        // no is valid regardless of case
+        String x = "?_askResult\nNO";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test
+    public void resultset_tsv_boolean_12() {
+        // no is valid regardless of case
+        String x = "?_askResult\nnO";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_01() {
         // Two vars, row of 3 values.
-        String x = "?x\t?y\n'a'\t'b'\t'c'" ;
+        String x = "?x\t?y\n'a'\t'b'\t'c'";
         parseTSV(x);
     }
 
-    @Test (expected=ResultSetException.class) 
-    public void resultset_bad_tsv_02()
-    {
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_02() {
         // Two vars, row of 1 value only.
-        String x = "?x\t?y\n'a'" ;
+        String x = "?x\t?y\n'a'";
         parseTSV(x);
     }
 
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_03()
-    {
-    	// No input
-    	parseTSV("");
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_03() {
+        // No input
+        parseTSV("");
     }
-    
-    @Test (expected=ResultSetException.class)
-    public void resultset_bad_tsv_04()
-    {
-    	//Two vars but a completely empty row (should contain a tab)
-    	String x = "?x\t?y\n\n";
-    	parseTSV(x);
+
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_04() {
+        // Two vars but a completely empty row (should contain a tab)
+        String x = "?x\t?y\n\n";
+        parseTSV(x);
     }
-    
+
     // various values - broken
-    
-    @Test(expected=ResultSetException.class)
-    public void resultset_bad_tsv_05()
-    {
+
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_05() {
         String x = "?x\n<http://example/";
         parseTSV(x);
     }
-    
-    @Test(expected=ResultSetException.class)
-    public void resultset_bad_tsv_06()
-    {
+
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_06() {
         String x = "?x\n<http://example/ white space >";
         parseTSV(x);
     }
 
-    @Test(expected=ResultSetException.class)
-    public void resultset_bad_tsv_07()
-    {
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_07() {
         String x = "?x\n<<<<http://example/>>>>";
         parseTSV(x);
     }
 
-    @Test (expected=ResultSetException.class)
-    public void resultset_bad_tsv_08()
-    {
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_08() {
         String x = "?x\n_:abc def";
         parseTSV(x);
     }
-    
-    @Test (expected=ResultSetException.class)
-    public void resultset_bad_tsv_09()
-    {
-    	String x = "x\n<http://example.com>";
-    	parseTSV(x);
-    }
-    
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_boolean_01()
-    {
-    	//Not in allowed set of true yes false no
-    	String x = "?_askResults\nblah";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_boolean_02()
-    {
-    	//Missing header
-    	String x = "true";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_boolean_03()
-    {
-    	//Missing boolean
-    	String x = "?_askResult\n";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_boolean_04()
-    {
-    	//A normal result set header
-    	String x = "?x\n";
-    	parseTSVAsBoolean(x, false);
-    }
-    
-    @Test (expected=ARQException.class)
-    public void resultset_bad_tsv_boolean_05()
-    {
-    	//A normal result set header
-    	String x = "?x\t?y\n";
-    	parseTSVAsBoolean(x, false);
-    }
-
-    private void parseTSV(String x)
-    {
-        byte[] b = StrUtils.asUTF8bytes(x) ;
-        ByteArrayInputStream in = new ByteArrayInputStream(b) ;
-        ResultSet rs2 = ResultSetFactory.fromTSV(in) ;
-        
-        while (rs2.hasNext())
-        {
-        	rs2.nextBinding();
+
+    @Test(expected = ResultSetException.class)
+    public void resultset_bad_tsv_09() {
+        String x = "x\n<http://example.com>";
+        parseTSV(x);
+    }
+
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_boolean_01() {
+        // Not in allowed set of true yes false no
+        String x = "?_askResults\nblah";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_boolean_02() {
+        // Missing header
+        String x = "true";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_boolean_03() {
+        // Missing boolean
+        String x = "?_askResult\n";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_boolean_04() {
+        // A normal result set header
+        String x = "?x\n";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test(expected = ARQException.class)
+    public void resultset_bad_tsv_boolean_05() {
+        // A normal result set header
+        String x = "?x\t?y\n";
+        parseTSVAsBoolean(x, false);
+    }
+
+    @Test
+    public void resultset_json_01() {
+        //@formatter:off
+        String input = StrUtils.strjoinNL("{\"head\":{\"vars\":[\"s\"]},",
+                                          "     \"results\": {",
+                                          "      \"bindings\":[",
+                                          "       {\"s\":{\"type\":\"uri\",\"value\":\"http://rdf.myexperiment.org/ontologies/snarm/Policy\"}}",
+                                          "      ]",
+                                          "     },",
+                                          "     \"warnings\": [\"parser warning: Variable o was bound but is unused in the query on line 1\",",
+                                          "     \"parser warning: Variable p was bound but is unused in the query on line 1\"]",
+                                          "    })",
+                                          "    }");
+        //@formatter:on
+        parseJSON(input);
+    }
+
+    private void parseTSV(String x) {
+        byte[] b = StrUtils.asUTF8bytes(x);
+        ByteArrayInputStream in = new ByteArrayInputStream(b);
+        ResultSet rs2 = ResultSetFactory.fromTSV(in);
+
+        while (rs2.hasNext()) {
+            rs2.nextBinding();
+        }
+    }
+
+    private void parseJSON(String input) {
+        byte[] b = StrUtils.asUTF8bytes(input);
+        ByteArrayInputStream in = new ByteArrayInputStream(b);
+        ResultSet rs = ResultSetFactory.fromJSON(in);
+
+        while (rs.hasNext()) {
+            rs.nextBinding();
         }
     }
-    
-    private void parseTSVAsBoolean(String x, boolean expected)
-    {
-    	byte[] b = StrUtils.asUTF8bytes(x);
-    	ByteArrayInputStream in = new ByteArrayInputStream(b);
-    	boolean actual = TSVInput.booleanFromTSV(in);
-    	
-    	Assert.assertEquals(expected, actual);
-    }    
+
+    private void parseTSVAsBoolean(String x, boolean expected) {
+        byte[] b = StrUtils.asUTF8bytes(x);
+        ByteArrayInputStream in = new ByteArrayInputStream(b);
+        boolean actual = TSVInput.booleanFromTSV(in);
+
+        Assert.assertEquals(expected, actual);
+    }
 }