How do you print in a Go test using the "testing" package? - testing

I'm running a test in Go with a statement to print something (i.e. for debugging of tests) but it's not printing anything.
func TestPrintSomething(t *testing.T) {
fmt.Println("Say hi")
}
When I run go test on this file, this is the output:
ok command-line-arguments 0.004s
The only way to really get it to print, as far as I know, is to print it via t.Error(), like so:
func TestPrintSomethingAgain(t *testing.T) {
t.Error("Say hi")
}
Which outputs this:
Say hi
--- FAIL: TestPrintSomethingAgain (0.00 seconds)
foo_test.go:35: Say hi
FAIL
FAIL command-line-arguments 0.003s
gom: exit status 1
I've Googled and looked through the manual but didn't find anything.

The structs testing.T and testing.B both have a .Log and .Logf method that sound to be what you are looking for. .Log and .Logf are similar to fmt.Print and fmt.Printf respectively.
See more details here: http://golang.org/pkg/testing/#pkg-index
fmt.X print statements do work inside tests, but you will find their output is probably not on screen where you expect to find it and, hence, why you should use the logging methods in testing.
If, as in your case, you want to see the logs for tests that are not failing, you have to provide go test the -v flag (v for verbosity). More details on testing flags can be found here: https://golang.org/cmd/go/#hdr-Testing_flags

For example,
package verbose
import (
"fmt"
"testing"
)
func TestPrintSomething(t *testing.T) {
fmt.Println("Say hi")
t.Log("Say bye")
}
go test -v
=== RUN TestPrintSomething
Say hi
--- PASS: TestPrintSomething (0.00 seconds)
v_test.go:10: Say bye
PASS
ok so/v 0.002s
Command go
Description of testing flags
-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
Package testing
func (*T) Log
func (c *T) Log(args ...interface{})
Log formats its arguments using default formatting, analogous to Println, and records the text in the error log. For tests, the text will be printed only if the test fails or the -test.v flag is set. For benchmarks, the text is always printed to avoid having performance depend on the value of the -test.v flag.

t.Log() will not show up until after the test is complete, so if you're trying to debug a test that is hanging or performing badly it seems you need to use fmt.
Yes: that was the case up to Go 1.13 (August 2019) included.
And that was followed in golang.org issue 24929
Consider the following (silly) automated tests:
func TestFoo(t *testing.T) {
t.Parallel()
for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(3 * time.Second)
}
}
func TestBar(t *testing.T) {
t.Parallel()
for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(2 * time.Second)
}
}
func TestBaz(t *testing.T) {
t.Parallel()
for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(1 * time.Second)
}
}
If I run go test -v, I get no log output until all of TestFoo is done, then no output until all of TestBar is done, and again no more output until all of TestBaz is done.
This is fine if the tests are working, but if there is some sort of bug, there are a few cases where buffering log output is problematic:
When iterating locally, I want to be able to make a change, run my tests, see what's happening in the logs immediately to understand what's going on, hit CTRL+C to shut the test down early if necessary, make another change, re-run the tests, and so on.
If TestFoo is slow (e.g., it's an integration test), I get no log output until the very end of the test. This significantly slows down iteration.
If TestFoo has a bug that causes it to hang and never complete, I'd get no log output whatsoever. In these cases, t.Log and t.Logf are of no use at all.
This makes debugging very difficult.
Moreover, not only do I get no log output, but if the test hangs too long, either the Go test timeout kills the test after 10 minutes, or if I increase that timeout, many CI servers will also kill off tests if there is no log output after a certain amount of time (e.g., 10 minutes in CircleCI).
So now my tests are killed and I have nothing in the logs to tell me what happened.
But for (possibly) Go 1.14 (Q1 2020): CL 127120
testing: stream log output in verbose mode
The output now is:
=== RUN TestFoo
=== PAUSE TestFoo
=== RUN TestBar
=== PAUSE TestBar
=== RUN TestBaz
=== PAUSE TestBaz
=== CONT TestFoo
=== CONT TestBaz
main_test.go:30: 0
=== CONT TestFoo
main_test.go:12: 0
=== CONT TestBar
main_test.go:21: 0
=== CONT TestBaz
main_test.go:30: 1
main_test.go:30: 2
=== CONT TestBar
main_test.go:21: 1
=== CONT TestFoo
main_test.go:12: 1
=== CONT TestBaz
main_test.go:30: 3
main_test.go:30: 4
=== CONT TestBar
main_test.go:21: 2
=== CONT TestBaz
main_test.go:30: 5
=== CONT TestFoo
main_test.go:12: 2
=== CONT TestBar
main_test.go:21: 3
=== CONT TestBaz
main_test.go:30: 6
main_test.go:30: 7
=== CONT TestBar
main_test.go:21: 4
=== CONT TestBaz
main_test.go:30: 8
=== CONT TestFoo
main_test.go:12: 3
=== CONT TestBaz
main_test.go:30: 9
=== CONT TestBar
main_test.go:21: 5
=== CONT TestBaz
main_test.go:30: 10
main_test.go:30: 11
=== CONT TestFoo
main_test.go:12: 4
=== CONT TestBar
main_test.go:21: 6
=== CONT TestBaz
main_test.go:30: 12
main_test.go:30: 13
=== CONT TestBar
main_test.go:21: 7
=== CONT TestBaz
main_test.go:30: 14
=== CONT TestFoo
main_test.go:12: 5
--- PASS: TestBaz (15.01s)
=== CONT TestBar
main_test.go:21: 8
=== CONT TestFoo
main_test.go:12: 6
=== CONT TestBar
main_test.go:21: 9
main_test.go:21: 10
=== CONT TestFoo
main_test.go:12: 7
=== CONT TestBar
main_test.go:21: 11
=== CONT TestFoo
main_test.go:12: 8
=== CONT TestBar
main_test.go:21: 12
main_test.go:21: 13
=== CONT TestFoo
main_test.go:12: 9
=== CONT TestBar
main_test.go:21: 14
=== CONT TestFoo
main_test.go:12: 10
--- PASS: TestBar (30.01s)
=== CONT TestFoo
main_test.go:12: 11
main_test.go:12: 12
main_test.go:12: 13
main_test.go:12: 14
--- PASS: TestFoo (45.02s)
PASS
ok command-line-arguments 45.022s
It is indeed in Go 1.14, as Dave Cheney attests in "go test -v streaming output":
In Go 1.14, go test -v will stream t.Log output as it happens, rather than hoarding it til the end of the test run.
Under Go 1.14 the fmt.Println and t.Log lines are interleaved, rather than waiting for the test to complete, demonstrating that test output is streamed when go test -v is used.
Advantage, according to Dave:
This is a great quality of life improvement for integration style tests that often retry for long periods when the test is failing.
Streaming t.Log output will help Gophers debug those test failures without having to wait until the entire test times out to receive their output.

For testing sometimes I do
fmt.Fprintln(os.Stdout, "hello")
Also, you can print to:
fmt.Fprintln(os.Stderr, "hello")

t.Log and t.Logf do print out in your test but can often be missed as it prints on the same line as your test. What I do is Log them in a way that makes them stand out, ie
t.Run("FindIntercomUserAndReturnID should find an intercom user", func(t *testing.T) {
id, err := ic.FindIntercomUserAndReturnID("test3#test.com")
assert.Nil(t, err)
assert.NotNil(t, id)
t.Logf("\n\nid: %v\n\n", *id)
})
which prints it to the terminal as,
=== RUN TestIntercom
=== RUN TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user
TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user: intercom_test.go:34:
id: 5ea8caed05a4862c0d712008
--- PASS: TestIntercom (1.45s)
--- PASS: TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user (1.45s)
PASS
ok github.com/RuNpiXelruN/third-party-delete-service 1.470s

WARNING: The answers here do not apply when testing multiple packages at once.
The answers from #VonC and #voidlogic are fantastic, but I wanted to bring the following thread to attention in case someone is running a permutation of go test -v ./...: https://github.com/golang/go/issues/46959
The issue lies in implementation nuances/difficulties related to running tests from multiple packages.
For example, running go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./multiple/packages/exist/below/... will only print logs after the test completes.
However, running go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./this/path/points/to/one/package/only/... will stream the output as expected.

In case your using testing.M and associated setup/teardown; -v is valid here as well.
package g
import (
"os"
"fmt"
"testing"
)
func TestSomething(t *testing.T) {
t.Skip("later")
}
func setup() {
fmt.Println("setting up")
}
func teardown() {
fmt.Println("tearing down")
}
func TestMain(m *testing.M) {
setup()
result := m.Run()
teardown()
os.Exit(result)
}
$ go test -v g_test.go
setting up
=== RUN TestSomething
g_test.go:10: later
--- SKIP: TestSomething (0.00s)
PASS
tearing down
ok command-line-arguments 0.002s

The *_test.go file is a Go source like the others, you can initialize a new logger every time if you need to dump complex data structure, here an example:
// initZapLog is delegated to initialize a new 'log manager'
func initZapLog() *zap.Logger {
config := zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
return logger
}
Then, every time, in every test:
func TestCreateDB(t *testing.T) {
loggerMgr := initZapLog()
// Make logger avaible everywhere
zap.ReplaceGlobals(loggerMgr)
defer loggerMgr.Sync() // flushes buffer, if any
logger := loggerMgr.Sugar()
logger.Debug("START")
conf := initConf()
/* Your test here
if false {
t.Fail()
}*/
}

Related

Can Golang test scripts issue warnings rather than errors?

I have a set of tests that may not pass due to external 3rd party issues.
I don't want the test to fail when this condition occurs but would like to be made aware.
Issuing a t.Errorf() is not idea because it will stop all subsequent tests. Is there some kind of "Warning" I can trigger that the test script would post and then continue with the remainder of the tests?
The go test tool is like the compiler. To the compiler something either compiles or doesn't, there are no warnings. I think the closest thing you're going to get is to use t.Skip. It will stop execution of the current test but does not mark it as failed. You will not see anything in the output of go test however so you have to use go test -v.
Here's an example package that uses t.Skipf if the addExternal function fails.
package app
import "testing"
func add(a, b int) int {
return a + b
}
func addExternal(a, b int) int {
return 4
}
func divide(a, b int) int {
return a / b
}
func TestThing(t *testing.T) {
got := add(1, 2)
want := 3
if got != want {
t.Errorf("add(1, 2) = %d, want %d", got, want)
}
}
func TestExternalThing(t *testing.T) {
got := addExternal(3, 4)
want := 7
if got != want {
t.Skipf("addExternal(3, 4) = %d, want %d", got, want)
}
}
func TestAnotherThing(t *testing.T) {
got := divide(6, 3)
want := 2
if got != want {
t.Errorf("divide(6, 3) = %d, want %d", got, want)
}
}
And here's the output from running that. Note the return status is 0 and the package is considered to have passed
$ go test -v
=== RUN TestThing
--- PASS: TestThing (0.00s)
=== RUN TestExternalThing
--- SKIP: TestExternalThing (0.00s)
app_test.go:29: addExternal(3, 4) = 4, want 7
=== RUN TestAnotherThing
--- PASS: TestAnotherThing (0.00s)
PASS
ok github.com/jcbwlkr/app 0.006s
$ echo $?
0
Note though that if I change the t.Skipf to t.Errorf or t.Fatalf I get this output
$ go test -v
=== RUN TestThing
--- PASS: TestThing (0.00s)
=== RUN TestExternalThing
--- FAIL: TestExternalThing (0.00s)
app_test.go:29: addExternal(3, 4) = 4, want 7
=== RUN TestAnotherThing
--- PASS: TestAnotherThing (0.00s)
FAIL
exit status 1
FAIL github.com/jcbwlkr/app 0.005s
$ echo $?
1
The other tests in the package are still ran. If I was testing multiple packages such as with go test -v ./... I believe they would also still be ran.

how to deal with the "fmt" golang library package for CLI testing

Disclaimer: I wish you a merry XMas and I hope my question does not disturb you!
sample.go:
package main
import(
"fmt"
"os"
)
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Println("Error: something is wrong!")
os.Exit(1)
} else {
fmt.Println("May the force be with you!")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: something is wrong!
// exit status 1
I did a lot of research on how to use interfaces in golang testing. But so far I was not able to wrap my head around this completely. At least I can not see how interfaces help me when I need to "mock" (apologies for using this word) golang std. library packages like "fmt".
I came up with two scenarios:
use os/exec to test the command line interface
wrap fmt package so I have control and am able to check the output strings
I do not like both scenarios:
I experience going through the actual command line a convoluted and not-performant (see below). Might have portability issues, too.
I believe this is the way to go but I fear that wrapping the fmt package might be a lot of work (at least wrapping the time package for testing turned out a non-trivial task (https://github.com/finklabs/ttime)).
Actual Question here: Is there another (better/simpler/idiomatic) way?
Note: I want to do this in pure golang, I am not interested in the next testing framework.
cli_test.go:
package main
import(
"os/exec"
"testing"
)
func TestCli(t *testing.T) {
out, err := exec.Command("go run sample.go").Output()
if err != nil {
t.Fatal(err)
}
if string(out) != "May the force be with you!\nError: this is broken and not useful!\nexit status 1" {
t.Fatal("There is something wrong with the CLI")
}
}
Chapter 11 of Kerningham's Book gives a good solution to this question.
The trick is to change the calls to fmt.Printline() to calls to
fmt.Fprint(out, ...) where out is initialised to os.Stdout
This can be overwritten in the test harness to new(bytes.Buffer) allowing the
test to capture the output.
See https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo.go and
https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo_test.go
edited by OP...
sample.go:
package main
import(
"fmt"
"os"
"io"
)
var out io.Writer = os.Stdout // modified during testing
var exit func(code int) = os.Exit
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Fprint(out, "Error: something is wrong!\n")
exit(1)
} else {
fmt.Fprint(out, "May the force be with you!\n")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: this is broken and not useful!
// exit status 1
cli_test.go:
package main
import(
"bytes"
"testing"
)
func TestUsefulPositive(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
s := sample{42}
s.useful()
if out.(*bytes.Buffer).String() != "May the force be with you!\n" {
t.Fatal("There is something wrong with the CLI")
}
}
func TestUsefulNegative(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
code := 0
osexit := exit
exit = func(c int) { code = c }
defer func() { exit = osexit }()
s := sample{0}
s.useful()
if out.(*bytes.Buffer).String() != "Error: something is wrong!\n" {
t.Fatal("There is something wrong with the CLI")
}
if code != 1 {
t.Fatal("Wrong exit code!")
}
}
Am I missing something here or are you talking of testable examples?
Basically, it works like this: In a *_test.go file, you need to adhere to the convention Example[[T][_M]] where T is a placeholder for the type and M a placeholder for the method you want to display the testable example as example code in the Godoc. If the function is just called Example(), the code will be shown as a package example.
Below the last line of the code of your example, you can put a comment like this
// Output:
// Foo
Now go test will make sure that the testable example function either exactly puts out everything below // Output: (including whitespace) or it will make the test fail.
Here is an actual example for an testable example
func ExampleMongoStore_Get() {
sessionId := "ExampleGetSession"
data, err := ms.Get(sessionId)
if err == sessionmw.ErrSessionNotFound {
fmt.Printf("Session '%s' not found\n", sessionId)
data = make(map[string]interface{})
data["foo"] = "bar"
ms.Save(sessionId, data)
}
loaded, _ := ms.Get(sessionId)
fmt.Printf("Loaded value '%s' for key '%s' in session '%s'",
loaded["foo"],
"foo", sessionId)
// Output:
// Session 'ExampleGetSession' not found
// Loaded value 'bar' for key 'foo' in session 'ExampleGetSession'
}
Edit: Have a look at the output of above example at godoc.org

Tests panic when HTTP server starts after old one is closed

I have some tests written, each function starts iron's HTTP server and closes it after test is done:
extern crate iron;
use iron::prelude::*;
fn hello_world(_: &mut Request) -> IronResult<Response> {
Ok(Response::with((iron::status::Ok, "Hello World")))
}
#[test]
fn test1() {
let mut server = Iron::new(hello_world).http("localhost:3000").unwrap();
server.close().unwrap();
}
#[test]
fn test2() {
let mut server = Iron::new(hello_world).http("localhost:3000").unwrap();
server.close().unwrap();
}
Doing cargo test I'm having:
Running target/debug/lib-f236975fe924352b
running 2 tests
test test1 ... ok
test test2 ... FAILED
failures:
---- test2 stdout ----
thread 'test2' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Error { repr: Os { code: 98, message: "Address already in use" } })', ../src/libcore/result.rs:736
failures:
test2
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
It seems like the port is still in use by the start of the second test.
Rust's test runner is parallel by default, so yes, I would expect that this would happen. I would pick a different port number for each test, at least to start, since it's so easy.

How to fix line numbers in Go test output?

Let's consider this simple testing code.
(Note: assertSomething is super simple here, but normally I'd write a more specialised helper for the task at hand that would look at multiple things and could report more than one type of error.)
package hello
import "testing"
func TestFoo(t *testing.T) {
assertSomething(t, 2+2 == 4) // line 6
assertSomething(t, 2+3 == 6) // line 7
}
func assertSomething(t *testing.T, expected bool) {
if !expected {
t.Error("Something's not right") // line 12
}
}
When I run go test, I get the following:
--- FAIL: TestFoo (0.00s)
hello.go:12: Something's not right
FAIL
exit status 1
FAIL kos/hello 0.008s
I have two questions:
1) The error points to line 12 - why? How does t.Error find out which line it was called from?
2) In the helper, I'd like to specify that t.Error should look stack level higher to determine the line number to print, so that I would get a message like this:
--- FAIL: TestFoo (0.00s)
hello.go:7: Something's not right
Python allows me to do this, for instance, in warnings.warn("message", stacklevel=2) - how would I implement the equivalent here?
Things have changed since go 1.9.
Helper() method has been added to testing.T and testing.B. It's intended to be invoked from testing helpers such as assertSomething to indicate the function is a helper and we're not interested in line numbers coming from it.
package main
import "testing"
func TestFoo(t *testing.T) {
assertSomething(t, 2+2 == 4) // line 6
assertSomething(t, 2+3 == 6) // line 7
}
func assertSomething(t *testing.T, expected bool) {
if !expected {
t.Helper()
t.Error("Something's not right") // line 12
}
}
The output contains correct line numbers:
=== RUN TestFoo
--- FAIL: TestFoo (0.00s)
main.go:7: Something's not right
FAIL
You can also try it on Go Playground.
You can do what you're asking, and you can find out how t.Error works by looking at the source code. The function decorate is what you're looking for I think.
But, in the case where you have significant amounts of checking code, and for some reason it's getting duplicated in your test, it's better to extract that as a function that returns an error than passing in a testing.T and making it an "assertion". Indeed, writing assertion functions is explicitly discouraged in the language FAQ.
package hello
import "testing"
func TestFoo(t *testing.T) {
if err := checkSomething(2+2 == 4); err != nil {
t.Errorf("2+2=4 failed: %s", err)
}
if err := checkSomething(2+3 == 6); err != nil {
t.Errorf("2+3=6 failed: %s", err)
}
}
func checkSomething(v bool) error {
if !v {
return errors.New("something's not right")
}
return nil
}
But here's what I think idiomatic testing code would look like. It's table-driven, and the cases include inputs and expected output, leading to really clear error messages when the tests fail.
package hello
import "testing"
func TestFoo(t *testing.T) {
cases := []struct {
a, b, want int
}{
{2, 2, 4},
{2, 3, 6},
}
for _, c := range cases {
if got := operation(c.a, c.b); got != c.want {
t.Errorf("operation(%d, %d) = %d, want %d", c.a, c.b, got, c.want)
}
}
}
func operation(a, b int) int {
return a + b
}

Check if a process exists in go way

If I have the PID of a process, is os.FindProcess enough to test for the existing of the process? I mean if it returns err can I assume that it's terminated (or killed)?
Edit:
I've just wrote a wrapper function around kill -s 0 (old-style bash process testing). This works without any problem, but I'm still happy if there is other solutions (done with go libraries) to this problem.:
func checkPid(pid int) bool {
out, err := exec.Command("kill", "-s", "0", strconv.Itoa(pid)).CombinedOutput()
if err != nil {
log.Println(err)
}
if string(out) == "" {
return true // pid exist
}
return false
}
Here is the traditional unix way to see if a process is alive - send it a signal of 0 (like you did with your bash example).
From kill(2):
If sig is 0, then no signal is sent, but error checking is still per‐
formed; this can be used to check for the existence of a process ID or
process group ID.
And translated into Go
package main
import (
"fmt"
"log"
"os"
"strconv"
"syscall"
)
func main() {
for _, p := range os.Args[1:] {
pid, err := strconv.ParseInt(p, 10, 64)
if err != nil {
log.Fatal(err)
}
process, err := os.FindProcess(int(pid))
if err != nil {
fmt.Printf("Failed to find process: %s\n", err)
} else {
err := process.Signal(syscall.Signal(0))
fmt.Printf("process.Signal on pid %d returned: %v\n", pid, err)
}
}
}
When you run it you get this, showing that process 123 is dead, process 1 is alive but not owned by you and process 12606 is alive and owned by you.
$ ./kill 1 $$ 123
process.Signal on pid 1 returned: operation not permitted
process.Signal on pid 12606 returned: <nil>
process.Signal on pid 123 returned: no such process
On unix like systems (linux, freebsd, etc) os.FindProcess will never return an error. I don't know what happens on Windows. This means you won't know if the PID is correct until you try to use the *os.Process for something.
You can look at the code here.
You can also just use syscall.Kill. It amounts to less code.
killErr := syscall.Kill(pid, syscall.Signal(0))
procExists := killErr == nil
If a previously known pid is not found in the system (not sure of go functions), it means process has definitely terminated and has been joined (on Unix, with wait call) too.
But other way around is not necessarily true. Just because a pid exists, it does not quarantee it is same process as before. There are only 65535 valid pids in standard Linux for example, and they can get re-used when there is a wrap-around. However, if you check reasonably often, for practical purposes you don't need to care about this (as long as pid of wrong new process being found is not a security vulnerability or something else critical, which somebody might try to trigger intentionally for malicious purposes).
Related links (and Related questions on their right columns):
https://superuser.com/questions/135007/how-are-pids-generated
https://unix.stackexchange.com/questions/26677/will-process-ids-be-recycled-what-if-you-reach-the-maximal-id
All the answers so far are incomplete implementations. See https://github.com/shirou/gopsutil/blob/c141152a7b8f59b63e060fa8450f5cd5e7196dfb/process/process_posix.go#L73 for a more complete implementation (copied inline)
func PidExists(pid int32) (bool, error) {
if pid <= 0 {
return false, fmt.Errorf("invalid pid %v", pid)
}
proc, err := os.FindProcess(int(pid))
if err != nil {
return false, err
}
err = proc.Signal(syscall.Signal(0))
if err == nil {
return true, nil
}
if err.Error() == "os: process already finished" {
return false, nil
}
errno, ok := err.(syscall.Errno)
if !ok {
return false, err
}
switch errno {
case syscall.ESRCH:
return false, nil
case syscall.EPERM:
return true, nil
}
return false, err
}
Here is one way of how to check if a process exists/running with Golang on Windows.
We execute the command:
TASKLIST /V /NH /FI "PID eq 23232"
Which can return either:
INFO: No tasks are running which match the specified criteria.
Or if found:
Image Name PID Session Name Session# Mem Usage Status User Name CPU Time Window Title
========================= ======== ================ =========== ============ =============== ================================================== ============ ========================================================================
chrome.exe 23232 Console 1 42,472 K Unknown THANOS\MARVEL 0:00:00 N/A
Here is a function that takes advantage of this information.
func isProcessRunning(pid int) bool {
cmd := exec.Command("TASKLIST", "/FI", fmt.Sprintf("PID eq %d", pid))
result, err := cmd.Output()
if err != nil {
return false
}
return !bytes.Contains(result, []byte("No tasks are running"))
}
The best thing about this is you can find the process by other parameters too:
ImageName eq, ne Image Name String
PID eq, ne, gt, lt, ge, le Process ID, A Positive integer.
Session eq, ne, gt, lt, ge, le Any valid session number.
SessionName eq, ne String
Status eq, ne RUNNING | NOT RESPONDING | UNKNOWN
CPUTime eq, ne, gt, lt, ge, le Time hh:mm:ss
MemUsage eq, ne, gt, lt, ge, le Memory usage in KB, specify a valid integer.
Username eq, ne User name ([Domain\]User).
Services eq, ne Service Name String
Windowtitle eq, ne Window Title String
Modules eq, ne DLL Name String
On Windows checking the result of os.FindProcess() seems to be enough to check if process is running.
func isProcessRunning(pid int) bool {
_, err = os.FindProcess(pid)
if err != nil {
return false
}
if runtime.GOOS == "windows" {
return true
}
return false // further checking for other systems then Windows is not supported here
}
After searching for a few hours, the correct answer to know if a process is running on Windows is the following:
func CheckProcessLife(pid int){
cmd,_ := exec.Command("tasklist","/FI", "PID eq " + strconv.Itoa(pid)).Output()
output := string(cmd[:])
splitOutp := strings.Split(output, " ")
if !(splitOutp[1] == "no") {
time.Sleep(500 * time.Millisecond)
fmt.Println("Process is running...")
CheckProcessLife(pid)
}else{
fmt.Println("Process is no longer running.")
}
}
You can check if the process is running with his PID or directly with his name, only change this line:
cmd,_ := exec.Command("tasklist","/FI", "IMAGENAME eq yourprocessname.exe").Output()