Esempio n. 1
0
// 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)
}
Esempio n. 2
0
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()
}
Esempio n. 3
0
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)
	}
}
Esempio n. 4
0
// 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
}