package main import ( "strings" "testing" ) // A number is an effect ID, not a name. That is the whole rule: a user typing // digits means the number, and a name is never written as digits, so the two // cannot be confused. func TestEffectTakesAnIDAsWellAsAName(t *testing.T) { tests := []struct { name string argument string wantValue uint8 wantOutput string }{ {"an ID the definition names", "13", 13, "Effect set to index 13"}, {"the ID past the end of the definition", "46", 46, "Effect set to index 46"}, {"ID 0, which two channels refuse", "0", 0, "Effect set to index 0"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { proto := &fakeZoneProtocol{failAt: -1} t.Cleanup(vendoredDefinitions(t)) stdout, _, err := executeEffectCommand(t, proto, "Backlight", tt.argument) if err != nil { t.Fatalf("effect %s: %v", tt.argument, err) } if !strings.Contains(stdout, tt.wantOutput) { t.Errorf("stdout = %q, want it to contain %q", stdout, tt.wantOutput) } if len(proto.reports) != 1 { t.Fatalf("reports = %v, want one write", proto.reports) } if got := proto.reports[0]; got.value != tt.wantValue || got.param != 0x02 { t.Errorf("wrote value %d param 0x%02x, want %d param 0x02", got.value, got.param, tt.wantValue) } }) } } // The point of taking IDs on the same command: a board with no names is still // fully drivable, because an ID is the only thing it ever needed. func TestEffectTakesAnIDOnAKeyboardWithNoNames(t *testing.T) { proto := &fakeZoneProtocol{failAt: -1} t.Cleanup(definitionFlagRestore(t)) t.Cleanup(forceDefinitionsDir(t, t.TempDir())) stdout, _, err := executeEffectCommand(t, proto, "Backlight", "46") if err != nil { t.Fatalf("effect 46 without a definition: %v", err) } if len(proto.reports) != 1 || proto.reports[0].value != 46 { t.Errorf("reports = %v, want one write of 46", proto.reports) } if !strings.Contains(stdout, "Effect set to index 46") { t.Errorf("stdout = %q, want the ID form reported", stdout) } } // A number that cannot be an effect ID is a number that cannot be used, and the // message says so rather than looking for a name nobody wrote. func TestEffectSaysWhenAnIDIsOutOfRange(t *testing.T) { proto := &fakeZoneProtocol{failAt: -1} t.Cleanup(vendoredDefinitions(t)) _, _, err := executeEffectCommand(t, proto, "Backlight", "300") if err == nil { t.Fatal("effect 300 = nil error, want a range error") } if !strings.Contains(err.Error(), "0-255") { t.Errorf("error = %q, want it to name the range", err) } if len(proto.reports) != 0 { t.Errorf("reports = %v, want nothing written for a value that cannot be one", proto.reports) } } // A name still resolves by name, and still goes through the alias table. func TestEffectStillTakesAName(t *testing.T) { proto := &fakeZoneProtocol{failAt: -1} t.Cleanup(vendoredDefinitions(t)) stdout, _, err := executeEffectCommand(t, proto, "Backlight", "rainbow_moving_chevron") if err != nil { t.Fatalf("effect by name: %v", err) } if len(proto.reports) != 1 || proto.reports[0].value != 17 { t.Errorf("reports = %v, want one write of 17", proto.reports) } if stdout != "Effect set to \"rainbow_moving_chevron\"\n" { t.Errorf("stdout = %q, want the plain success line", stdout) } } // An unknown name is still an unknown name, and the message points at the ID form // rather than at a command that no longer exists. func TestUnknownNameErrorPointsAtTheIDForm(t *testing.T) { proto := &fakeZoneProtocol{failAt: -1} t.Cleanup(vendoredDefinitions(t)) _, _, err := executeEffectCommand(t, proto, "Backlight", "nonsense") if err == nil { t.Fatal("effect nonsense = nil error, want one") } if strings.Contains(err.Error(), "`mode") { t.Errorf("error = %q, want no reference to a removed command", err) } if !strings.Contains(err.Error(), "effect ") { t.Errorf("error = %q, want it to point at the ID form", err) } } // The mode command is gone: an ID goes through the command that also takes names. func TestModeCommandIsGone(t *testing.T) { root := newRootCommand() registerCommands(root) registerFlagCompletions(root) for _, c := range root.Commands() { if c.Name() == "mode" { t.Fatal("the mode command is still registered; effect takes an ID now") } } }