Skip to content

Commit

Permalink
Test xfidopen and xfidclose
Browse files Browse the repository at this point in the history
Also fix crash due to Window.ctrllock not being initialized.
  • Loading branch information
fhs authored and rjkroege committed Aug 10, 2019
1 parent 85eee89 commit 53c949e
Show file tree
Hide file tree
Showing 3 changed files with 294 additions and 15 deletions.
6 changes: 3 additions & 3 deletions wind.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Window struct {
addr Range
limit Range

nopen [QMAX]byte
nopen [QMAX]byte // number of open Fid for each file in the file server
nomark bool
wrselrange Range
rdselfd *os.File // temporary file for rdsel read requests
Expand All @@ -38,12 +38,12 @@ type Window struct {
eventx *Xfid
events []byte

owner int
owner int // TODO(fhs): change type to rune
maxlines int
dirnames []string
widths []int
incl []string
ctrllock *sync.Mutex
ctrllock sync.Mutex
ctlfid uint32
dumpstr string
dumpdir string
Expand Down
28 changes: 16 additions & 12 deletions xfid.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ out:
x.respond(&fc, nil)
}

// These variables are only used for testing.
var (
testTempFileFail bool
testIOCopyFail bool
)

func xfidopen(x *Xfid) {
// log.Println("xfidopen", x)
// defer log.Println("xfidopen done")
Expand All @@ -92,9 +98,7 @@ func xfidopen(x *Xfid) {
w.limit = Range{-1, -1}
}
w.nopen[q]++
case QWdata:
fallthrough
case QWxdata:
case QWdata, QWxdata:
w.nopen[q]++
case QWevent:
if w.nopen[q] == 0 {
Expand All @@ -116,22 +120,23 @@ func xfidopen(x *Xfid) {
x.respond(&fc, ErrInUse)
return
}
var err error
// TODO(flux): Move the TempFile and Remove
// into a tempfile() call
w.rdselfd, err = ioutil.TempFile("", "acme")
if err != nil {
tmp, err := ioutil.TempFile("", "acme")
if err != nil || testTempFileFail {
w.Unlock()
x.respond(&fc, fmt.Errorf("can't create temp file"))
return
}
os.Remove(w.rdselfd.Name()) // tempfile ORCLOSE
os.Remove(tmp.Name()) // tempfile ORCLOSE
w.nopen[q]++

_, err = io.Copy(w.rdselfd, t.file.b.Reader(t.q0, t.q1))
if err != nil {
_, err = io.Copy(tmp, t.file.b.Reader(t.q0, t.q1))
if err != nil || testIOCopyFail {
// TODO(fhs): Do we want to send an error response to the client?
warning(nil, fmt.Sprintf("can't write temp file for pipe command %v\n", err))
}
w.rdselfd = tmp
case QWwrsel:
w.nopen[q]++
seq++
Expand Down Expand Up @@ -204,9 +209,7 @@ func xfidclose(x *Xfid) {
w.ctlfid = MaxFid
w.ctrllock.Unlock()
}
case QWdata:
fallthrough
case QWxdata:
case QWdata, QWxdata:
w.nomark = false
fallthrough
case QWaddr:
Expand Down Expand Up @@ -575,6 +578,7 @@ forloop:
w.ctlfid = x.f.fid
case "unlock": // release exclusive use
w.ctlfid = math.MaxUint32
// BUG(fhs): This will crash if the lock isn't already locked.
w.ctrllock.Unlock()
case "clean": // mark window 'clean', seq=0
t = &w.body
Expand Down
275 changes: 275 additions & 0 deletions xfid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
Expand Down Expand Up @@ -118,6 +119,9 @@ func TestQWrdsel(t *testing.T) {
if mr.err != nil {
t.Fatalf("got error %v; want nil", mr.err)
}
if w.rdselfd == nil {
t.Fatalf("w.rdselfd is nil after open")
}
if got, want := mr.fcall.Qid, qid; !cmp.Equal(got, want) {
t.Fatalf("Qid.Path is %v; want %v", got, want)
}
Expand Down Expand Up @@ -192,6 +196,277 @@ func TestXfidwriteQWaddr(t *testing.T) {
}
}

func TestXfidopen(t *testing.T) {
for _, tc := range []struct {
name string
q uint64
}{
{"QWaddr", QWaddr},
{"QWdata", QWdata},
{"QWxdata", QWxdata},
{"QWevent", QWevent},
{"QWrdsel", QWrdsel},
{"QWwrsel", QWwrsel},
{"QWeditout", QWeditout},
{"Qlog", Qlog},
{"Qeditout", Qeditout},
} {
t.Run(tc.name, func(t *testing.T) {
editing = Inserting // for QWeditout
mr := new(mockResponder)
var w *Window
q := tc.q
if q != Qlog && q != Qeditout {
w = NewWindow().initHeadless(nil)
w.col = new(Column)
w.body.fr = &MockFrame{}
}
x := &Xfid{
f: &Fid{
qid: plan9.Qid{Path: QID(0, q)},
w: w,
},
fs: mr,
}
xfidopen(x)

switch q {
case QWaddr:
if got, want := w.addr, (Range{0, 0}); !reflect.DeepEqual(got, want) {
t.Errorf("w.addr is %v; want %v", got, want)
}
if got, want := w.limit, (Range{-1, -1}); !reflect.DeepEqual(got, want) {
t.Errorf("w.limit is %v; want %v", got, want)
}
}

switch q {
case QWeditout, Qlog, Qeditout: // Do nothing.
default:
if got, want := w.nopen[q], byte(1); got != want {
t.Errorf("w.nopen[%v] is %v; want %v", q, got, want)
}
}
if mr.err != nil {
t.Fatalf("got error %v; want nil", mr.err)
}
if got, want := mr.fcall.Qid, (plan9.Qid{Path: QID(0, q)}); !cmp.Equal(got, want) {
t.Errorf("Fcall.Qid is %#v; want %#v", got, want)
}
if got, want := mr.fcall.Iounit, uint32(8168); got != want {
t.Errorf("Fcall.Iounit is %v; want %v", got, want)
}
if !x.f.open {
t.Errorf("fid not open")
}
})
}
}

func TestXfidopenQeditout(t *testing.T) {
mr := new(mockResponder)
x := &Xfid{
f: &Fid{
qid: plan9.Qid{Path: QID(0, Qeditout)},
},
fs: mr,
}
editoutlk = nil
xfidopen(x)
if got, want := mr.err, ErrInUse; got != want {
t.Errorf("got error %v; want %v", got, want)
}
}

func TestXfidopenQWeditout(t *testing.T) {
mr := new(mockResponder)
x := &Xfid{
f: &Fid{
qid: plan9.Qid{Path: QID(0, QWeditout)},
w: NewWindow().initHeadless(nil),
},
fs: mr,
}
t.Run("ErrInUse", func(t *testing.T) {
x.f.w.editoutlk = nil
xfidopen(x)
if got, want := mr.err, ErrInUse; got != want {
t.Errorf("got error %v; want %v", got, want)
}
})
t.Run("ErrPermission", func(t *testing.T) {
editing = Inactive
xfidopen(x)
if got, want := mr.err, ErrPermission; got != want {
t.Errorf("got error %v; want %v", got, want)
}
})
}

func TestXfidopenQWrdsel(t *testing.T) {
mr := new(mockResponder)
x := &Xfid{
f: &Fid{
qid: plan9.Qid{Path: QID(0, QWrdsel)},
w: NewWindow().initHeadless(nil),
},
fs: mr,
}
t.Run("ErrInUse", func(t *testing.T) {
x.f.w.rdselfd = os.Stdout // any non-nil file will do
xfidopen(x)
if got, want := mr.err, ErrInUse; got != want {
t.Errorf("got error %v; want %v", got, want)
}
})
t.Run("TempFile", func(t *testing.T) {
x.f.w.rdselfd = nil
testTempFileFail = true
defer func() { testTempFileFail = false }()
xfidopen(x)
if mr.err == nil {
t.Errorf("got nil error")
}
if got, want := mr.err.Error(), "can't create temp file"; got != want {
t.Errorf("got error %v; want %v", got, want)
}
if x.f.w.rdselfd != nil {
t.Errorf("non-nil w.rdselfd %v", x.f.w.rdselfd)
}
})
t.Run("CopyFail", func(t *testing.T) {
x.f.w.rdselfd = nil
warnings = nil
testIOCopyFail = true
defer func() {
warnings = nil
testIOCopyFail = false
}()
xfidopen(x)
if mr.err != nil {
t.Errorf("got error %q; want nil", mr.err)
}
if len(warnings) == 0 {
t.Fatalf("not warning generated")
}
got := string(warnings[0].buf)
want := "can't write temp file for pipe command"
if !strings.HasPrefix(got, want) {
t.Errorf("got warning %q; want prefix %q", got, want)
}
})
}

func TestXfidclose(t *testing.T) {
t.Run("NotOpen", func(t *testing.T) {
mr := new(mockResponder)
w := NewWindow().initHeadless(nil)
w.tag.fr = &MockFrame{}
w.body.fr = &MockFrame{}
x := &Xfid{
f: &Fid{
qid: plan9.Qid{Path: QID(0, QWdata)},
w: w,
},
fs: mr,
}
xfidclose(x)
if mr.err != nil {
t.Errorf("got error %v", mr.err)
}
})

for _, tc := range []struct {
name string
q uint64
}{
{"QWctl", QWctl},
{"QWaddr", QWaddr},
{"QWdata", QWdata},
{"QWxdata", QWxdata},
{"QWevent", QWevent},
{"QWrdsel", QWrdsel},
{"QWwrsel", QWwrsel},
{"QWeditout", QWeditout},
{"Qeditout", Qeditout},
} {
t.Run(tc.name, func(t *testing.T) {
tmpfile, err := ioutil.TempFile("", "edwood")
if err != nil {
t.Fatalf("can't create temporary file: %v", err)
}
defer os.Remove(tmpfile.Name())

mr := new(mockResponder)
var w *Window
q := tc.q
if q != Qeditout {
w = NewWindow().initHeadless(nil)
w.tag.fr = &MockFrame{}
w.body.fr = &MockFrame{}
w.body.display = edwoodtest.NewDisplay()
w.col = new(Column)
w.rdselfd = tmpfile
w.nomark = true
w.nopen[q] = 1
w.dumpstr = "win"
w.dumpdir = "/home/gopher"
w.ctlfid = 0
w.ctrllock.Lock()
close(w.editoutlk) // prevent block on send
}
editoutlk = make(chan bool)
close(editoutlk) // prevent block on send
x := &Xfid{
f: &Fid{
qid: plan9.Qid{
Path: QID(0, q),
},
w: w,
open: true,
},
fs: mr,
}
xfidclose(x)
if mr.err != nil {
t.Errorf("got error %v", mr.err)
}

switch q {
case QWctl:
if got, want := w.ctlfid, uint32(MaxFid); got != want {
t.Errorf("w.ctlfid is %v; want %v", got, want)
}
case QWdata, QWxdata:
if w.nomark != false {
t.Errorf("w.nomark is true")
}
fallthrough
case QWaddr, QWevent:
if got, want := w.nopen[q], byte(0); got != want {
t.Errorf("w.nopen[%v] is %v; want %v", q, got, want)
}
if q == QWevent {
if w.dumpstr != "" {
t.Errorf("w.dumpstr is %q", w.dumpstr)
}
if w.dumpdir != "" {
t.Errorf("w.dumpdir is %q", w.dumpdir)
}
}
case QWrdsel:
if w.rdselfd != nil {
t.Errorf("w.rdselfd is not nil")
}
case QWwrsel:
if w.nomark != false {
t.Errorf("w.nomark is true")
}
}
})
}
}

func TestXfidruneread(t *testing.T) {
tt := []struct {
body []rune // window body
Expand Down

0 comments on commit 53c949e

Please sign in to comment.