@@ -35,9 +35,11 @@ import (
3535 "github.com/snapcore/snapd/overlord/hookstate/hooktest"
3636 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
3737 "github.com/snapcore/snapd/overlord/snapstate"
38+ "github.com/snapcore/snapd/overlord/snapstate/sequence"
3839 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
3940 "github.com/snapcore/snapd/overlord/state"
4041 "github.com/snapcore/snapd/snap"
42+ "github.com/snapcore/snapd/snap/naming"
4143 "github.com/snapcore/snapd/snap/snaptest"
4244 "github.com/snapcore/snapd/testutil"
4345)
@@ -362,3 +364,141 @@ func (s *installSuite) TestNoWaitInstallAndRemoveCommands(c *C) {
362364 s .st .Unlock ()
363365 }
364366}
367+
368+ func (s * installSuite ) TestInstallWithParallelInstalledSnap (c * C ) {
369+ s .st .Lock ()
370+ s .chg = s .st .NewChange ("install change" , "install change" )
371+ task := s .st .NewTask ("test-task" , "my test task" )
372+ s .chg .AddTask (task )
373+ setup := & hookstate.HookSetup {Snap : "test-snap_foo" , Revision : snap .R (1 ), Hook : "test-hook" }
374+
375+ // create a context for the parallel installed snap
376+ var err error
377+ s .mockContext , err = hookstate .NewContext (task , task .State (), setup , s .mockHandler , "" )
378+ c .Assert (err , IsNil )
379+
380+ installTask := s .st .NewTask ("queued" , "queued task" )
381+ s .st .Unlock ()
382+
383+ restore := ctlcmd .MockSnapstateInstallComponentsFunc (func (ctx context.Context , st * state.State , names []string , info * snap.Info , vsets * snapasserts.ValidationSets , opts snapstate.Options ) ([]* state.TaskSet , error ) {
384+ c .Check (names , DeepEquals , []string {"two" })
385+ c .Check (opts , DeepEquals , snapstate.Options {ExpectOneSnap : true ,
386+ ConflictOptions : snapstate.ConflictOptions {FromChange : s .mockContext .ChangeID ()}})
387+ var ts state.TaskSet
388+ ts .AddTask (installTask )
389+ return []* state.TaskSet {& ts }, nil
390+ })
391+ defer restore ()
392+
393+ rev := snap .R (1 )
394+ si := & snap.SideInfo {
395+ RealName : "test-snap" ,
396+ Revision : rev ,
397+ SnapID : "test-snap-id" ,
398+ }
399+
400+ seq := snapstatetest .NewSequenceFromRevisionSideInfos ([]* sequence.RevisionSideState {
401+ sequence .NewRevisionSideState (si , nil ),
402+ })
403+
404+ // component +one is already installed
405+ seq .AddComponentForRevision (snap .R (1 ), sequence .NewComponentState (& snap.ComponentSideInfo {
406+ Component : naming .NewComponentRef ("test-snap" , "one" ),
407+ Revision : snap .R (1 ),
408+ }, snap .StandardComponent ))
409+
410+ s .st .Lock ()
411+ snapstate .Set (s .st , "test-snap_foo" , & snapstate.SnapState {
412+ Active : true ,
413+ Sequence : seq ,
414+ Current : rev ,
415+ })
416+ s .st .Unlock ()
417+
418+ stdout , stderr , err := ctlcmd .Run (s .mockContext , []string {"install" , "+one" , "+two" }, 0 , nil )
419+ c .Check (err , IsNil )
420+ c .Check (stdout , HasLen , 0 )
421+ c .Check (string (stderr ), Matches , `(?sm).*snapctl: component "one" is already installed` )
422+ }
423+
424+ func (s * installSuite ) TestInstallAllAlreadyInstalled (c * C ) {
425+ rev := snap .R (1 )
426+ si := & snap.SideInfo {
427+ RealName : "test-snap" ,
428+ Revision : rev ,
429+ SnapID : "test-snap-id" ,
430+ }
431+
432+ seq := snapstatetest .NewSequenceFromRevisionSideInfos ([]* sequence.RevisionSideState {
433+ sequence .NewRevisionSideState (si , nil ),
434+ })
435+
436+ seq .AddComponentForRevision (snap .R (1 ), sequence .NewComponentState (& snap.ComponentSideInfo {
437+ Component : naming .NewComponentRef ("test-snap" , "one" ),
438+ Revision : snap .R (1 ),
439+ }, snap .StandardComponent ))
440+
441+ seq .AddComponentForRevision (snap .R (1 ), sequence .NewComponentState (& snap.ComponentSideInfo {
442+ Component : naming .NewComponentRef ("test-snap" , "two" ),
443+ Revision : snap .R (1 ),
444+ }, snap .StandardComponent ))
445+
446+ s .st .Lock ()
447+ snapstate .Set (s .st , "test-snap" , & snapstate.SnapState {
448+ Active : true ,
449+ Sequence : seq ,
450+ Current : rev ,
451+ })
452+ s .st .Unlock ()
453+
454+ stdout , stderr , err := ctlcmd .Run (s .mockContext , []string {"install" , "+one" , "+two" }, 0 , nil )
455+ c .Check (err , IsNil )
456+ c .Check (stdout , HasLen , 0 )
457+ c .Check (string (stderr ), Matches , `(?sm).*snapctl: component "one" is already installed` )
458+ c .Check (string (stderr ), Matches , `(?sm).*snapctl: component "two" is already installed` )
459+ }
460+
461+ func (s * installSuite ) TestInstallSomeAlreadyInstalled (c * C ) {
462+ s .st .Lock ()
463+ task := s .st .NewTask ("queued" , "queued task" )
464+ s .st .Unlock ()
465+
466+ restore := ctlcmd .MockSnapstateInstallComponentsFunc (func (ctx context.Context , st * state.State , names []string , info * snap.Info , vsets * snapasserts.ValidationSets , opts snapstate.Options ) ([]* state.TaskSet , error ) {
467+ c .Check (names , DeepEquals , []string {"two" })
468+ c .Check (opts , DeepEquals , snapstate.Options {ExpectOneSnap : true ,
469+ ConflictOptions : snapstate.ConflictOptions {FromChange : s .mockContext .ChangeID ()}})
470+ var ts state.TaskSet
471+ ts .AddTask (task )
472+ return []* state.TaskSet {& ts }, nil
473+ })
474+ defer restore ()
475+
476+ rev := snap .R (1 )
477+ si := & snap.SideInfo {
478+ RealName : "test-snap" ,
479+ Revision : rev ,
480+ SnapID : "test-snap-id" ,
481+ }
482+
483+ seq := snapstatetest .NewSequenceFromRevisionSideInfos ([]* sequence.RevisionSideState {
484+ sequence .NewRevisionSideState (si , nil ),
485+ })
486+
487+ seq .AddComponentForRevision (snap .R (1 ), sequence .NewComponentState (& snap.ComponentSideInfo {
488+ Component : naming .NewComponentRef ("test-snap" , "one" ),
489+ Revision : snap .R (1 ),
490+ }, snap .StandardComponent ))
491+
492+ s .st .Lock ()
493+ snapstate .Set (s .st , "test-snap" , & snapstate.SnapState {
494+ Active : true ,
495+ Sequence : seq ,
496+ Current : rev ,
497+ })
498+
499+ s .st .Unlock ()
500+ stdout , stderr , err := ctlcmd .Run (s .mockContext , []string {"install" , "+one" , "+two" }, 0 , nil )
501+ c .Check (err , IsNil )
502+ c .Check (stdout , HasLen , 0 )
503+ c .Check (string (stderr ), Matches , `snapctl: component "one" is already installed\n` )
504+ }
0 commit comments