/*
 * 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.livy.repl

import org.apache.spark.SparkConf
import org.json4s.DefaultFormats
import org.json4s.JsonDSL._
import org.scalatest._

import org.apache.livy.rsc.driver.SparkEntries

class SparkRInterpreterSpec extends BaseInterpreterSpec {

  implicit val formats = DefaultFormats

  override protected def withFixture(test: NoArgTest): Outcome = {
    assume(!sys.props.getOrElse("skipRTests", "false").toBoolean, "Skipping R tests.")
    super.withFixture(test)
  }


  override def createInterpreter(): Interpreter = {
    val sparkConf = new SparkConf()
    SparkRInterpreter(sparkConf, new SparkEntries(sparkConf))
  }

  it should "execute `1 + 2` == 3" in withInterpreter { interpreter =>
    val response = interpreter.execute("1 + 2")
    response should equal (Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "[1] 3"
    ))
  }

  it should "execute multiple statements" in withInterpreter { interpreter =>
    var response = interpreter.execute("x = 1")
    response should equal (Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> ""
    ))

    response = interpreter.execute("y = 2")
    response should equal (Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> ""
    ))

    response = interpreter.execute("x + y")
    response should equal (Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "[1] 3"
    ))
  }

  it should "execute multiple statements in one block" in withInterpreter { interpreter =>
    val response = interpreter.execute(
      """
        |x = 1
        |
        |y = 2
        |
        |x + y
      """.stripMargin)
    response should equal(Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "[1] 3"
    ))
  }

  it should "get multiple outputs in one block" in withInterpreter { interpreter =>
    val response = interpreter.execute(
      """
        |print("1")
        |print("2")
      """.stripMargin)
    response should equal(Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "[1] \"1\"\n[1] \"2\""
    ))
  }

  it should "capture stdout" in withInterpreter { interpreter =>
    val response = interpreter.execute("cat(3)")
    response should equal(Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "3"
    ))
  }

  it should "report an error if accessing an unknown variable" in withInterpreter { interpreter =>
    val response = interpreter.execute("x")
    assert(response.isInstanceOf[Interpreter.ExecuteError])
    val errorResponse = response.asInstanceOf[Interpreter.ExecuteError]
    errorResponse.ename should be ("Error")
    assert(errorResponse.evalue.contains("object 'x' not found"))
  }


  it should "not hang when executing incomplete statements" in withInterpreter { interpreter =>
    val response = interpreter.execute("x[")
    response should equal(Interpreter.ExecuteError(
      "Error",
        """[1] "Error in parse(text = \"x[\"): <text>:2:0: unexpected end of input\n1: x[\n   ^""""
    ))
  }

  it should "escape the statement" in withInterpreter { interpreter =>
    val response = interpreter.execute("print(\"a\")")
    response should equal(Interpreter.ExecuteSuccess(
      TEXT_PLAIN -> "[1] \"a\""
    ))
  }

}
