@@ -254,6 +254,76 @@ func TestDaemonRestart(t *testing.T) {
254254 <- statusC
255255}
256256
257+ func TestContainerPTY (t * testing.T ) {
258+ t .Parallel ()
259+
260+ client , err := newClient (t , address )
261+ if err != nil {
262+ t .Fatal (err )
263+ }
264+ defer client .Close ()
265+
266+ var (
267+ image Image
268+ ctx , cancel = testContext ()
269+ id = t .Name ()
270+ )
271+ defer cancel ()
272+
273+ image , err = client .GetImage (ctx , testImage )
274+ if err != nil {
275+ t .Fatal (err )
276+ }
277+
278+ container , err := client .NewContainer (ctx , id , WithNewSpec (oci .WithImageConfig (image ), oci .WithTTY , withProcessArgs ("echo" , "hello" )), WithNewSnapshot (id , image ))
279+ if err != nil {
280+ t .Fatal (err )
281+ }
282+ defer container .Delete (ctx , WithSnapshotCleanup )
283+
284+ direct , err := newDirectIOWithTerminal (ctx )
285+ if err != nil {
286+ t .Fatal (err )
287+ }
288+ defer direct .Delete ()
289+ var (
290+ wg sync.WaitGroup
291+ buf = bytes .NewBuffer (nil )
292+ )
293+ wg .Add (1 )
294+ go func () {
295+ defer wg .Done ()
296+ io .Copy (buf , direct .Stdout )
297+ }()
298+
299+ task , err := container .NewTask (ctx , direct .IOCreate )
300+ if err != nil {
301+ t .Fatal (err )
302+ }
303+ defer task .Delete (ctx )
304+
305+ status , err := task .Wait (ctx )
306+ if err != nil {
307+ t .Error (err )
308+ }
309+
310+ if err := task .Start (ctx ); err != nil {
311+ t .Fatal (err )
312+ }
313+
314+ <- status
315+ wg .Wait ()
316+
317+ if err := direct .Close (); err != nil {
318+ t .Error (err )
319+ }
320+
321+ out := buf .String ()
322+ if ! strings .ContainsAny (fmt .Sprintf ("%#q" , out ), `\x00` ) {
323+ t .Fatal (`expected \x00 in output` )
324+ }
325+ }
326+
257327func TestContainerAttach (t * testing.T ) {
258328 t .Parallel ()
259329
@@ -290,7 +360,7 @@ func TestContainerAttach(t *testing.T) {
290360
291361 expected := "hello" + newLine
292362
293- direct , err := newDirectIO (ctx )
363+ direct , err := newDirectIOStandard (ctx )
294364 if err != nil {
295365 t .Fatal (err )
296366 }
@@ -359,12 +429,24 @@ func TestContainerAttach(t *testing.T) {
359429 }
360430}
361431
362- func newDirectIO (ctx context.Context ) (* directIO , error ) {
432+ func newDirectIOStandard (ctx context.Context ) (* directIO , error ) {
433+ return newDirectIO (ctx , false )
434+ }
435+
436+ func newDirectIOWithTerminal (ctx context.Context ) (* directIO , error ) {
437+ return newDirectIO (ctx , true )
438+ }
439+
440+ func newDirectIO (ctx context.Context , terminal bool ) (* directIO , error ) {
363441 fifos , err := cio .NewFIFOSetInDir ("" , "" , false )
364442 if err != nil {
365443 return nil , err
366444 }
367- dio , err := cio .NewDirectIO (ctx , fifos )
445+ f := cio .NewDirectIO
446+ if terminal {
447+ f = cio .NewDirectIOWithTerminal
448+ }
449+ dio , err := f (ctx , fifos )
368450 if err != nil {
369451 return nil , err
370452 }
@@ -426,7 +508,7 @@ func TestContainerUsername(t *testing.T) {
426508 if err != nil {
427509 t .Fatal (err )
428510 }
429- direct , err := newDirectIO (ctx )
511+ direct , err := newDirectIOStandard (ctx )
430512 if err != nil {
431513 t .Fatal (err )
432514 }
@@ -501,7 +583,7 @@ func testContainerUser(t *testing.T, userstr, expectedOutput string) {
501583 if err != nil {
502584 t .Fatal (err )
503585 }
504- direct , err := newDirectIO (ctx )
586+ direct , err := newDirectIOStandard (ctx )
505587 if err != nil {
506588 t .Fatal (err )
507589 }
@@ -586,7 +668,7 @@ func TestContainerAttachProcess(t *testing.T) {
586668 expected := "hello" + newLine
587669
588670 // creating IO early for easy resource cleanup
589- direct , err := newDirectIO (ctx )
671+ direct , err := newDirectIOStandard (ctx )
590672 if err != nil {
591673 t .Fatal (err )
592674 }
@@ -693,7 +775,7 @@ func TestContainerUserID(t *testing.T) {
693775 if err != nil {
694776 t .Fatal (err )
695777 }
696- direct , err := newDirectIO (ctx )
778+ direct , err := newDirectIOStandard (ctx )
697779 if err != nil {
698780 t .Fatal (err )
699781 }
0 commit comments