summaryrefslogblamecommitdiff
path: root/src/test/scala/forge/ForgeTests.scala
blob: 5e9d8e37394cf4ea999a7ef101b6899b2fd4162d (plain) (tree)
1
2
3
4
5
6
7
8
9
10


              
                  
                              
                                    
 
                    

                                                         
                      
                              

                 

                              
     
 
                      

                                    
     
                   



                                       
     
                       

                                              

     

                             
                                              

     



























































                                                                                     
     

 

                                                                      
                                                                          

                                  
 







                                          



                                                                                 

                                    
                                                                             


                                    





                                 
       
     
































                                                                                       

































                                                              
                       


                                                                                
                                                                                                  
               







                                                                                                    
         
       
 


                                             

                                                            

                                                                  
                                                            


                     
                                                            
 
                         
                                                        
 
                       
                                                            



                                   




























                                                                                    
                      



                                                                         
 
                         
                                                        
 
                       







                                                                                    
       








                                                                           
     
 
 









                                                                                              
package forge

import utest._
import Target.test
import java.nio.{file => jnio}
object ForgeTests extends TestSuite{

  val tests = Tests{
    val baseCtx = DefCtx("forge.ForgeTests.tests ", None)

    object Singleton {
      val single = T{ test() }
    }
    object Pair {
      val up = T{ test() }
      val down = T{ test(up) }
    }

    object AnonTriple{
      val up = T{ test() }
      val down = T{ test(test(up)) }
    }
    object Diamond{
      val up = T{ test() }
      val left = T{ test(up) }
      val right = T{ test(up) }
      val down = T{ test(left, right) }
    }
    object AnonDiamond{
      val up = T{ test() }
      val down = T{ test(test(up), test(up)) }
    }

    object AnonImpureDiamond{
      val up = T{ test() }
      val down = T{ test(test(up), test(up)) }
    }


    'syntaxLimits - {
      // Make sure that we properly prohibit cases where a `test()` target can
      // be created more than once with the same `DefCtx`, while still allowing
      // cases where the `test()` target is created exactly one time, or even
      // zero-or-one times (since that's ok, as long as it's not more than once)

      'neg - {
        'nakedTest - {
          compileError("test()")
          ()
        }
        'notFunctionCall - {
          compileError("T{ 123 }")
          ()
        }
        'functionCallWithoutImplicit - {
          compileError("T{ println() }")
          ()
        }
        // Make sure the snippets without `test()`s compile, but the same snippets
        // *with* the `test()` calls do not (presumably due to the `@compileTimeOnly`
        // annotation)
        //
        // For some reason, `if(false)` isn't good enough because scalac constant
        // folds the conditional, eliminates the entire code block, and makes any
        // `@compileTimeOnly`s annotations disappear...


        'canEvaluateMoreThanOnce - {
          if (math.random() > 10) T{ Seq(1, 2).map(_ => ???); test() }
          compileError("T{ Seq(1, 2).map(_ => test()); test() }")

          if (math.random() > 10) T{ class Foo{ ??? }; test() }
          compileError("T{ class Foo{ test() }; test() }")

          if (math.random() > 10) T{ test({while(true){ }; ???}) }
          compileError("T{ test({while(true){ test() }; ???}) }")

          if (math.random() > 10) T{ do{ } while(true); test() }
          compileError("T{ do{ test() } while(true); test() }")

          if (math.random() > 10) T{ def foo() = ???; test() }
          compileError("T{ def foo() = test(); test() }")

          if (math.random() > 10) T{ None.getOrElse(???); test() }
          if (math.random() > 10) T{ None.contains(test()); test() }
          compileError("T{ None.getOrElse(test()); test() }")

          ()
        }
      }
      'pos - {
        T{ test({val x = test(); x}) }
        T{ test({lazy val x = test(); x}) }
        T { object foo {val x = test()}; test(foo.x) }
        T{ test({val x = if (math.random() > 0.5) test() else test(); x}) }

        ()
      }
    }


    'topoSortedTransitiveTargets - {
      def check(targets: Seq[Target[_]], expected: Seq[Target[_]]) = {
        val result = Evaluator.topoSortedTransitiveTargets(targets).values
        assert(result == expected)
      }

      'singleton - check(
        targets = Seq(Singleton.single),
        expected = Seq(Singleton.single)
      )
      'pair - check(
        targets = Seq(Pair.down),
        expected = Seq(Pair.up, Pair.down)
      )
      'anonTriple - check(
        targets = Seq(AnonTriple.down),
        expected = Seq(AnonTriple.up, AnonTriple.down.inputs(0), AnonTriple.down)
      )
      'diamond - check(
        targets = Seq(Diamond.down),
        expected = Seq(Diamond.up, Diamond.left, Diamond.right, Diamond.down)
      )
      'anonDiamond - check(
        targets = Seq(Diamond.down),
        expected = Seq(
          Diamond.up,
          Diamond.down.inputs(0),
          Diamond.down.inputs(1),
          Diamond.down
        )
      )
    }

    'groupAroundNamedTargets - {
      def check(target: Target[_], expected: Seq[Seq[Target[_]]]) = {
        val grouped = Evaluator.groupAroundNamedTargets(
          Evaluator.topoSortedTransitiveTargets(Seq(target))
        )
        assert(grouped == expected)
      }
      'singleton - check(
        Singleton.single,
        Seq(Seq(Singleton.single))
      )
      'pair - check(
        Pair.down,
        Seq(Seq(Pair.up), Seq(Pair.down))
      )
      'anonTriple - check(
        AnonTriple.down,
        Seq(Seq(AnonTriple.up), Seq(AnonTriple.down.inputs(0), AnonTriple.down))
      )
      'diamond - check(
        Diamond.down,
        Seq(Seq(Diamond.up), Seq(Diamond.left), Seq(Diamond.right), Seq(Diamond.down))
      )
      'anonDiamond - check(
        AnonDiamond.down,
        Seq(
          Seq(AnonDiamond.up),
          Seq(AnonDiamond.down.inputs(1), AnonDiamond.down.inputs(0), AnonDiamond.down)
        )
      )
    }

    'labeling - {

      def check(t: Target[_], relPath: String) = {
        val targetLabel = t.defCtx.label
        val expectedLabel = baseCtx.label + relPath
        assert(targetLabel == expectedLabel)
      }
      'singleton - check(Singleton.single, "Singleton.single")
      'pair - {
        check(Pair.up, "Pair.up")
        check(Pair.down, "Pair.down")
      }

      'anonTriple - {
        check(AnonTriple.up, "AnonTriple.up")
        check(AnonTriple.down.inputs(0), "AnonTriple.down1")
        check(AnonTriple.down, "AnonTriple.down")
      }

      'diamond - {
        check(Diamond.up, "Diamond.up")
        check(Diamond.left, "Diamond.left")
        check(Diamond.right, "Diamond.right")
        check(Diamond.down, "Diamond.down")
      }

      'anonDiamond - {
        check(AnonDiamond.up, "AnonDiamond.up")
        check(AnonDiamond.down.inputs(0), "AnonDiamond.down1")
        check(AnonDiamond.down.inputs(1), "AnonDiamond.down2")
        check(AnonDiamond.down, "AnonDiamond.down")
      }

    }
    'evaluateSingle - {
      val evaluator = new Evaluator(jnio.Paths.get("target/workspace"), baseCtx)

      def check(target: Target[_], expValue: Any, expEvaled: Seq[Target[_]]) = {
        val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(Seq(target))
        assert(
          returnedValues == Seq(expValue),
          returnedEvaluated == expEvaled
        )
        // Second time the value is already cached, so no evaluation needed
        val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(Seq(target))
        assert(
          returnedValues2 == returnedValues,
          returnedEvaluated2 == Nil
        )
      }

      'singleton - {
        import Singleton._
        // First time the target is evaluated
        check(single, expValue = 0, expEvaled = Seq(single))

        single.counter += 1
        // After incrementing the counter, it forces re-evaluation
        check(single, expValue = 1, expEvaled = Seq(single))
      }
      'pair - {
        import Pair._
        check(down, expValue = 0, expEvaled = Seq(up, down))

        down.counter += 1
        check(down, expValue = 1, expEvaled = Seq(down))

        up.counter += 1
        check(down, expValue = 2, expEvaled = Seq(up, down))
      }
      'anonTriple - {
        import AnonTriple._
        val middle = down.inputs(0)
        check(down, expValue = 0, expEvaled = Seq(up, middle, down))

        down.counter += 1
        check(down, expValue = 1, expEvaled = Seq(down))

        up.counter += 1
        check(down, expValue = 2, expEvaled = Seq(up, middle, down))

        middle.asInstanceOf[Target.Test].counter += 1

        check(down, expValue = 3, expEvaled = Seq(middle, down))
      }
      'diamond - {
        import Diamond._
        check(down, expValue = 0, expEvaled = Seq(up, left, right, down))

        down.counter += 1
        check(down, expValue = 1, expEvaled = Seq(down))

        up.counter += 1
        // Increment by 2 because up is referenced twice: once by left once by right
        check(down, expValue = 3, expEvaled = Seq(up, left, right, down))

        left.counter += 1
        check(down, expValue = 4, expEvaled = Seq(left, down))

        right.counter += 1
        check(down, expValue = 5, expEvaled = Seq(right, down))
      }
      'anonDiamond - {
        import AnonDiamond._
        val left = down.inputs(0).asInstanceOf[Target.Test]
        val right = down.inputs(1).asInstanceOf[Target.Test]
        check(down, expValue = 0, expEvaled = Seq(up, left, right, down))

        down.counter += 1
        check(down, expValue = 1, expEvaled = Seq(down))

        up.counter += 1
        // Increment by 2 because up is referenced twice: once by left once by right
        check(down, expValue = 3, expEvaled = Seq(up, left, right, down))

        left.counter += 1
        check(down, expValue = 4, expEvaled = Seq(left, down))

        right.counter += 1
        check(down, expValue = 5, expEvaled = Seq(right, down))
      }
//      'anonImpureDiamond - {
//        import AnonImpureDiamond._
//        val left = down.inputs(0).asInstanceOf[Target.Test]
//        val right = down.inputs(1).asInstanceOf[Target.Test]
//        check(down, expValue = 0, expEvaled = Seq(up, left, right, down))
//
//        down.counter += 1
//        check(down, expValue = 1, expEvaled = Seq(left, down))
//      }
    }


//    'full - {
//      val sourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/src"))
//      val resourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/resources"))
//      val allSources = list(sourceRoot)
//      val classFiles = compileAll(allSources)
//      val jar = jarUp(resourceRoot, classFiles)
//      Evaluator.apply(jar, jnio.Paths.get("target/workspace"))
//    }
  }
}