From ba9b6784f98a5b49e55e611a499be11dfa6823b7 Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 2 Aug 2016 19:32:53 -0700 Subject: [PATCH 01/10] [SPARK-16866][SQL] Basic infrastructure for file-based SQL end-to-end tests --- .../resources/sql-tests/inputs/blacklist.sql | 4 + .../sql-tests/inputs/number-format.sql | 13 ++ .../sql-tests/results/number-format.sql.xml | 49 +++++ .../org/apache/spark/sql/SQLQuerySuite.scala | 36 ---- .../apache/spark/sql/SQLQueryTestSuite.scala | 186 ++++++++++++++++++ 5 files changed, 252 insertions(+), 36 deletions(-) create mode 100644 sql/core/src/test/resources/sql-tests/inputs/blacklist.sql create mode 100644 sql/core/src/test/resources/sql-tests/inputs/number-format.sql create mode 100644 sql/core/src/test/resources/sql-tests/results/number-format.sql.xml create mode 100644 sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala diff --git a/sql/core/src/test/resources/sql-tests/inputs/blacklist.sql b/sql/core/src/test/resources/sql-tests/inputs/blacklist.sql new file mode 100644 index 000000000000..d69f8147a526 --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/inputs/blacklist.sql @@ -0,0 +1,4 @@ +-- This is a query file that has been blacklisted. +-- It includes a query that should crash Spark. +-- If the test case is run, the whole suite would fail. +some random not working query that should crash Spark. diff --git a/sql/core/src/test/resources/sql-tests/inputs/number-format.sql b/sql/core/src/test/resources/sql-tests/inputs/number-format.sql new file mode 100644 index 000000000000..2c66828605bb --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/inputs/number-format.sql @@ -0,0 +1,13 @@ +-- Verifies how we parse numbers + +-- parse as ints +select 1, -1; + +-- parse as longs +select 2147483648, -2147483649; + +-- parse as decimals +select 9223372036854775808, -9223372036854775809; + +-- various floating point formats +select 0.3, -0.8, .5, -.18; diff --git a/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml b/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml new file mode 100644 index 000000000000..f7f9f9ae13ec --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala index 8e7c8d7f079f..be5a1872f7c4 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQuerySuite.scala @@ -1358,42 +1358,6 @@ class SQLQuerySuite extends QueryTest with SharedSQLContext { ) } - test("Floating point number format") { - checkAnswer( - sql("SELECT 0.3"), Row(BigDecimal(0.3)) - ) - - checkAnswer( - sql("SELECT -0.8"), Row(BigDecimal(-0.8)) - ) - - checkAnswer( - sql("SELECT .5"), Row(BigDecimal(0.5)) - ) - - checkAnswer( - sql("SELECT -.18"), Row(BigDecimal(-0.18)) - ) - } - - test("Auto cast integer type") { - checkAnswer( - sql(s"SELECT ${Int.MaxValue + 1L}"), Row(Int.MaxValue + 1L) - ) - - checkAnswer( - sql(s"SELECT ${Int.MinValue - 1L}"), Row(Int.MinValue - 1L) - ) - - checkAnswer( - sql("SELECT 9223372036854775808"), Row(new java.math.BigDecimal("9223372036854775808")) - ) - - checkAnswer( - sql("SELECT -9223372036854775809"), Row(new java.math.BigDecimal("-9223372036854775809")) - ) - } - test("Test to check we can apply sign to expression") { checkAnswer( diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala new file mode 100644 index 000000000000..b52fb737fe9c --- /dev/null +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.spark.sql + +import java.io.File +import java.util.{Locale, TimeZone} + +import org.apache.spark.sql.catalyst.rules.RuleExecutor +import org.apache.spark.sql.catalyst.util.{fileToString, stringToFile} +import org.apache.spark.sql.test.SharedSQLContext + +/** + * End-to-end test cases for SQL queries. + * + * Each case is loaded from a file in "spark/sql/core/src/test/resources/sql-tests/inputs". + * Each case has a golden result file in "spark/sql/core/src/test/resources/sql-tests/results". + * + * To re-generate golden files, run: + * {{{ + * SPARK_GENERATE_GOLDEN_FILES=1 build/sbt "sql/test-only *SQLQueryTestSuite" + * }}} + * + * The format for input files is simple: + * 1. A list of SQL queries separated by semicolon. + * 2. Lines starting with -- are treated as comments and ignored. + * + * For example: + * {{{ + * -- this is a comment + * select 1 + 2; + * select current_date; + * }}} + * + * Result files are encoded as XMLs. + */ +class SQLQueryTestSuite extends QueryTest with SharedSQLContext { + + private val regenerateGoldenFiles: Boolean = System.getenv("SPARK_GENERATE_GOLDEN_FILES") == "1" + + private val inputFilePath = "src/test/resources/sql-tests/inputs/" + private val goldenFilePath = "src/test/resources/sql-tests/results/" + + /** List of test cases to ignore, in lower cases. */ + private val blackList = Set( + "blacklist.sql" // Do NOT remove this one. It is here to test the blacklist functionality. + ) + + // Create all the test cases. + listTestCases().foreach(createScalaTestCase) + + /** A test case. */ + private case class TestCase(name: String, inputFile: String, resultFile: String) + + /** A single SQL query's output. */ + private case class QueryOutput(sql: String, schema: String, output: String) { + def toXML: String = { + // We are explicitly not using multi-line string due to stripMargin removing |s, + // and not using XML interpolation because there is no simple way to indent outputs nicely + // (scala.xml.PrettyPrinter has issue with tabs). + "\n" + + s" \n" + + s" \n" + + s" \n" + + s"" + } + } + + private def createScalaTestCase(testCase: TestCase): Unit = { + if (blackList.contains(testCase.name.toLowerCase)) { + // Create a test case to ignore this case. + ignore(testCase.name) { /* Do nothing */ } + } else { + // Create a test case to run this case. + test(testCase.name) { runTest(testCase) } + } + } + + /** Run a test case. */ + private def runTest(testCase: TestCase): Unit = { + val input = fileToString(new File(testCase.inputFile)) + + // List of SQL queries to run + val queries: Seq[String] = { + val cleaned = input.split("\n").filterNot(_.matches("--.*(?<=[^\\\\]);")).mkString("\n") + cleaned.split("(?<=[^\\\\]);").map(_.trim).filterNot(q => q == "").toSeq + } + + // Run the SQL queries preparing them for comparison. + val outputs: Seq[QueryOutput] = queries.map { sql => + val df = spark.sql(sql) + // We might need to do some query canonicalization in the future. + QueryOutput( + sql = sql, + schema = df.schema.map(_.dataType.simpleString).mkString(", "), + output = df.showString(_numRows = 10000, truncate = 10000).trim) + } + + if (regenerateGoldenFiles) { + // If generate golden file flag is on, create the golden file. + // Again, we are explicitly not using multi-line string due to stripMargin removing |s, + // and not using XML interpolation because there is no simple way to indent outputs nicely + // (scala.xml.PrettyPrinter has issue with tabs). + val xmlOutput = { + "\n" + + "\n" + + outputs.map(_.toXML).mkString("\n") + + "\n\n" + } + stringToFile(new File(testCase.resultFile), xmlOutput) + } + + // Read back the golden file. + val expectedOutputs: Seq[QueryOutput] = { + val xml = scala.xml.XML.loadString(fileToString(new File(testCase.resultFile))) + (xml \ "query").map { q => + QueryOutput( + sql = (q \ "sql").text, + schema = (q \ "schema").text, + output = (q \ "output").text.trim) + } + } + + // Compare results. + assertResult(expectedOutputs.size, s"Number of queries should be ${expectedOutputs.size}") { + outputs.size + } + + outputs.zip(expectedOutputs).zipWithIndex.foreach { case ((output, expected), i) => + assertResult(expected.sql, s"SQL query should match for query #$i") { output.sql } + assertResult(expected.schema, s"Schema should match for query #$i") { output.schema } + assertResult(expected.output, s"Result should match for query #$i") { output.output } + } + } + + private def listTestCases(): Seq[TestCase] = { + listFilesRecursively(new File(inputFilePath)).map { file => + val resultFile = file.getAbsolutePath.replace(inputFilePath, goldenFilePath) + ".xml" + TestCase(file.getName, file.getAbsolutePath, resultFile) + } + } + + /** Returns all the files (not directories) in a directory, recursively. */ + private def listFilesRecursively(path: File): Seq[File] = { + val (dirs, files) = path.listFiles().partition(_.isDirectory) + files ++ dirs.flatMap(listFilesRecursively) + } + + private val originalTimeZone = TimeZone.getDefault + private val originalLocale = Locale.getDefault + + override def beforeAll(): Unit = { + super.beforeAll() + // Timezone is fixed to America/Los_Angeles for those timezone sensitive tests (timestamp_*) + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")) + // Add Locale setting + Locale.setDefault(Locale.US) + RuleExecutor.resetTime() + } + + override def afterAll(): Unit = { + try { + TimeZone.setDefault(originalTimeZone) + Locale.setDefault(originalLocale) + + // For debugging dump some statistics about how much time was spent in various optimizer rules + logWarning(RuleExecutor.dumpTimeSpent()) + } finally { + super.afterAll() + } + } +} From 9b360dad38b11c66869a1a0a76783c747ca423fb Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 2 Aug 2016 19:41:04 -0700 Subject: [PATCH 02/10] rat exclude --- dev/.rat-excludes | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/.rat-excludes b/dev/.rat-excludes index 0c866717a3f4..e35fdf22034b 100644 --- a/dev/.rat-excludes +++ b/dev/.rat-excludes @@ -99,4 +99,5 @@ spark-deps-.* .*tsv org.apache.spark.scheduler.ExternalClusterManager .*\.sql +.*\.sql\.xml .Rbuildignore From a1e1b578cd8f7aa45fd0db107b194c500284ae79 Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Wed, 3 Aug 2016 13:17:32 -0700 Subject: [PATCH 03/10] Use a more concise and human readable format. --- .../sql-tests/results/number-format.sql.out | 50 +++++++++++++ .../sql-tests/results/number-format.sql.xml | 49 ------------- .../apache/spark/sql/SQLQueryTestSuite.scala | 73 ++++++++++++------- 3 files changed, 95 insertions(+), 77 deletions(-) create mode 100644 sql/core/src/test/resources/sql-tests/results/number-format.sql.out delete mode 100644 sql/core/src/test/resources/sql-tests/results/number-format.sql.xml diff --git a/sql/core/src/test/resources/sql-tests/results/number-format.sql.out b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out new file mode 100644 index 000000000000..8757119f8f6c --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out @@ -0,0 +1,50 @@ +-- Automatically generated by org.apache.spark.sql.SQLQueryTestSuite +-- Number of queries: 4 + + +-- !query 0 +select 1, -1 +-- !query 0 schema +int, int +-- !query 0 output ++---+----+ +| 1|(-1)| ++---+----+ +| 1| -1| ++---+----+ + + +-- !query 1 +select 2147483648, -2147483649 +-- !query 1 schema +bigint, bigint +-- !query 1 output ++----------+-------------+ +|2147483648|(-2147483649)| ++----------+-------------+ +|2147483648| -2147483649| ++----------+-------------+ + + +-- !query 2 +select 9223372036854775808, -9223372036854775809 +-- !query 2 schema +decimal(19,0), decimal(19,0) +-- !query 2 output ++-------------------+----------------------+ +|9223372036854775808|(-9223372036854775809)| ++-------------------+----------------------+ +|9223372036854775808| -9223372036854775809| ++-------------------+----------------------+ + + +-- !query 3 +select 0.3, -0.8, .5, -.18 +-- !query 3 schema +decimal(1,1), decimal(1,1), decimal(1,1), decimal(2,2) +-- !query 3 output ++---+------+---+-------+ +|0.3|(-0.8)|0.5|(-0.18)| ++---+------+---+-------+ +|0.3| -0.8|0.5| -0.18| ++---+------+---+-------+ diff --git a/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml b/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml deleted file mode 100644 index f7f9f9ae13ec..000000000000 --- a/sql/core/src/test/resources/sql-tests/results/number-format.sql.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index b52fb737fe9c..3238a97c7dd5 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -42,11 +42,28 @@ import org.apache.spark.sql.test.SharedSQLContext * For example: * {{{ * -- this is a comment - * select 1 + 2; + * select 1, -1; * select current_date; * }}} * - * Result files are encoded as XMLs. + * The format for golden result files look roughly like: + * {{{ + * -- some header information + * + * -- !query 0 + * select 1, -1 + * -- !query 0 schema + * int, int + * -- !query 0 output + * +---+----+ + * | 1|(-1)| + * +---+----+ + * | 1| -1| + * +---+----+ + * + * -- !query 1 + * ... + * }}} */ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { @@ -68,15 +85,14 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { /** A single SQL query's output. */ private case class QueryOutput(sql: String, schema: String, output: String) { - def toXML: String = { - // We are explicitly not using multi-line string due to stripMargin removing |s, - // and not using XML interpolation because there is no simple way to indent outputs nicely - // (scala.xml.PrettyPrinter has issue with tabs). - "\n" + - s" \n" + - s" \n" + - s" \n" + - s"" + def toString(queryIndex: Int): String = { + // We are explicitly not using multi-line string due to stripMargin removing "|" in output. + s"-- !query $queryIndex\n" + + sql + "\n" + + s"-- !query $queryIndex schema\n" + + schema + "\n" + + s"-- !query $queryIndex output\n" + + output } } @@ -96,7 +112,9 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { // List of SQL queries to run val queries: Seq[String] = { - val cleaned = input.split("\n").filterNot(_.matches("--.*(?<=[^\\\\]);")).mkString("\n") + // val cleaned = input.split("\n").filterNot(_.matches("--.*(?<=[^\\\\]);")).mkString("\n") + val cleaned = input.split("\n").filterNot(_.startsWith("--")).mkString("\n") + // note: this is not a robust way to split queries using semicolon, but works for now. cleaned.split("(?<=[^\\\\]);").map(_.trim).filterNot(q => q == "").toSeq } @@ -111,27 +129,26 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { } if (regenerateGoldenFiles) { - // If generate golden file flag is on, create the golden file. - // Again, we are explicitly not using multi-line string due to stripMargin removing |s, - // and not using XML interpolation because there is no simple way to indent outputs nicely - // (scala.xml.PrettyPrinter has issue with tabs). - val xmlOutput = { - "\n" + - "\n" + - outputs.map(_.toXML).mkString("\n") + - "\n\n" + // Again, we are explicitly not using multi-line string due to stripMargin removing "|". + val goldenOutput = { + s"-- Automatically generated by ${getClass.getName}\n" + + s"-- Number of queries: ${outputs.size}\n\n\n" + + outputs.zipWithIndex.map{case (qr, i) => qr.toString(i)}.mkString("\n\n\n") + "\n" } - stringToFile(new File(testCase.resultFile), xmlOutput) + stringToFile(new File(testCase.resultFile), goldenOutput) } // Read back the golden file. val expectedOutputs: Seq[QueryOutput] = { - val xml = scala.xml.XML.loadString(fileToString(new File(testCase.resultFile))) - (xml \ "query").map { q => + val goldenOutput = fileToString(new File(testCase.resultFile)) + val segments = goldenOutput.split("-- !query.+\n") + assert(segments.size == outputs.size * 3 + 1) // each query has 3 segments, plus the header + Seq.tabulate(outputs.size) { i => QueryOutput( - sql = (q \ "sql").text, - schema = (q \ "schema").text, - output = (q \ "output").text.trim) + sql = segments(i * 3 + 1).trim, + schema = segments(i * 3 + 2).trim, + output = segments(i * 3 + 3).trim + ) } } @@ -149,7 +166,7 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { private def listTestCases(): Seq[TestCase] = { listFilesRecursively(new File(inputFilePath)).map { file => - val resultFile = file.getAbsolutePath.replace(inputFilePath, goldenFilePath) + ".xml" + val resultFile = file.getAbsolutePath.replace(inputFilePath, goldenFilePath) + ".out" TestCase(file.getName, file.getAbsolutePath, resultFile) } } From 2352d6f2005bce8c241bc55a8668e1968f65d450 Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Sat, 6 Aug 2016 07:42:39 +0800 Subject: [PATCH 04/10] rat exclude --- dev/.rat-excludes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/.rat-excludes b/dev/.rat-excludes index e35fdf22034b..dee8098ba29a 100644 --- a/dev/.rat-excludes +++ b/dev/.rat-excludes @@ -99,5 +99,5 @@ spark-deps-.* .*tsv org.apache.spark.scheduler.ExternalClusterManager .*\.sql -.*\.sql\.xml +.*\.sql\.out .Rbuildignore From 0359756e0d3aca8136feb3496bf761ea620b8503 Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 9 Aug 2016 14:36:54 -0700 Subject: [PATCH 05/10] code review --- dev/.rat-excludes | 1 - .../sql-tests/results/number-format.sql.out | 32 +++++-------------- .../apache/spark/sql/SQLQueryTestSuite.scala | 4 +-- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/dev/.rat-excludes b/dev/.rat-excludes index dee8098ba29a..0c866717a3f4 100644 --- a/dev/.rat-excludes +++ b/dev/.rat-excludes @@ -99,5 +99,4 @@ spark-deps-.* .*tsv org.apache.spark.scheduler.ExternalClusterManager .*\.sql -.*\.sql\.out .Rbuildignore diff --git a/sql/core/src/test/resources/sql-tests/results/number-format.sql.out b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out index 8757119f8f6c..3c3fcbd91b91 100644 --- a/sql/core/src/test/resources/sql-tests/results/number-format.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out @@ -5,46 +5,30 @@ -- !query 0 select 1, -1 -- !query 0 schema -int, int +struct<1:int,(-1):int> -- !query 0 output -+---+----+ -| 1|(-1)| -+---+----+ -| 1| -1| -+---+----+ +1 -1 -- !query 1 select 2147483648, -2147483649 -- !query 1 schema -bigint, bigint +struct<2147483648:bigint,(-2147483649):bigint> -- !query 1 output -+----------+-------------+ -|2147483648|(-2147483649)| -+----------+-------------+ -|2147483648| -2147483649| -+----------+-------------+ +2147483648 -2147483649 -- !query 2 select 9223372036854775808, -9223372036854775809 -- !query 2 schema -decimal(19,0), decimal(19,0) +struct<9223372036854775808:decimal(19,0),(-9223372036854775809):decimal(19,0)> -- !query 2 output -+-------------------+----------------------+ -|9223372036854775808|(-9223372036854775809)| -+-------------------+----------------------+ -|9223372036854775808| -9223372036854775809| -+-------------------+----------------------+ +9223372036854775808 -9223372036854775809 -- !query 3 select 0.3, -0.8, .5, -.18 -- !query 3 schema -decimal(1,1), decimal(1,1), decimal(1,1), decimal(2,2) +struct<0.3:decimal(1,1),(-0.8):decimal(1,1),0.5:decimal(1,1),(-0.18):decimal(2,2)> -- !query 3 output -+---+------+---+-------+ -|0.3|(-0.8)|0.5|(-0.18)| -+---+------+---+-------+ -|0.3| -0.8|0.5| -0.18| -+---+------+---+-------+ +0.3 -0.8 0.5 -0.18 diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index 3238a97c7dd5..56cf6ea4bf2f 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -124,8 +124,8 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { // We might need to do some query canonicalization in the future. QueryOutput( sql = sql, - schema = df.schema.map(_.dataType.simpleString).mkString(", "), - output = df.showString(_numRows = 10000, truncate = 10000).trim) + schema = df.schema.catalogString, + output = df.queryExecution.hiveResultString().mkString("\n")) } if (regenerateGoldenFiles) { From 5eb01fe1f014941f1fe9154dee03b7326453315f Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 9 Aug 2016 15:45:30 -0700 Subject: [PATCH 06/10] make it work for maven --- .../resources/sql-tests/inputs/number-format.sql | 4 ++-- .../sql-tests/results/number-format.sql.out | 6 +++--- .../org/apache/spark/sql/SQLQueryTestSuite.scala | 15 +++++++++++++-- .../sql/catalyst/LogicalPlanToSQLSuite.scala | 3 +-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/number-format.sql b/sql/core/src/test/resources/sql-tests/inputs/number-format.sql index 2c66828605bb..60076a843158 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/number-format.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/number-format.sql @@ -9,5 +9,5 @@ select 2147483648, -2147483649; -- parse as decimals select 9223372036854775808, -9223372036854775809; --- various floating point formats -select 0.3, -0.8, .5, -.18; +-- various floating point (decimal) formats +select 0.3, -0.8, .5, -.18, 0.1111; diff --git a/sql/core/src/test/resources/sql-tests/results/number-format.sql.out b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out index 3c3fcbd91b91..4b800b7d9256 100644 --- a/sql/core/src/test/resources/sql-tests/results/number-format.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/number-format.sql.out @@ -27,8 +27,8 @@ struct<9223372036854775808:decimal(19,0),(-9223372036854775809):decimal(19,0)> -- !query 3 -select 0.3, -0.8, .5, -.18 +select 0.3, -0.8, .5, -.18, 0.1111 -- !query 3 schema -struct<0.3:decimal(1,1),(-0.8):decimal(1,1),0.5:decimal(1,1),(-0.18):decimal(2,2)> +struct<0.3:decimal(1,1),(-0.8):decimal(1,1),0.5:decimal(1,1),(-0.18):decimal(2,2),0.1111:decimal(4,4)> -- !query 3 output -0.3 -0.8 0.5 -0.18 +0.3 -0.8 0.5 -0.18 0.1111 diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index 56cf6ea4bf2f..edb266fe92f0 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -69,8 +69,19 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { private val regenerateGoldenFiles: Boolean = System.getenv("SPARK_GENERATE_GOLDEN_FILES") == "1" - private val inputFilePath = "src/test/resources/sql-tests/inputs/" - private val goldenFilePath = "src/test/resources/sql-tests/results/" + private val baseResourcePath = { + // If regenerateGoldenFiles is true, we must be running this in SBT and we use hard-coded + // relative path. Otherwise, we use classloader's getResource to find the location. + if (regenerateGoldenFiles) { + java.nio.file.Paths.get("src", "test", "resources", "sql-tests").toFile + } else { + val res = getClass.getClassLoader.getResource("sql-tests") + new File(res.getFile) + } + } + + private val inputFilePath = new File(baseResourcePath, "inputs").getAbsolutePath + private val goldenFilePath = new File(baseResourcePath, "results").getAbsolutePath /** List of test cases to ignore, in lower cases. */ private val blackList = Set( diff --git a/sql/hive/src/test/scala/org/apache/spark/sql/catalyst/LogicalPlanToSQLSuite.scala b/sql/hive/src/test/scala/org/apache/spark/sql/catalyst/LogicalPlanToSQLSuite.scala index d8ab864ca6fc..4e5a51155def 100644 --- a/sql/hive/src/test/scala/org/apache/spark/sql/catalyst/LogicalPlanToSQLSuite.scala +++ b/sql/hive/src/test/scala/org/apache/spark/sql/catalyst/LogicalPlanToSQLSuite.scala @@ -41,8 +41,7 @@ class LogicalPlanToSQLSuite extends SQLBuilderTest with SQLTestUtils { import testImplicits._ // Used for generating new query answer files by saving - private val regenerateGoldenFiles: Boolean = - Option(System.getenv("SPARK_GENERATE_GOLDEN_FILES")) == Some("1") + private val regenerateGoldenFiles: Boolean = System.getenv("SPARK_GENERATE_GOLDEN_FILES") == "1" private val goldenSQLPath = "src/test/resources/sqlgen/" protected override def beforeAll(): Unit = { From 26c77715b469803c13cc67dd40b8dbbadd44516d Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 9 Aug 2016 23:02:46 -0700 Subject: [PATCH 07/10] Update example --- .../scala/org/apache/spark/sql/SQLQueryTestSuite.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index edb266fe92f0..e4624966b5ca 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -53,13 +53,11 @@ import org.apache.spark.sql.test.SharedSQLContext * -- !query 0 * select 1, -1 * -- !query 0 schema - * int, int + * struct<...schema...> * -- !query 0 output - * +---+----+ - * | 1|(-1)| - * +---+----+ - * | 1| -1| - * +---+----+ + * ... data row 1 ... + * ... data row 2 ... + * ... * * -- !query 1 * ... From 7497742617420da77932fe20fff3d422b1a52fde Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 9 Aug 2016 23:04:38 -0700 Subject: [PATCH 08/10] Simplify filter --- .../src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index e4624966b5ca..cb0cf619202d 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -124,7 +124,7 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { // val cleaned = input.split("\n").filterNot(_.matches("--.*(?<=[^\\\\]);")).mkString("\n") val cleaned = input.split("\n").filterNot(_.startsWith("--")).mkString("\n") // note: this is not a robust way to split queries using semicolon, but works for now. - cleaned.split("(?<=[^\\\\]);").map(_.trim).filterNot(q => q == "").toSeq + cleaned.split("(?<=[^\\\\]);").map(_.trim).filter(_ != "").toSeq } // Run the SQL queries preparing them for comparison. From 14f4959edc6163568fa05f546cebb736e7b3985d Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Tue, 9 Aug 2016 23:18:56 -0700 Subject: [PATCH 09/10] cr --- .../src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index cb0cf619202d..81af67e8486c 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -121,7 +121,6 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { // List of SQL queries to run val queries: Seq[String] = { - // val cleaned = input.split("\n").filterNot(_.matches("--.*(?<=[^\\\\]);")).mkString("\n") val cleaned = input.split("\n").filterNot(_.startsWith("--")).mkString("\n") // note: this is not a robust way to split queries using semicolon, but works for now. cleaned.split("(?<=[^\\\\]);").map(_.trim).filter(_ != "").toSeq From 288b699cd1f814c0e20b9b8838f57ec768987e18 Mon Sep 17 00:00:00 2001 From: petermaxlee Date: Wed, 10 Aug 2016 00:33:47 -0700 Subject: [PATCH 10/10] better message for assert. --- .../test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala index 81af67e8486c..08b8432d68eb 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/SQLQueryTestSuite.scala @@ -150,7 +150,11 @@ class SQLQueryTestSuite extends QueryTest with SharedSQLContext { val expectedOutputs: Seq[QueryOutput] = { val goldenOutput = fileToString(new File(testCase.resultFile)) val segments = goldenOutput.split("-- !query.+\n") - assert(segments.size == outputs.size * 3 + 1) // each query has 3 segments, plus the header + + // each query has 3 segments, plus the header + assert(segments.size == outputs.size * 3 + 1, + s"Expected ${outputs.size * 3 + 1} blocks in result file but got ${segments.size}. " + + s"Try regenerate the result files.") Seq.tabulate(outputs.size) { i => QueryOutput( sql = segments(i * 3 + 1).trim,