// Run the harness, which listens for requests and proxies them to the app // server, which it runs and rebuilds as necessary. func (h *Harness) Run() { revel.ConfigureLogging() watcher = revel.NewWatcher() watcher.Listen(h, revel.CodePaths...) go func() { addr := fmt.Sprintf("%s:%d", revel.HttpAddr, revel.HttpPort) glog.Infof("Listening on %s", addr) var err error if revel.HttpSsl { err = http.ListenAndServeTLS(addr, revel.HttpSslCert, revel.HttpSslKey, h) } else { err = http.ListenAndServe(addr, h) } if err != nil { glog.Fatalln("Failed to start reverse proxy:", err) } }() // Kill the app on signal. ch := make(chan os.Signal) signal.Notify(ch, os.Interrupt, os.Kill) <-ch if h.app != nil { h.app.Kill() } os.Exit(1) }
func runApp(args []string) { if len(args) == 0 { errorf("No import path given.\nRun 'revel help run' for usage.\n") } // Determine the run mode. mode := "dev" if len(args) >= 2 { mode = args[1] } // Find and parse app.conf revel.Init(mode, args[0], "") revel.LoadModules() revel.LoadMimeConfig() // Set working directory to BasePath, to make relative paths convenient and // dependable. if err := os.Chdir(revel.BasePath); err != nil { log.Fatalln("Failed to change directory into app path: ", err) } // Determine the override port, if any. port := revel.HttpPort if len(args) == 3 { var err error if port, err = strconv.Atoi(args[2]); err != nil { errorf("Failed to parse port as integer: %s", args[2]) } } glog.Infof("Running %s (%s) in %s mode", revel.AppName, revel.ImportPath, mode) glog.V(1).Info("Base path: ", revel.BasePath) // If the app is run in "watched" mode, use the harness to run it. if revel.Config.BoolDefault("watch", true) && revel.Config.BoolDefault("watch.code", true) { revel.HttpPort = port harness.NewHarness().Run() // Never returns. } // Else, just build and run the app. app, err := harness.Build() if err != nil { errorf("Failed to build app: %s", err) } app.Port = port app.Cmd().Run() }
func testApp(args []string) { var err error if len(args) == 0 { errorf("No import path given.\nRun 'revel help test' for usage.\n") } mode := "dev" if len(args) >= 2 { mode = args[1] } // Find and parse app.conf revel.Init(mode, args[0], "") revel.LoadModules() // Set working directory to BasePath, to make relative paths convenient and // dependable. if err := os.Chdir(revel.BasePath); err != nil { log.Fatalln("Failed to change directory into app path: ", err) } // Ensure that the testrunner is loaded in this mode. testRunnerFound := false for _, module := range revel.Modules { if module.ImportPath == "github.com/hongrich/revel/modules/testrunner" { testRunnerFound = true break } } if !testRunnerFound { errorf(`Error: The testrunner module is not running. You can add it to a run mode configuration with the following line: module.testrunner = github.com/hongrich/revel/modules/testrunner `) } // Create a directory to hold the test result files. resultPath := filepath.Join(revel.BasePath, "test-results") if err = os.RemoveAll(resultPath); err != nil { errorf("Failed to remove test result directory %s: %s", resultPath, err) } if err = os.Mkdir(resultPath, 0777); err != nil { errorf("Failed to create test result directory %s: %s", resultPath, err) } app, reverr := harness.Build() if reverr != nil { errorf("Error building: %s", reverr) } // Direct all the output into a file in the test-results directory. cmd := app.Cmd() // Start the app... if err := cmd.Start(); err != nil { errorf("%s", err) } defer cmd.Kill() glog.Infof("Testing %s (%s) in %s mode", revel.AppName, revel.ImportPath, mode) // Get a list of tests. // Since this is the first request to the server, retry/sleep a couple times // in case it hasn't finished starting up yet. var ( testSuites []controllers.TestSuiteDesc resp *http.Response baseUrl = fmt.Sprintf("http://127.0.0.1:%d", revel.HttpPort) ) for i := 0; ; i++ { if resp, err = http.Get(baseUrl + "/@tests.list"); err == nil { break } if i < 3 { time.Sleep(3 * time.Second) continue } errorf("Failed to request test list: %s", err) } defer resp.Body.Close() json.NewDecoder(resp.Body).Decode(&testSuites) // If a specific TestSuite[.Method] is specified, only run that suite/test if len(args) == 3 { testSuites = filterTestSuites(testSuites, args[2]) } fmt.Printf("\n%d test suite%s to run.\n", len(testSuites), pluralize(len(testSuites), "", "s")) fmt.Println() // Load the result template, which we execute for each suite. module, _ := revel.ModuleByName("testrunner") TemplateLoader := revel.NewTemplateLoader([]string{filepath.Join(module.Path, "app", "views")}) if err := TemplateLoader.Refresh(); err != nil { errorf("Failed to compile templates: %s", err) } resultTemplate, err := TemplateLoader.Template("TestRunner/SuiteResult.html") if err != nil { errorf("Failed to load suite result template: %s", err) } // Run each suite. var ( overallSuccess = true failedResults []controllers.TestSuiteResult ) for _, suite := range testSuites { // Print the name of the suite we're running. name := suite.Name if len(name) > 22 { name = name[:19] + "..." } fmt.Printf("%-22s", name) // Run every test. startTime := time.Now() suiteResult := controllers.TestSuiteResult{Name: suite.Name, Passed: true} for _, test := range suite.Tests { testUrl := baseUrl + "/@tests/" + suite.Name + "/" + test.Name resp, err := http.Get(testUrl) if err != nil { errorf("Failed to fetch test result at url %s: %s", testUrl, err) } defer resp.Body.Close() var testResult controllers.TestResult json.NewDecoder(resp.Body).Decode(&testResult) if !testResult.Passed { suiteResult.Passed = false } suiteResult.Results = append(suiteResult.Results, testResult) } overallSuccess = overallSuccess && suiteResult.Passed // Print result. (Just PASSED or FAILED, and the time taken) suiteResultStr, suiteAlert := "PASSED", "" if !suiteResult.Passed { suiteResultStr, suiteAlert = "FAILED", "!" failedResults = append(failedResults, suiteResult) } fmt.Printf("%8s%3s%6ds\n", suiteResultStr, suiteAlert, int(time.Since(startTime).Seconds())) // Create the result HTML file. suiteResultFilename := filepath.Join(resultPath, fmt.Sprintf("%s.%s.html", suite.Name, strings.ToLower(suiteResultStr))) suiteResultFile, err := os.Create(suiteResultFilename) if err != nil { errorf("Failed to create result file %s: %s", suiteResultFilename, err) } if err = resultTemplate.Execute(suiteResultFile, suiteResult); err != nil { errorf("Failed to render result template: %s", err) } } fmt.Println() if overallSuccess { writeResultFile(resultPath, "result.passed", "passed") fmt.Println("All Tests Passed.") } else { for _, failedResult := range failedResults { fmt.Printf("Failures:\n") for _, result := range failedResult.Results { if !result.Passed { fmt.Printf("%s.%s\n", failedResult.Name, result.Name) fmt.Printf("%s\n\n", result.ErrorSummary) } } } writeResultFile(resultPath, "result.failed", "failed") errorf("Some tests failed. See file://%s for results.", resultPath) } }
// Build the app: // 1. Generate the the main.go file. // 2. Run the appropriate "go build" command. // Requires that revel.Init has been called previously. // Returns the path to the built binary, and an error if there was a problem building it. func Build() (app *App, compileError *revel.Error) { start := time.Now() // First, clear the generated files (to avoid them messing with ProcessSource). cleanSource("tmp", "routes") sourceInfo, compileError := ProcessSource(revel.CodePaths) if compileError != nil { return nil, compileError } // Add the db.import to the import paths. if dbImportPath, found := revel.Config.String("db.import"); found { sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, dbImportPath) } // Generate two source files. templateArgs := map[string]interface{}{ "Controllers": sourceInfo.ControllerSpecs(), "ValidationKeys": sourceInfo.ValidationKeys, "ImportPaths": calcImportAliases(sourceInfo), "TestSuites": sourceInfo.TestSuites(), } genSource("tmp", "main.go", MAIN, templateArgs) genSource("routes", "routes.go", ROUTES, templateArgs) // Read build config. buildTags := revel.Config.StringDefault("build.tags", "") // Build the user program (all code under app). // It relies on the user having "go" installed. goPath, err := exec.LookPath("go") if err != nil { glog.Fatalf("Go executable not found in PATH.") } pkg, err := build.Default.Import(revel.ImportPath, "", build.FindOnly) if err != nil { glog.Fatalln("Failure importing", revel.ImportPath) } binName := filepath.Join(pkg.BinDir, filepath.Base(revel.BasePath)) if runtime.GOOS == "windows" { binName += ".exe" } gotten := make(map[string]struct{}) for { buildCmd := exec.Command(goPath, "build", "-i", "-tags", buildTags, "-o", binName, path.Join(revel.ImportPath, "app", "tmp")) glog.V(1).Infoln("Exec:", buildCmd.Args) output, err := buildCmd.CombinedOutput() // If the build succeeded, we're done. if err == nil { glog.Infof("Build took %s", time.Since(start)) return NewApp(binName), nil } glog.Error(string(output)) // See if it was an import error that we can go get. matches := importErrorPattern.FindStringSubmatch(string(output)) if matches == nil { return nil, newCompileError(output) } // Ensure we haven't already tried to go get it. pkgName := matches[1] if _, alreadyTried := gotten[pkgName]; alreadyTried { return nil, newCompileError(output) } gotten[pkgName] = struct{}{} // Execute "go get <pkg>" getCmd := exec.Command(goPath, "get", pkgName) glog.V(1).Infoln("Exec:", getCmd.Args) getOutput, err := getCmd.CombinedOutput() if err != nil { glog.Error(string(getOutput)) return nil, newCompileError(output) } // Success getting the import, attempt to build again. } glog.Fatal("Not reachable") return nil, nil }