Commit e2f2721e authored by Kirill Smelkov's avatar Kirill Smelkov

go/internal/xtesting: Prepare to run user's tests with both py2 and py3

Tests, that want to verify something wrt both py2 and py3 will do
WithEachPy(tested_func) which will run tested_func two times:

1) under environment where python on $PATH points to python2, and
2) under environment where python on $PATH points to python3.

Client tests will now need to use just "python" instead of e.g.
"python2" or "python3" to run some python program, and each time
"python" will correspond to current phase of WithEachPy execution.

This will be soon handy to test things wrt e.g. ZEO/py2 and ZEO/py3
servers and similar situations.

Tests that merely want to use some python program just for their inner
working, for example to run `zodb commit`, no longer indicate their
preference for py2. For such tests it is a matter of preference in
pre-setup environment to where "python" points.

For the reference under environments created with py2py3-venv(*) default
"python" currently points to "python2".

(*) see nexedi/zodbtools@fac2f190
parent 84fed748
// Copyright (C) 2017-2021 Nexedi SA and Contributors. // Copyright (C) 2017-2024 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com> // Kirill Smelkov <kirr@nexedi.com>
// //
// This program is free software: you can Use, Study, Modify and Redistribute // This program is free software: you can Use, Study, Modify and Redistribute
...@@ -39,9 +39,35 @@ import ( ...@@ -39,9 +39,35 @@ import (
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
) )
// WithEachPy runs f for all Python versions we care about.
//
// For each version f is run separately under corresponding environment where
// `python` on $PATH runs that python version.
//
// We currently support the following versions:
//
// - python2
// - python3
func WithEachPy(t *testing.T, f func(t *testing.T)) {
tmpd := t.TempDir()
for major := 2; major <= 3; major++ {
t.Run(fmt.Sprintf("py%d", major), func(t *testing.T) {
err := os.WriteFile(tmpd + "/python", []byte("#!/bin/sh\n" +
fmt.Sprintf("exec python%d \"$@\"\n", major)), 0777)
if err != nil {
t.Fatal(err)
}
t.Setenv("PATH", fmt.Sprintf("%s%c%s", tmpd, os.PathListSeparator, os.Getenv("PATH")))
f(t)
})
}
}
var ( var (
pyMu sync.Mutex pyMu sync.Mutex
pyHave = map[string]bool{} // {} pymod -> y/n ; ".python" indicates python presence pyHaveByVer = map[string]map[string]bool{} // {} pyver -> {} pymod -> y/n
) )
// NeedPy skips current test if python and specified modules are not available. // NeedPy skips current test if python and specified modules are not available.
...@@ -59,24 +85,30 @@ func NeedPy(t testing.TB, modules ...string) { ...@@ -59,24 +85,30 @@ func NeedPy(t testing.TB, modules ...string) {
pyMu.Lock() pyMu.Lock()
defer pyMu.Unlock() defer pyMu.Unlock()
// verify if python is present // verify if python is present, retrieve its version
havePy, know := pyHave[".python"] //
if !know { // Under different WithEachPy executions python might point to
cmd := exec.Command("python2", "-c", "0") // different python versions, so we cannot cache whether we have python
err := cmd.Run() // or not. Retrieving `python --version` should be relatively fast - ~5ms -
havePy = (err == nil) // contrary to 100-200-300ms when running python code.
pyHave[".python"] = havePy cmd := exec.Command("python", "--version")
out, err := cmd.CombinedOutput() // py2 emits version to stderr, py3 to stdout
if (err != nil) {
t.Skipf("skipping: python is not available: %s\n%s", err, out)
} }
pyver := string(out) // e.g. "Python 2.7.18"
if !havePy { pyHave, ok := pyHaveByVer[pyver]
t.Skipf("skipping: python is not availble") if !ok {
pyHave = map[string]bool{}
pyHaveByVer[pyver] = pyHave
} }
var donthave []string var donthave []string
for _, pymod := range modules { for _, pymod := range modules {
have, know := pyHave[pymod] have, know := pyHave[pymod]
if !know { if !know {
cmd := exec.Command("python2", "-c", "import "+pymod) cmd := exec.Command("python", "-c", "import "+pymod)
err := cmd.Run() err := cmd.Run()
have = (err == nil) have = (err == nil)
pyHave[pymod] = have pyHave[pymod] = have
...@@ -125,7 +157,7 @@ func ZPyCommitRaw(zurl string, at zodb.Tid, objv ...ZRawObject) (_ zodb.Tid, err ...@@ -125,7 +157,7 @@ func ZPyCommitRaw(zurl string, at zodb.Tid, objv ...ZRawObject) (_ zodb.Tid, err
zin.WriteString("\n") zin.WriteString("\n")
// run py `zodb commit` // run py `zodb commit`
cmd:= exec.Command("python2", "-m", "zodbtools.zodb", "commit", zurl, at.String()) cmd:= exec.Command("python", "-m", "zodbtools.zodb", "commit", zurl, at.String())
cmd.Stdin = zin cmd.Stdin = zin
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
out, err := cmd.Output() out, err := cmd.Output()
...@@ -149,7 +181,7 @@ func ZPyRestore(zurl string, zin string) (tidv []zodb.Tid, err error) { ...@@ -149,7 +181,7 @@ func ZPyRestore(zurl string, zin string) (tidv []zodb.Tid, err error) {
defer xerr.Contextf(&err, "%s: zpyrestore", zurl) defer xerr.Contextf(&err, "%s: zpyrestore", zurl)
// run py `zodb restore` // run py `zodb restore`
cmd:= exec.Command("python2", "-m", "zodbtools.zodb", "restore", zurl) cmd:= exec.Command("python", "-m", "zodbtools.zodb", "restore", zurl)
cmd.Stdin = strings.NewReader(zin) cmd.Stdin = strings.NewReader(zin)
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
out, err := cmd.Output() out, err := cmd.Output()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment