Strabon

changeset 975:11e81ae216cb

Finished a simple store test. It may be needed to delete and import again the project because pom.xml files have been changed.
author Panayiotis Smeros <psmeros@di.uoa.gr>
date Thu Apr 04 17:16:15 2013 +0300 (2013-04-04)
parents f658d2b036d9
children f880f694108f
files testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TemplateTest.java testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TemplateTests.java testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TestStore.java testsuite/src/test/resources/TestStore.nt testsuite/src/test/resources/TestStore.rq testsuite/src/test/resources/TestStore.sr
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TemplateTest.java	Thu Apr 04 17:16:15 2013 +0300
     1.3 @@ -0,0 +1,159 @@
     1.4 +/**
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.8 + * 
     1.9 + * Copyright (C) 2010, 2011, 2012, 2013 Pyravlos Team
    1.10 + * 
    1.11 + * http://www.strabon.di.uoa.gr/
    1.12 + */
    1.13 +package eu.earthobservatory.runtime.postgis;
    1.14 +
    1.15 +import static org.junit.Assert.assertEquals;
    1.16 +import java.io.BufferedReader;
    1.17 +import java.io.IOException;
    1.18 +import java.io.InputStream;
    1.19 +import java.io.InputStreamReader;
    1.20 +import java.sql.Connection;
    1.21 +import java.sql.DriverManager;
    1.22 +import java.sql.PreparedStatement;
    1.23 +import java.sql.ResultSet;
    1.24 +import java.sql.SQLException;
    1.25 +import java.util.ArrayList;
    1.26 +import java.util.Iterator;
    1.27 +import java.util.Properties;
    1.28 +import org.openrdf.query.MalformedQueryException;
    1.29 +import org.openrdf.query.QueryEvaluationException;
    1.30 +import org.openrdf.query.TupleQueryResultHandlerException;
    1.31 +import org.openrdf.repository.RepositoryException;
    1.32 +import org.openrdf.rio.RDFHandlerException;
    1.33 +import org.openrdf.rio.RDFParseException;
    1.34 +import eu.earthobservatory.runtime.generaldb.InvalidDatasetFormatFault;
    1.35 +import eu.earthobservatory.runtime.postgis.Strabon;
    1.36 +
    1.37 +/**
    1.38 + * A template class with useful methods for the tests.
    1.39 + * 
    1.40 + * @author Panayiotis Smeros <psmeros@di.uoa.gr>
    1.41 + */
    1.42 +public class TemplateTest
    1.43 +{	
    1.44 +	public static String databaseTemplateName = null;
    1.45 +	public static String defaultUser = null;
    1.46 +	public static String serverName = null;
    1.47 +	public static String username = null;
    1.48 +	public static String password = null;
    1.49 +	public static Integer port = null;
    1.50 +	
    1.51 +	public static Connection conn = null;
    1.52 +	public static String databaseName = null;
    1.53 +	
    1.54 +	private static Strabon strabon = null;
    1.55 +	
    1.56 +	public static void createdb() throws Exception
    1.57 +	{
    1.58 +		String url="";
    1.59 +		ArrayList<String> databases=new ArrayList<String>();
    1.60 +        PreparedStatement pst = null;
    1.61 +		
    1.62 +		//Read properties
    1.63 +		Properties properties = new Properties();
    1.64 +		InputStream propertiesStream =  TemplateTest.class.getResourceAsStream("/databases.properties");
    1.65 +		properties.load(propertiesStream);
    1.66 +
    1.67 +		databaseTemplateName = properties.getProperty("postgis.databaseTemplateName");
    1.68 +		defaultUser = properties.getProperty("postgis.defaultUser");
    1.69 +		serverName = properties.getProperty("postgis.serverName");
    1.70 +		username = properties.getProperty("postgis.username");
    1.71 +		password = properties.getProperty("postgis.password");
    1.72 +		port = Integer.parseInt(properties.getProperty("postgis.port"));
    1.73 +
    1.74 +		//Connect to server and create the temp database
    1.75 +		url = "jdbc:postgresql://"+serverName+":"+port+"/"+defaultUser;
    1.76 +		conn = DriverManager.getConnection(url, username, password);
    1.77 +		
    1.78 +        pst = conn.prepareStatement("SELECT * FROM pg_catalog.pg_database");
    1.79 +        ResultSet rs = pst.executeQuery();
    1.80 +
    1.81 +        while (rs.next())
    1.82 +        {
    1.83 +        	databases.add(rs.getString(1));
    1.84 +        }
    1.85 +        rs.close();
    1.86 +        pst.close();
    1.87 +   
    1.88 +        databaseName="teststrabon"+(int)(Math.random()*10000);
    1.89 +        while(databases.contains(databaseName))
    1.90 +        {
    1.91 +        	databaseName+="0";
    1.92 +        }
    1.93 +    
    1.94 +		pst = conn.prepareStatement("CREATE DATABASE "+databaseName+" TEMPLATE " + databaseTemplateName);
    1.95 +		pst.executeUpdate();
    1.96 +		pst.close();
    1.97 +		conn.close();
    1.98 +
    1.99 +		url = "jdbc:postgresql://"+serverName+":"+port+"/"+databaseName;
   1.100 +		conn = DriverManager.getConnection(url, username, password);
   1.101 +		
   1.102 +	    strabon = new Strabon(databaseName, username, password, port, serverName, true);
   1.103 +	}
   1.104 +	
   1.105 +	public static void storeDataset(String datasetFile) throws RDFParseException, RepositoryException, RDFHandlerException, IOException, InvalidDatasetFormatFault
   1.106 +	{
   1.107 +	    if(datasetFile.endsWith(".nt"))
   1.108 +	    	strabon.storeInRepo(datasetFile, "NTRIPLES");
   1.109 +	    else if(datasetFile.endsWith(".nq"))
   1.110 +	    	strabon.storeInRepo(datasetFile, "NQUADS");
   1.111 +	}
   1.112 +	
   1.113 +	public static void testQuery(String queryFile, String resultsFile) throws IOException, MalformedQueryException, QueryEvaluationException, TupleQueryResultHandlerException
   1.114 +	{
   1.115 +		BufferedReader queryReader = new BufferedReader(new InputStreamReader(TemplateTest.class.getResourceAsStream(queryFile)));
   1.116 +		BufferedReader resultsReader = new BufferedReader(new InputStreamReader(TemplateTest.class.getResourceAsStream(resultsFile)));
   1.117 +		String query="";
   1.118 +		ArrayList<String> actualResults = new ArrayList<String>();
   1.119 +		ArrayList<String> expectedResults = new ArrayList<String>();
   1.120 +		
   1.121 +		while (queryReader.ready())
   1.122 +		{
   1.123 +			query+=queryReader.readLine()+"\n";
   1.124 +		}
   1.125 +		actualResults = (ArrayList<String>) strabon.query(query,strabon.getSailRepoConnection());
   1.126 +		
   1.127 +		while (resultsReader.ready())
   1.128 +		{
   1.129 +			expectedResults.add(resultsReader.readLine());
   1.130 +		}
   1.131 +		
   1.132 +		//Check if the actual result set is the same as the expected one
   1.133 +		assertEquals("Actual result set is not the same as the expected one.", expectedResults.size(), actualResults.size());
   1.134 +		Iterator<String> expectedResultsIterator = expectedResults.iterator();
   1.135 +		Iterator<String> actualResultsIterator = actualResults.iterator();
   1.136 +		
   1.137 +		while(expectedResultsIterator.hasNext() && actualResultsIterator.hasNext())
   1.138 +		{
   1.139 +			String eResults = expectedResultsIterator.next();
   1.140 +			String aResults = actualResultsIterator.next();	
   1.141 +			
   1.142 +			//Replace all the names of the variables with "?"
   1.143 +			aResults = aResults.replaceAll("[[A-Z][a-z][0-9]]*=", "?=");
   1.144 +			assertEquals("Actual result set is not the same as the expected one.", aResults, eResults);
   1.145 +		}
   1.146 +	}
   1.147 +	
   1.148 +	public static void dropdb() throws SQLException
   1.149 +	{
   1.150 +		strabon.close();
   1.151 +		
   1.152 +		//Drop the temp database
   1.153 +		conn.close();
   1.154 +		String url = "jdbc:postgresql://"+serverName+":"+port+"/"+defaultUser;
   1.155 +		conn = DriverManager.getConnection(url, username, password);
   1.156 +		
   1.157 +		PreparedStatement pst = conn.prepareStatement("DROP DATABASE "+databaseName);
   1.158 +		pst.executeUpdate();
   1.159 +		pst.close();
   1.160 +		conn.close();
   1.161 +	}
   1.162 +}
     2.1 --- a/testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TemplateTests.java	Wed Apr 03 20:35:45 2013 +0300
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,113 +0,0 @@
     2.4 -/**
     2.5 - * This Source Code Form is subject to the terms of the Mozilla Public
     2.6 - * License, v. 2.0. If a copy of the MPL was not distributed with this
     2.7 - * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     2.8 - * 
     2.9 - * Copyright (C) 2010, 2011, 2012, 2013 Pyravlos Team
    2.10 - * 
    2.11 - * http://www.strabon.di.uoa.gr/
    2.12 - */
    2.13 -package eu.earthobservatory.runtime.postgis;
    2.14 -
    2.15 -import static org.junit.Assert.assertNull;
    2.16 -import java.io.InputStream;
    2.17 -import java.sql.Connection;
    2.18 -import java.sql.DriverManager;
    2.19 -import java.sql.PreparedStatement;
    2.20 -import java.sql.ResultSet;
    2.21 -import java.sql.SQLException;
    2.22 -import java.util.ArrayList;
    2.23 -import java.util.Properties;
    2.24 -import eu.earthobservatory.runtime.postgis.Strabon;
    2.25 -
    2.26 -/**
    2.27 - * A template class with useful methods for the tests.  
    2.28 - * 
    2.29 - * @author Panayiotis Smeros <psmeros@di.uoa.gr>
    2.30 - */
    2.31 -public class TemplateTests {
    2.32 -	
    2.33 -	public static String databaseTemplateName = null;
    2.34 -	public static String defaultUser = null;
    2.35 -	public static String serverName = null;
    2.36 -	public static String username = null;
    2.37 -	public static String password = null;
    2.38 -	public static Integer port = null;
    2.39 -	
    2.40 -	public static Connection conn = null;
    2.41 -	public static String databaseName = null;
    2.42 -	
    2.43 -	public static Strabon beforeClass(String datasetFile) throws Exception
    2.44 -	{
    2.45 -		String url="";
    2.46 -		ArrayList<String> databases=new ArrayList<String>();
    2.47 -        PreparedStatement pst = null;
    2.48 -		
    2.49 -		// Read properties
    2.50 -		Properties properties = new Properties();
    2.51 -		InputStream propertiesStream =  TemplateTests.class.getResourceAsStream("/databases.properties");
    2.52 -		properties.load(propertiesStream);
    2.53 -
    2.54 -		databaseTemplateName = properties.getProperty("postgis.databaseTemplateName");;
    2.55 -		defaultUser = properties.getProperty("postgis.defaultUser");
    2.56 -		serverName = properties.getProperty("postgis.serverName");
    2.57 -		username = properties.getProperty("postgis.username");
    2.58 -		password = properties.getProperty("postgis.password");
    2.59 -		port = Integer.parseInt(properties.getProperty("postgis.port"));
    2.60 -
    2.61 -		//Connect to server and create the temp database
    2.62 -		url = "jdbc:postgresql://"+serverName+":"+port+"/"+defaultUser;
    2.63 -		conn = DriverManager.getConnection(url, username, password);
    2.64 -		assertNull(conn.getWarnings());
    2.65 -		
    2.66 -        pst = conn.prepareStatement("SELECT * FROM pg_catalog.pg_database");
    2.67 -        ResultSet rs = pst.executeQuery();
    2.68 -
    2.69 -        while (rs.next()) {
    2.70 -        	databases.add(rs.getString(1));
    2.71 -        }
    2.72 -        rs.close();
    2.73 -        pst.close();
    2.74 -   
    2.75 -        databaseName="teststrabon"+(int)(Math.random()*10000);
    2.76 -        while(databases.contains(databaseName)){
    2.77 -        	databaseName+="0";
    2.78 -        }
    2.79 -        	
    2.80 -        
    2.81 -		pst = conn.prepareStatement("CREATE DATABASE "+databaseName+" TEMPLATE " + databaseTemplateName);
    2.82 -		pst.executeUpdate();
    2.83 -		pst.close();
    2.84 -		conn.close();
    2.85 -
    2.86 -		url = "jdbc:postgresql://"+serverName+":"+port+"/"+databaseName;
    2.87 -		conn = DriverManager.getConnection(url, username, password);
    2.88 -		assertNull(conn.getWarnings());
    2.89 -		
    2.90 -	    Strabon strabon = new eu.earthobservatory.runtime.postgis.Strabon(databaseName, username, password, port, serverName, true);
    2.91 -
    2.92 -	    if(datasetFile.endsWith(".nt"))
    2.93 -	    	strabon.storeInRepo(datasetFile, "NTRIPLES");
    2.94 -	    else if(datasetFile.endsWith(".nq"))
    2.95 -	    	strabon.storeInRepo(datasetFile, "NQUADS");
    2.96 -	    
    2.97 -		return strabon;
    2.98 -	}
    2.99 -	
   2.100 -
   2.101 -	public static void afterClass(Strabon strabon) throws SQLException
   2.102 -	{
   2.103 -		strabon.close();
   2.104 -		
   2.105 -		//Drop the temp database
   2.106 -		conn.close();
   2.107 -		String url = "jdbc:postgresql://"+serverName+":"+port+"/"+defaultUser;
   2.108 -		conn = DriverManager.getConnection(url, username, password);
   2.109 -		assertNull(conn.getWarnings());
   2.110 -		
   2.111 -		PreparedStatement pst = conn.prepareStatement("DROP DATABASE "+databaseName);
   2.112 -		pst.executeUpdate();
   2.113 -		pst.close();
   2.114 -		conn.close();
   2.115 -	}
   2.116 -}
     3.1 --- a/testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TestStore.java	Wed Apr 03 20:35:45 2013 +0300
     3.2 +++ b/testsuite/src/test/java/eu/earthobservatory/runtime/postgis/TestStore.java	Thu Apr 04 17:16:15 2013 +0300
     3.3 @@ -3,34 +3,23 @@
     3.4   * License, v. 2.0. If a copy of the MPL was not distributed with this
     3.5   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     3.6   * 
     3.7 - * Copyright (C) 2010, 2011, 2012, Pyravlos Team
     3.8 + * Copyright (C) 2010, 2011, 2012, 2013 Pyravlos Team
     3.9   * 
    3.10   * http://www.strabon.di.uoa.gr/
    3.11   */
    3.12  package eu.earthobservatory.runtime.postgis;
    3.13  
    3.14 -import static org.junit.Assert.assertTrue;
    3.15 -import java.io.*;
    3.16 -import java.sql.SQLException;
    3.17 -import java.util.ArrayList;
    3.18 -import java.util.Iterator;
    3.19  import org.junit.AfterClass;
    3.20  import org.junit.BeforeClass;
    3.21  import org.junit.Test;
    3.22 -import org.openrdf.query.MalformedQueryException;
    3.23 -import org.openrdf.query.QueryEvaluationException;
    3.24 -import org.openrdf.query.TupleQueryResultHandlerException;
    3.25 -import eu.earthobservatory.runtime.postgis.Strabon;
    3.26  
    3.27  /**
    3.28 - * A set of simple tests on SPARQL query functionality 
    3.29 + * A simple store test. It tests if the input dataset is stored and retrieved correctly.
    3.30   * 
    3.31   * @author Panayiotis Smeros <psmeros@di.uoa.gr
    3.32   */
    3.33 -public class TestStore{
    3.34 -	
    3.35 -	private static Strabon strabon;
    3.36 -	
    3.37 +public class TestStore
    3.38 +{	
    3.39  	private static final String datasetFile = "/TestStore.nt";
    3.40  	private static final String queryFile = "/TestStore.rq";
    3.41  	private static final String resultsFile = "/TestStore.sr";
    3.42 @@ -38,38 +27,19 @@
    3.43  	@BeforeClass
    3.44  	public static void beforeClass() throws Exception
    3.45  	{
    3.46 -		strabon = TemplateTests.beforeClass(datasetFile);
    3.47 +		TemplateTest.createdb();
    3.48 +		TemplateTest.storeDataset(datasetFile);
    3.49  	}
    3.50  	
    3.51  	@Test
    3.52 -	public void test() throws IOException, MalformedQueryException, QueryEvaluationException, TupleQueryResultHandlerException
    3.53 +	public void test() throws Exception
    3.54  	{
    3.55 -		BufferedReader queryReader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(queryFile)));
    3.56 -		BufferedReader resultsReader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(resultsFile)));
    3.57 -		String query="";
    3.58 -		
    3.59 -		while (queryReader.ready()) 
    3.60 -		{
    3.61 -			query+=queryReader.readLine()+"\n";
    3.62 -		}
    3.63 -
    3.64 -		ArrayList<String> bindings = (ArrayList<String>) strabon.query(query,strabon.getSailRepoConnection());
    3.65 -		
    3.66 -		Iterator<String> iterator = bindings.iterator();
    3.67 -		while(iterator.hasNext() && resultsReader.ready())
    3.68 -		{
    3.69 -			String binding = iterator.next();
    3.70 -			//System.out.println(binding);
    3.71 -			binding=binding.replaceAll("[[A-Z][a-z][0-9]]*=", "?=");
    3.72 -			assertTrue(resultsReader.readLine().equals(binding));
    3.73 -			//System.out.println(binding);
    3.74 -		}
    3.75 -
    3.76 +		TemplateTest.testQuery(queryFile, resultsFile);
    3.77  	}
    3.78  	
    3.79  	@AfterClass
    3.80 -	public static void afterClass() throws SQLException
    3.81 +	public static void afterClass() throws Exception
    3.82  	{
    3.83 -		TemplateTests.afterClass(strabon);
    3.84 +		TemplateTest.dropdb();
    3.85  	}
    3.86  }
     4.1 --- a/testsuite/src/test/resources/TestStore.nt	Wed Apr 03 20:35:45 2013 +0300
     4.2 +++ b/testsuite/src/test/resources/TestStore.nt	Thu Apr 04 17:16:15 2013 +0300
     4.3 @@ -1,8 +1,9 @@
     4.4  <http://example.org/item1> <http://example.org/id> "1"^^<http://www.w3.org/2001/XMLSchema#int>.
     4.5 +<http://example.org/item1> <http://example.org/value> "10"^^<http://www.w3.org/2001/XMLSchema#int>.
     4.6 +<http://example.org/item1> <http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry> "POINT(1 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>.
     4.7  <http://example.org/item2> <http://example.org/id> "2"^^<http://www.w3.org/2001/XMLSchema#int>.
     4.8 +<http://example.org/item2> <http://example.org/value> "20"^^<http://www.w3.org/2001/XMLSchema#int>.
     4.9 +<http://example.org/item2> <http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry> "POINT(2 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>.
    4.10  <http://example.org/item3> <http://example.org/id> "3"^^<http://www.w3.org/2001/XMLSchema#int>.
    4.11 -<http://example.org/item1> <http://example.org/value> "10"^^<http://www.w3.org/2001/XMLSchema#int>.
    4.12 -<http://example.org/item2> <http://example.org/value> "20"^^<http://www.w3.org/2001/XMLSchema#int>.
    4.13 -<http://example.org/item1> <http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry> "POINT(1 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>.
    4.14 -<http://example.org/item2> <http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry> "POINT(2 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>.
    4.15 +<http://example.org/item3> <http://example.org/value> "30"^^<http://www.w3.org/2001/XMLSchema#int>.
    4.16  <http://example.org/item3> <http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry> "POINT(3 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>.
     5.1 --- a/testsuite/src/test/resources/TestStore.rq	Wed Apr 03 20:35:45 2013 +0300
     5.2 +++ b/testsuite/src/test/resources/TestStore.rq	Thu Apr 04 17:16:15 2013 +0300
     5.3 @@ -1,3 +1,3 @@
     5.4  SELECT *
     5.5  WHERE{?s ?p ?o}
     5.6 -ORDER BY ?s
     5.7 \ No newline at end of file
     5.8 +ORDER BY ?s ?p ?o
     5.9 \ No newline at end of file
     6.1 --- a/testsuite/src/test/resources/TestStore.sr	Wed Apr 03 20:35:45 2013 +0300
     6.2 +++ b/testsuite/src/test/resources/TestStore.sr	Thu Apr 04 17:16:15 2013 +0300
     6.3 @@ -1,8 +1,9 @@
     6.4 -[?=http://example.org/item1;?="POINT(1 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>;?=http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry]
     6.5  [?=http://example.org/item1;?="1"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/id]
     6.6  [?=http://example.org/item1;?="10"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/value]
     6.7 +[?=http://example.org/item1;?="POINT(1 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>;?=http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry]
     6.8 +[?=http://example.org/item2;?="2"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/id]
     6.9 +[?=http://example.org/item2;?="20"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/value]
    6.10  [?=http://example.org/item2;?="POINT(2 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>;?=http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry]
    6.11 -[?=http://example.org/item2;?="20"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/value]
    6.12 -[?=http://example.org/item2;?="2"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/id]
    6.13 -[?=http://example.org/item3;?="POINT(3 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>;?=http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry]
    6.14 -[?=http://example.org/item3;?="3"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/id]
    6.15 \ No newline at end of file
    6.16 +[?=http://example.org/item3;?="3"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/id]
    6.17 +[?=http://example.org/item3;?="30"^^<http://www.w3.org/2001/XMLSchema#int>;?=http://example.org/value]
    6.18 +[?=http://example.org/item3;?="POINT(3 0)"^^<http://strdf.di.uoa.gr/ontology#WKT>;?=http://teleios.di.uoa.gr/ontologies/noaOntology.owl#hasGeometry]
    6.19 \ No newline at end of file