package convert import ( "fmt" "go/ast" "strings" "unicode" ) /* * Creates a func init() node */ func createVarUnderscoreBlock() *ast.ValueSpec { valueSpec := &ast.ValueSpec{} object := &ast.Object{Kind: 4, Name: "_", Decl: valueSpec, Data: 0} ident := &ast.Ident{Name: "_", Obj: object} valueSpec.Names = append(valueSpec.Names, ident) return valueSpec } /* * Creates a Describe("Testing with ginkgo", func() { }) node */ func createDescribeBlock() *ast.CallExpr { blockStatement := &ast.BlockStmt{List: []ast.Stmt{}} fieldList := &ast.FieldList{} funcType := &ast.FuncType{Params: fieldList} funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement} basicLit := &ast.BasicLit{Kind: 9, Value: "\"Testing with Ginkgo\""} describeIdent := &ast.Ident{Name: "Describe"} return &ast.CallExpr{Fun: describeIdent, Args: []ast.Expr{basicLit, funcLit}} } /* * Convenience function to return the name of the *testing.T param * for a Test function that will be rewritten. This is useful because * we will want to replace the usage of this named *testing.T inside the * body of the function with a GinktoT. */ func namedTestingTArg(node *ast.FuncDecl) string { return node.Type.Params.List[0].Names[0].Name // *exhale* } /* * Convenience function to return the block statement node for a Describe statement */ func blockStatementFromDescribe(desc *ast.CallExpr) *ast.BlockStmt { var funcLit *ast.FuncLit var found = false for _, node := range desc.Args { switch node := node.(type) { case *ast.FuncLit: found = true funcLit = node break } } if !found { panic("Error finding ast.FuncLit inside describe statement. Somebody done goofed.") } return funcLit.Body } /* convenience function for creating an It("TestNameHere") * with all the body of the test function inside the anonymous * func passed to It() */ func createItStatementForTestFunc(testFunc *ast.FuncDecl) *ast.ExprStmt { blockStatement := &ast.BlockStmt{List: testFunc.Body.List} fieldList := &ast.FieldList{} funcType := &ast.FuncType{Params: fieldList} funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement} testName := rewriteTestName(testFunc.Name.Name) basicLit := &ast.BasicLit{Kind: 9, Value: fmt.Sprintf("\"%s\"", testName)} itBlockIdent := &ast.Ident{Name: "It"} callExpr := &ast.CallExpr{Fun: itBlockIdent, Args: []ast.Expr{basicLit, funcLit}} return &ast.ExprStmt{X: callExpr} } /* * rewrite test names to be human readable * eg: rewrites "TestSomethingAmazing" as "something amazing" */ func rewriteTestName(testName string) string { nameComponents := []string{} currentString := "" indexOfTest := strings.Index(testName, "Test") if indexOfTest != 0 { return testName } testName = strings.Replace(testName, "Test", "", 1) first, rest := testName[0], testName[1:] testName = string(unicode.ToLower(rune(first))) + rest for _, rune := range testName { if unicode.IsUpper(rune) { nameComponents = append(nameComponents, currentString) currentString = string(unicode.ToLower(rune)) } else { currentString += string(rune) } } return strings.Join(append(nameComponents, currentString), " ") } func newGinkgoTFromIdent(ident *ast.Ident) *ast.CallExpr { return &ast.CallExpr{ Lparen: ident.NamePos + 1, Rparen: ident.NamePos + 2, Fun: &ast.Ident{Name: "GinkgoT"}, } } func newGinkgoTInterface() *ast.Ident { return &ast.Ident{Name: "GinkgoTInterface"} }