This is an automated email from the ASF dual-hosted git repository.
hongze pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git
The following commit(s) were added to refs/heads/main by this push:
new 1c505dfd7 [VL] Gluten-it: Improve test report table format for
parameterized test (#6052)
1c505dfd7 is described below
commit 1c505dfd7cec48d04c37bbf714875883969cb1d7
Author: Hongze Zhang <[email protected]>
AuthorDate: Wed Jun 12 17:13:31 2024 +0800
[VL] Gluten-it: Improve test report table format for parameterized test
(#6052)
---
.../gluten/integration/command/Parameterized.java | 2 +-
.../gluten/integration/action/Parameterized.scala | 222 +++++++++++++--------
.../apache/gluten/integration/action/Queries.scala | 6 +-
.../gluten/integration/action/QueriesCompare.scala | 46 ++---
.../gluten/integration/action/TableRender.scala | 40 +++-
.../apache/gluten/integration/action/package.scala | 36 ++++
.../integration/action/TableRenderTest.scala | 21 ++
7 files changed, 243 insertions(+), 130 deletions(-)
diff --git
a/tools/gluten-it/common/src/main/java/org/apache/gluten/integration/command/Parameterized.java
b/tools/gluten-it/common/src/main/java/org/apache/gluten/integration/command/Parameterized.java
index 7e1234e76..cadff0a2d 100644
---
a/tools/gluten-it/common/src/main/java/org/apache/gluten/integration/command/Parameterized.java
+++
b/tools/gluten-it/common/src/main/java/org/apache/gluten/integration/command/Parameterized.java
@@ -65,7 +65,7 @@ public class Parameterized implements Callable<Integer> {
@Override
public Integer call() throws Exception {
- final Map<String, Map<String, List<Map.Entry<String, String>>>> parsed =
new HashMap<>();
+ final Map<String, Map<String, List<Map.Entry<String, String>>>> parsed =
new LinkedHashMap<>();
final Seq<scala.collection.immutable.Set<DimKv>> excludedCombinations =
JavaConverters.asScalaBufferConverter(Arrays.stream(excludedDims).map(d -> {
final Matcher m = excludedDimsPattern.matcher(d);
diff --git
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Parameterized.scala
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Parameterized.scala
index 8f5bc0946..e2fc526ce 100644
---
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Parameterized.scala
+++
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Parameterized.scala
@@ -18,12 +18,14 @@ package org.apache.gluten.integration.action
import org.apache.commons.lang3.exception.ExceptionUtils
import org.apache.gluten.integration.action.Actions.QuerySelector
+import org.apache.gluten.integration.action.TableRender.Field
import
org.apache.gluten.integration.action.TableRender.RowParser.FieldAppender.RowAppender
import org.apache.gluten.integration.stat.RamStat
import org.apache.gluten.integration.{QueryRunner, Suite, TableCreator}
import org.apache.spark.sql.ConfUtils.ConfImplicits._
import org.apache.spark.sql.SparkSessionSwitcher
+import java.util.concurrent.atomic.AtomicInteger
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
@@ -39,6 +41,8 @@ class Parameterized(
metrics: Array[String])
extends Action {
+ validateDims(configDimensions)
+
private def validateDims(configDimensions: Seq[Dim]): Unit = {
if (configDimensions
.map(dim => {
@@ -57,32 +61,33 @@ class Parameterized(
}
private val coordinates: mutable.LinkedHashMap[Coordinate, Seq[(String,
String)]] = {
- validateDims(configDimensions)
val dimCount = configDimensions.size
val coordinateMap = mutable.LinkedHashMap[Coordinate, Seq[(String,
String)]]()
+ val nextId: AtomicInteger = new AtomicInteger(1);
def fillCoordinates(
dimOffset: Int,
- intermediateCoordinates: Map[String, String],
+ intermediateCoordinate: Map[String, String],
intermediateConf: Seq[(String, String)]): Unit = {
if (dimOffset == dimCount) {
// we got one coordinate
excludedCombinations.foreach { ec: Set[DimKv] =>
if (ec.forall { kv =>
- intermediateCoordinates.contains(kv.k) &&
intermediateCoordinates(kv.k) == kv.v
+ intermediateCoordinate.contains(kv.k) &&
intermediateCoordinate(kv.k) == kv.v
}) {
- println(s"Coordinate ${Coordinate(intermediateCoordinates)}
excluded by $ec.")
+ println(s"Coordinate ${intermediateCoordinate} excluded by $ec.")
return
}
}
- coordinateMap(Coordinate(intermediateCoordinates)) = intermediateConf
+ coordinateMap(Coordinate(nextId.getAndIncrement(),
intermediateCoordinate)) =
+ intermediateConf
return
}
val dim = configDimensions(dimOffset)
dim.dimValues.foreach { dimValue =>
fillCoordinates(
dimOffset + 1,
- intermediateCoordinates + (dim.name -> dimValue.name),
+ intermediateCoordinate + (dim.name -> dimValue.name),
intermediateConf ++ dimValue.conf)
}
}
@@ -95,7 +100,6 @@ class Parameterized(
override def execute(suite: Suite): Boolean = {
val runner: QueryRunner =
new QueryRunner(suite.queryResource(), suite.dataWritePath(scale,
genPartitionedData))
- val allQueries = suite.allQueryIds()
val sessionSwitcher = suite.sessionSwitcher
val testConf = suite.getTestConf()
@@ -116,36 +120,40 @@ class Parameterized(
val runQueryIds = queries.select(suite)
- // warm up
- (0 until warmupIterations).foreach { _ =>
- runQueryIds.foreach { queryId =>
- Parameterized.warmUp(suite.tableCreator(), queryId, suite.desc(),
sessionSwitcher, runner)
- }
- }
-
- val results = coordinates.flatMap { entry =>
- val coordinate = entry._1
- val coordinateResults = (0 until iterations).flatMap { iteration =>
- println(s"Running tests (iteration $iteration) with coordinate
$coordinate...")
- runQueryIds.map { queryId =>
- Parameterized.runQuery(
- runner,
- suite.tableCreator(),
- sessionSwitcher,
+ val results = (0 until iterations).flatMap { iteration =>
+ runQueryIds.map { queryId =>
+ val queryResult =
+ TestResultLine(
queryId,
- coordinate,
- suite.desc(),
- explain,
- metrics)
- }
- }.toList
- coordinateResults
+ coordinates.map { entry =>
+ val coordinate = entry._1
+ println(s"Running tests (iteration $iteration) with coordinate
$coordinate...")
+ // warm up
+ (0 until warmupIterations).foreach { _ =>
+ Parameterized.warmUp(
+ runner,
+ suite.tableCreator(),
+ sessionSwitcher,
+ queryId,
+ suite.desc())
+ }
+ // run
+ Parameterized.runQuery(
+ runner,
+ suite.tableCreator(),
+ sessionSwitcher,
+ queryId,
+ coordinate,
+ suite.desc(),
+ explain,
+ metrics)
+ }.toList)
+ queryResult
+ }
}
- val dimNames = configDimensions.map(dim => dim.name)
-
- val passedCount = results.count(l => l.succeed)
- val count = results.count(_ => true)
+ val succeededCount = results.count(l => l.succeeded())
+ val totalCount = results.count(_ => true)
// RAM stats
println("Performing GC to collect RAM statistics... ")
@@ -160,22 +168,37 @@ class Parameterized(
println("")
println("Test report: ")
println("")
- printf("Summary: %d out of %d queries passed. \n", passedCount, count)
+ printf(
+ "Summary: %d out of %d queries successfully run on all config
combinations. \n",
+ succeededCount,
+ totalCount)
println("")
- TestResultLines(dimNames, metrics, results.filter(_.succeed)).print()
+ println("Configurations:")
+ coordinates.foreach { coord =>
+ println(s"${coord._1.id}. ${coord._1}")
+ }
+ println("")
+ val succeeded = results.filter(_.succeeded())
+ TestResultLines(
+ coordinates.size,
+ configDimensions,
+ metrics,
+ succeeded ++ TestResultLine.aggregate("all", succeeded))
+ .print()
println("")
- if (passedCount == count) {
+ if (succeededCount == totalCount) {
println("No failed queries. ")
println("")
} else {
println("Failed queries: ")
println("")
- TestResultLines(dimNames, metrics, results.filter(!_.succeed)).print()
+ TestResultLines(coordinates.size, configDimensions, metrics,
results.filter(!_.succeeded()))
+ .print()
println("")
}
- if (passedCount != count) {
+ if (succeededCount != totalCount) {
return false
}
true
@@ -185,56 +208,84 @@ class Parameterized(
case class DimKv(k: String, v: String)
case class Dim(name: String, dimValues: Seq[DimValue])
case class DimValue(name: String, conf: Seq[(String, String)])
-case class Coordinate(coordinate: Map[String, String]) // [dim, dim value]
-
-case class TestResultLine(
- queryId: String,
- succeed: Boolean,
- coordinate: Coordinate,
- rowCount: Option[Long],
- planningTimeMillis: Option[Long],
- executionTimeMillis: Option[Long],
- metrics: Map[String, Long],
- errorMessage: Option[String])
+// coordinate: [dim, dim value]
+case class Coordinate(id: Int, coordinate: Map[String, String]) {
+ override def toString: String = coordinate.mkString(", ")
+}
+
+case class TestResultLine(queryId: String, coordinates:
Seq[TestResultLine.Coord]) {
+ def succeeded(): Boolean = {
+ coordinates.forall(_.succeeded)
+ }
+}
object TestResultLine {
- class Parser(dimNames: Seq[String], metricNames: Seq[String])
- extends TableRender.RowParser[TestResultLine] {
+ case class Coord(
+ coordinate: Coordinate,
+ succeeded: Boolean,
+ rowCount: Option[Long],
+ planningTimeMillis: Option[Long],
+ executionTimeMillis: Option[Long],
+ metrics: Map[String, Long],
+ errorMessage: Option[String])
+
+ class Parser(metricNames: Seq[String]) extends
TableRender.RowParser[TestResultLine] {
override def parse(rowAppender: RowAppender, line: TestResultLine): Unit =
{
val inc = rowAppender.incremental()
inc.next().write(line.queryId)
- inc.next().write(line.succeed)
- dimNames.foreach { dimName =>
- val coordinate = line.coordinate.coordinate
- if (!coordinate.contains(dimName)) {
- throw new IllegalStateException("Dimension name not found" + dimName)
- }
- inc.next().write(coordinate(dimName))
- }
- metricNames.foreach { metricName =>
- val metrics = line.metrics
- inc.next().write(metrics.getOrElse(metricName, "N/A"))
- }
- inc.next().write(line.rowCount.getOrElse("N/A"))
- inc.next().write(line.planningTimeMillis.getOrElse("N/A"))
- inc.next().write(line.executionTimeMillis.getOrElse("N/A"))
+ val coords = line.coordinates
+ coords.foreach(coord => inc.next().write(coord.succeeded))
+ coords.foreach(coord => inc.next().write(coord.rowCount))
+ metricNames.foreach(metricName =>
+ coords.foreach(coord => inc.next().write(coord.metrics(metricName))))
+ coords.foreach(coord => inc.next().write(coord.planningTimeMillis))
+ coords.foreach(coord => inc.next().write(coord.executionTimeMillis))
+ }
+ }
+
+ def aggregate(name: String, lines: Iterable[TestResultLine]):
Iterable[TestResultLine] = {
+ if (lines.isEmpty) {
+ return Nil
+ }
+
+ if (lines.size == 1) {
+ return Nil
}
+
+ List(lines.reduce { (left, right) =>
+ TestResultLine(name, left.coordinates.zip(right.coordinates).map {
+ case (leftCoord, rightCoord) =>
+ assert(leftCoord.coordinate == rightCoord.coordinate)
+ Coord(
+ leftCoord.coordinate,
+ leftCoord.succeeded && rightCoord.succeeded,
+ (leftCoord.rowCount, rightCoord.rowCount).onBothProvided(_ + _),
+ (leftCoord.planningTimeMillis,
rightCoord.planningTimeMillis).onBothProvided(_ + _),
+ (leftCoord.executionTimeMillis,
rightCoord.executionTimeMillis).onBothProvided(_ + _),
+ (leftCoord.metrics, rightCoord.metrics).sumUp,
+ (leftCoord.errorMessage ++ rightCoord.errorMessage).reduceOption(_
+ ", " + _))
+ })
+ })
}
}
case class TestResultLines(
- dimNames: Seq[String],
+ coordCount: Int,
+ configDimensions: Seq[Dim],
metricNames: Seq[String],
lines: Iterable[TestResultLine]) {
def print(): Unit = {
- val fields = ListBuffer[String]("Query ID", "Succeeded")
- dimNames.foreach(dimName => fields.append(dimName))
- metricNames.foreach(metricName => fields.append(metricName))
- fields.append("Row Count")
- fields.append("Planning Time (Millis)")
- fields.append("Query Time (Millis)")
- val render = TableRender.plain[TestResultLine](fields: _*)(
- new TestResultLine.Parser(dimNames, metricNames))
+ val fields = ListBuffer[Field](Field.Leaf("Query ID"))
+ val coordFields = (1 to coordCount).map(id => Field.Leaf(id.toString))
+
+ fields.append(Field.Branch("Succeeded", coordFields))
+ fields.append(Field.Branch("Row Count", coordFields))
+ metricNames.foreach(metricName => fields.append(Field.Branch(metricName,
coordFields)))
+ fields.append(Field.Branch("Planning Time (Millis)", coordFields))
+ fields.append(Field.Branch("Query Time (Millis)", coordFields))
+
+ val render =
+ TableRender.create[TestResultLine](fields: _*)(new
TestResultLine.Parser(metricNames))
lines.foreach { line =>
render.appendRow(line)
@@ -253,10 +304,10 @@ object Parameterized {
coordinate: Coordinate,
desc: String,
explain: Boolean,
- metrics: Array[String]) = {
+ metrics: Array[String]): TestResultLine.Coord = {
println(s"Running query: $id...")
try {
- val testDesc = "Gluten Spark %s %s %s".format(desc, id, coordinate)
+ val testDesc = "Gluten Spark %s [%s] %s".format(desc, id, coordinate)
sessionSwitcher.useSession(coordinate.toString, testDesc)
runner.createTables(creator, sessionSwitcher.spark())
val result =
@@ -265,10 +316,9 @@ object Parameterized {
println(
s"Successfully ran query $id. " +
s"Returned row count: ${resultRows.length}")
- TestResultLine(
- id,
- succeed = true,
+ TestResultLine.Coord(
coordinate,
+ succeeded = true,
Some(resultRows.length),
Some(result.planningTimeMillis),
Some(result.executionTimeMillis),
@@ -280,16 +330,16 @@ object Parameterized {
println(
s"Error running query $id. " +
s" Error: ${error.get}")
- TestResultLine(id, succeed = false, coordinate, None, None, None,
Map.empty, error)
+ TestResultLine.Coord(coordinate, succeeded = false, None, None, None,
Map.empty, error)
}
}
- private[integration] def warmUp(
+ private def warmUp(
+ runner: QueryRunner,
creator: TableCreator,
- id: String,
- desc: String,
sessionSwitcher: SparkSessionSwitcher,
- runner: QueryRunner): Unit = {
+ id: String,
+ desc: String): Unit = {
println(s"Warming up: Running query: $id...")
try {
val testDesc = "Gluten Spark %s %s warm up".format(desc, id)
diff --git
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Queries.scala
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Queries.scala
index 540abbf45..de09d925e 100644
---
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Queries.scala
+++
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/Queries.scala
@@ -114,9 +114,9 @@ object Queries {
val inc = rowAppender.incremental()
inc.next().write(line.queryId)
inc.next().write(line.testPassed)
- inc.next().write(line.rowCount.getOrElse("N/A"))
- inc.next().write(line.planningTimeMillis.getOrElse("N/A"))
- inc.next().write(line.executionTimeMillis.getOrElse("N/A"))
+ inc.next().write(line.rowCount)
+ inc.next().write(line.planningTimeMillis)
+ inc.next().write(line.executionTimeMillis)
}
}
}
diff --git
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/QueriesCompare.scala
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/QueriesCompare.scala
index 596c293e4..d7b6ffff8 100644
---
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/QueriesCompare.scala
+++
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/QueriesCompare.scala
@@ -81,10 +81,10 @@ case class QueriesCompare(
println("")
}
- var all = QueriesCompare.aggregate(results, "all")
+ var all = QueriesCompare.aggregate("all", results)
if (passedCount != count) {
- all = QueriesCompare.aggregate(succeed, "succeeded") ::: all
+ all = QueriesCompare.aggregate("succeeded", succeed) ::: all
}
println("Overall: ")
@@ -123,13 +123,13 @@ object QueriesCompare {
} else None
inc.next().write(line.queryId)
inc.next().write(line.testPassed)
- inc.next().write(line.expectedRowCount.getOrElse("N/A"))
- inc.next().write(line.actualRowCount.getOrElse("N/A"))
- inc.next().write(line.expectedPlanningTimeMillis.getOrElse("N/A"))
- inc.next().write(line.actualPlanningTimeMillis.getOrElse("N/A"))
- inc.next().write(line.expectedExecutionTimeMillis.getOrElse("N/A"))
- inc.next().write(line.actualExecutionTimeMillis.getOrElse("N/A"))
- inc.next().write(speedUp.map("%.2f%%".format(_)).getOrElse("N/A"))
+ inc.next().write(line.expectedRowCount)
+ inc.next().write(line.actualRowCount)
+ inc.next().write(line.expectedPlanningTimeMillis)
+ inc.next().write(line.actualPlanningTimeMillis)
+ inc.next().write(line.expectedExecutionTimeMillis)
+ inc.next().write(line.actualExecutionTimeMillis)
+ inc.next().write(speedUp.map("%.2f%%".format(_)))
}
}
}
@@ -152,7 +152,7 @@ object QueriesCompare {
render.print(System.out)
}
- private def aggregate(succeed: List[TestResultLine], name: String):
List[TestResultLine] = {
+ private def aggregate(name: String, succeed: List[TestResultLine]):
List[TestResultLine] = {
if (succeed.isEmpty) {
return Nil
}
@@ -160,25 +160,13 @@ object QueriesCompare {
succeed.reduce((r1, r2) =>
TestResultLine(
name,
- testPassed = true,
- if (r1.expectedRowCount.nonEmpty && r2.expectedRowCount.nonEmpty)
- Some(r1.expectedRowCount.get + r2.expectedRowCount.get)
- else None,
- if (r1.actualRowCount.nonEmpty && r2.actualRowCount.nonEmpty)
- Some(r1.actualRowCount.get + r2.actualRowCount.get)
- else None,
- if (r1.expectedPlanningTimeMillis.nonEmpty &&
r2.expectedPlanningTimeMillis.nonEmpty)
- Some(r1.expectedPlanningTimeMillis.get +
r2.expectedPlanningTimeMillis.get)
- else None,
- if (r1.actualPlanningTimeMillis.nonEmpty &&
r2.actualPlanningTimeMillis.nonEmpty)
- Some(r1.actualPlanningTimeMillis.get +
r2.actualPlanningTimeMillis.get)
- else None,
- if (r1.expectedExecutionTimeMillis.nonEmpty &&
r2.expectedExecutionTimeMillis.nonEmpty)
- Some(r1.expectedExecutionTimeMillis.get +
r2.expectedExecutionTimeMillis.get)
- else None,
- if (r1.actualExecutionTimeMillis.nonEmpty &&
r2.actualExecutionTimeMillis.nonEmpty)
- Some(r1.actualExecutionTimeMillis.get +
r2.actualExecutionTimeMillis.get)
- else None,
+ r1.testPassed && r2.testPassed,
+ (r1.expectedRowCount, r2.expectedRowCount).onBothProvided(_ + _),
+ (r1.actualRowCount, r2.actualRowCount).onBothProvided(_ + _),
+ (r1.expectedPlanningTimeMillis,
r2.expectedPlanningTimeMillis).onBothProvided(_ + _),
+ (r1.actualPlanningTimeMillis,
r2.actualPlanningTimeMillis).onBothProvided(_ + _),
+ (r1.expectedExecutionTimeMillis,
r2.expectedExecutionTimeMillis).onBothProvided(_ + _),
+ (r1.actualExecutionTimeMillis,
r2.actualExecutionTimeMillis).onBothProvided(_ + _),
None)))
}
diff --git
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/TableRender.scala
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/TableRender.scala
index 4cded2848..2b1cca61e 100644
---
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/TableRender.scala
+++
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/TableRender.scala
@@ -20,7 +20,7 @@ package org.apache.gluten.integration.action
import org.apache.commons.lang3.StringUtils
import
org.apache.gluten.integration.action.TableRender.RowParser.FieldAppender.RowAppender
-import java.io.{OutputStream, PrintStream}
+import java.io.{ByteArrayOutputStream, OutputStream, PrintStream}
import scala.collection.mutable
trait TableRender[ROW <: Any] {
@@ -31,7 +31,8 @@ trait TableRender[ROW <: Any] {
object TableRender {
def create[ROW <: Any](fields: Field*)(implicit parser: RowParser[ROW]):
TableRender[ROW] = {
assert(fields.nonEmpty)
- new Impl[ROW](Schema(fields), parser)
+ // Deep copy to avoid duplications (In case caller reuses a sub-tree).
+ new Impl[ROW](Schema(fields.map(_.makeCopy())), parser)
}
def plain[ROW <: Any](fields: String*)(implicit parser: RowParser[ROW]):
TableRender[ROW] = {
@@ -40,8 +41,10 @@ object TableRender {
}
trait Field {
+ def id(): Int = System.identityHashCode(this)
def name: String
def leafs: Seq[Field.Leaf]
+ def makeCopy(): Field
}
object Field {
@@ -57,9 +60,12 @@ object TableRender {
children.map(child => leafsOf(child)).reduce(_ ++ _)
}
}
+
+ override def makeCopy(): Field = copy(name, children.map(_.makeCopy()))
}
case class Leaf(override val name: String) extends Field {
override val leafs: Seq[Leaf] = List(this)
+ override def makeCopy(): Field = copy()
}
}
@@ -109,7 +115,7 @@ object TableRender {
schema.leafs.zipWithIndex.foreach {
case (leaf, i) =>
val dataWidth = dataWidths(i)
- widthMap += (System.identityHashCode(leaf) -> (dataWidth max
(leaf.name.length + 2)))
+ widthMap += (leaf.id() -> (dataWidth max (leaf.name.length + 2)))
}
schema.fields.foreach { root =>
@@ -122,12 +128,12 @@ object TableRender {
.toInt
children.foreach(child => updateWidth(child, leafLowerBound *
child.leafs.size))
val childrenWidth =
- children.map(child =>
widthMap(System.identityHashCode(child))).sum
+ children.map(child => widthMap(child.id())).sum
val width = childrenWidth + children.size - 1
- val hash = System.identityHashCode(branch)
+ val hash = branch.id()
widthMap += hash -> width
case leaf @ Field.Leaf(name) =>
- val hash = System.identityHashCode(leaf)
+ val hash = leaf.id()
val newWidth = widthMap(hash) max lowerBound
widthMap.put(hash, newWidth)
case _ => new IllegalStateException()
@@ -146,9 +152,9 @@ object TableRender {
val schemaLine = cells
.map {
case Given(field) =>
- (field.name, widthMap(System.identityHashCode(field)))
+ (field.name, widthMap(field.id()))
case PlaceHolder(leaf) =>
- ("", widthMap(System.identityHashCode(leaf)))
+ ("", widthMap(leaf.id()))
}
.map {
case (name, width) =>
@@ -168,7 +174,7 @@ object TableRender {
val separationLine = schema.leafs
.map { leaf =>
- widthMap(System.identityHashCode(leaf))
+ widthMap(leaf.id())
}
.map { width =>
new String(Array.tabulate(width)(_ => '-'))
@@ -182,7 +188,7 @@ object TableRender {
.zip(schema.leafs)
.map {
case (value, leaf) =>
- (value, widthMap(System.identityHashCode(leaf)))
+ (value, widthMap(leaf.id()))
}
.map {
case (value, width) =>
@@ -194,6 +200,12 @@ object TableRender {
printer.flush()
}
+
+ override def toString: String = {
+ val out = new ByteArrayOutputStream()
+ print(out)
+ out.toString
+ }
}
trait RowParser[ROW <: Any] {
@@ -302,7 +314,13 @@ object TableRender {
override def write(value: Any): Unit = {
assert(field.isInstanceOf[Field.Leaf])
- mutableRow(column) = value.toString
+ mutableRow(column) = toString(value)
+ }
+
+ private def toString(value: Any): String = value match {
+ case Some(v) => toString(v)
+ case None => "N/A"
+ case other => other.toString
}
}
}
diff --git
a/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/package.scala
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/package.scala
new file mode 100644
index 000000000..6046ae4aa
--- /dev/null
+++
b/tools/gluten-it/common/src/main/scala/org/apache/gluten/integration/action/package.scala
@@ -0,0 +1,36 @@
+/*
+ * 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.gluten.integration
+
+package object action {
+ implicit class DualOptionsOps[T](value: (Option[T], Option[T])) {
+ def onBothProvided[R](func: (T, T) => R): Option[R] = {
+ if (value._1.isEmpty || value._2.isEmpty) {
+ return None
+ }
+ Some(func(value._1.get, value._2.get))
+ }
+ }
+
+ implicit class DualMetricsOps(value: (Map[String, Long], Map[String, Long]))
{
+ def sumUp: Map[String, Long] = {
+ assert(value._1.keySet == value._2.keySet)
+ value._1.map { case (k, v) => k -> (v + value._2(k)) }
+ }
+ }
+}
diff --git
a/tools/gluten-it/common/src/test/java/org/apache/gluten/integration/action/TableRenderTest.scala
b/tools/gluten-it/common/src/test/java/org/apache/gluten/integration/action/TableRenderTest.scala
index ce7b0974c..1efc72148 100644
---
a/tools/gluten-it/common/src/test/java/org/apache/gluten/integration/action/TableRenderTest.scala
+++
b/tools/gluten-it/common/src/test/java/org/apache/gluten/integration/action/TableRenderTest.scala
@@ -99,11 +99,32 @@ object TableRenderTest {
Console.out.println()
}
+ def case5(): Unit = {
+ val leafs = List(Leaf("1"), Leaf("2"), Leaf("3"), Leaf("4"))
+ val render: TableRender[Seq[String]] = TableRender.create(
+ Leaf("Query ID"),
+ Branch("Succeeded", leafs),
+ Branch("Row Count", leafs))(new RowParser[Seq[String]] {
+ override def parse(rowFactory: FieldAppender.RowAppender, row:
Seq[String]): Unit = {
+ val inc = rowFactory.incremental()
+ row.foreach(ceil => inc.next().write(ceil))
+ }
+ })
+
+ render.appendRow(
+ List("q1", "true", "true", "true && true && true && true", "true", "1",
"1", "1", "1"))
+ render.appendRow(
+ List("q2", "true", "true", "true", "true", "100000", "100000", "100000",
"100000"))
+ render.print(Console.out)
+ Console.out.println()
+ }
+
def main(args: Array[String]): Unit = {
case0()
case1()
case2()
case3()
case4()
+ case5()
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]