mirror of https://github.com/vishvananda/netlink
680 lines
15 KiB
Go
680 lines
15 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package netlink
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func SafeQdiscList(link Link) ([]Qdisc, error) {
|
|
qdiscs, err := QdiscList(link)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := []Qdisc{}
|
|
for _, qdisc := range qdiscs {
|
|
// fmt.Printf("%+v\n", qdisc)
|
|
// filter default qdisc created by kernel when custom one deleted
|
|
attrs := qdisc.Attrs()
|
|
if attrs.Handle == HANDLE_NONE && attrs.Parent == HANDLE_ROOT {
|
|
continue
|
|
}
|
|
result = append(result, qdisc)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func SafeClassList(link Link, handle uint32) ([]Class, error) {
|
|
classes, err := ClassList(link, handle)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := []Class{}
|
|
for ind := range classes {
|
|
double := false
|
|
for _, class2 := range classes[ind+1:] {
|
|
if classes[ind].Attrs().Handle == class2.Attrs().Handle {
|
|
double = true
|
|
}
|
|
}
|
|
if !double {
|
|
result = append(result, classes[ind])
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func testClassStats(this, that *ClassStatistics, t *testing.T) {
|
|
ok := reflect.DeepEqual(this, that)
|
|
if !ok {
|
|
t.Fatalf("%#v is expected but it actually was %#v", that, this)
|
|
}
|
|
}
|
|
|
|
func TestClassAddDel(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Handle: MakeHandle(0xffff, 0),
|
|
Parent: HANDLE_ROOT,
|
|
}
|
|
qdisc := NewHtb(attrs)
|
|
if err := QdiscAdd(qdisc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qdiscs, err := SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 1 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
_, ok := qdiscs[0].(*Htb)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
classattrs := ClassAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Parent: MakeHandle(0xffff, 0),
|
|
Handle: MakeHandle(0xffff, 2),
|
|
}
|
|
|
|
htbclassattrs := HtbClassAttrs{
|
|
Rate: 1234000,
|
|
Cbuffer: 1690,
|
|
Prio: 2,
|
|
Quantum: 1000,
|
|
}
|
|
class := NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassAdd(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
classes, err := SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 1 {
|
|
t.Fatal("Failed to add class")
|
|
}
|
|
|
|
htb, ok := classes[0].(*HtbClass)
|
|
if !ok {
|
|
t.Fatal("Class is the wrong type")
|
|
}
|
|
if htb.Rate != class.Rate {
|
|
t.Fatal("Rate doesn't match")
|
|
}
|
|
if htb.Ceil != class.Ceil {
|
|
t.Fatal("Ceil doesn't match")
|
|
}
|
|
if htb.Buffer != class.Buffer {
|
|
t.Fatal("Buffer doesn't match")
|
|
}
|
|
if htb.Cbuffer != class.Cbuffer {
|
|
t.Fatal("Cbuffer doesn't match")
|
|
}
|
|
if htb.Prio != class.Prio {
|
|
t.Fatal("Prio doesn't match")
|
|
}
|
|
if htb.Quantum != class.Quantum {
|
|
t.Fatal("Quantum doesn't match")
|
|
}
|
|
|
|
testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t)
|
|
|
|
qattrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Handle: MakeHandle(0x2, 0),
|
|
Parent: MakeHandle(0xffff, 2),
|
|
}
|
|
nattrs := NetemQdiscAttrs{
|
|
Latency: 20000,
|
|
Loss: 23.4,
|
|
Duplicate: 14.3,
|
|
LossCorr: 8.34,
|
|
Jitter: 1000,
|
|
DelayCorr: 12.3,
|
|
ReorderProb: 23.4,
|
|
CorruptProb: 10.0,
|
|
CorruptCorr: 10,
|
|
}
|
|
qdiscnetem := NewNetem(qattrs, nattrs)
|
|
if err := QdiscAdd(qdiscnetem); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
qdiscs, err = SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 2 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
_, ok = qdiscs[0].(*Htb)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
netem, ok := qdiscs[1].(*Netem)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
// Compare the record we got from the list with the one we created
|
|
if netem.Loss != qdiscnetem.Loss {
|
|
t.Fatal("Loss does not match")
|
|
}
|
|
if netem.Latency != qdiscnetem.Latency {
|
|
t.Fatal("Latency does not match")
|
|
}
|
|
if netem.CorruptProb != qdiscnetem.CorruptProb {
|
|
t.Fatal("CorruptProb does not match")
|
|
}
|
|
if netem.Jitter != qdiscnetem.Jitter {
|
|
t.Fatal("Jitter does not match")
|
|
}
|
|
if netem.LossCorr != qdiscnetem.LossCorr {
|
|
t.Fatal("Loss does not match")
|
|
}
|
|
if netem.DuplicateCorr != qdiscnetem.DuplicateCorr {
|
|
t.Fatal("DuplicateCorr does not match")
|
|
}
|
|
|
|
// Deletion
|
|
// automatically removes netem qdisc
|
|
if err := ClassDel(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
classes, err = SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 0 {
|
|
t.Fatal("Failed to remove class")
|
|
}
|
|
if err := QdiscDel(qdisc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qdiscs, err = SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 0 {
|
|
t.Fatal("Failed to remove qdisc")
|
|
}
|
|
}
|
|
|
|
func TestHtbClassAddHtbClassChangeDel(t *testing.T) {
|
|
/**
|
|
This test first set up a interface ans set up a Htb qdisc
|
|
A HTB class is attach to it and a Netem qdisc is attached to that class
|
|
Next, we test changing the HTB class in place and confirming the Netem is
|
|
still attached. We also check that invoting ClassChange with a non-existing
|
|
class will fail.
|
|
Finally, we test ClassReplace. We confirm it correctly behave like
|
|
ClassChange when the parent/handle pair exists and that it will create a
|
|
new class if the handle is modified.
|
|
*/
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Handle: MakeHandle(0xffff, 0),
|
|
Parent: HANDLE_ROOT,
|
|
}
|
|
qdisc := NewHtb(attrs)
|
|
if err := QdiscAdd(qdisc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qdiscs, err := SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 1 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
_, ok := qdiscs[0].(*Htb)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
classattrs := ClassAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Parent: MakeHandle(0xffff, 0),
|
|
Handle: MakeHandle(0xffff, 2),
|
|
}
|
|
|
|
htbclassattrs := HtbClassAttrs{
|
|
Rate: uint64(1<<32) + 10,
|
|
Ceil: uint64(1<<32) + 20,
|
|
Cbuffer: 1690,
|
|
}
|
|
class := NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassAdd(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
classes, err := SafeClassList(link, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 1 {
|
|
t.Fatal("Failed to add class")
|
|
}
|
|
|
|
htb, ok := classes[0].(*HtbClass)
|
|
if !ok {
|
|
t.Fatal("Class is the wrong type")
|
|
}
|
|
|
|
testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t)
|
|
|
|
qattrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Handle: MakeHandle(0x2, 0),
|
|
Parent: MakeHandle(0xffff, 2),
|
|
}
|
|
nattrs := NetemQdiscAttrs{
|
|
Latency: 20000,
|
|
Loss: 23.4,
|
|
Duplicate: 14.3,
|
|
LossCorr: 8.34,
|
|
Jitter: 1000,
|
|
DelayCorr: 12.3,
|
|
ReorderProb: 23.4,
|
|
CorruptProb: 10.0,
|
|
CorruptCorr: 10,
|
|
}
|
|
qdiscnetem := NewNetem(qattrs, nattrs)
|
|
if err := QdiscAdd(qdiscnetem); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
qdiscs, err = SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 2 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
|
|
_, ok = qdiscs[1].(*Netem)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
// Change
|
|
// For change to work, the handle and parent cannot be changed.
|
|
|
|
// First, test it fails if we change the Handle.
|
|
oldHandle := classattrs.Handle
|
|
classattrs.Handle = MakeHandle(0xffff, 3)
|
|
class = NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassChange(class); err == nil {
|
|
t.Fatal("ClassChange should not work when using a different handle.")
|
|
}
|
|
// It should work with the same handle
|
|
classattrs.Handle = oldHandle
|
|
htbclassattrs.Rate = 4321000
|
|
class = NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassChange(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
classes, err = SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 1 {
|
|
t.Fatalf(
|
|
"1 class expected, %d found",
|
|
len(classes),
|
|
)
|
|
}
|
|
|
|
htb, ok = classes[0].(*HtbClass)
|
|
if !ok {
|
|
t.Fatal("Class is the wrong type")
|
|
}
|
|
// Verify that the rate value has changed.
|
|
if htb.Rate != class.Rate {
|
|
t.Fatal("Rate did not get changed while changing the class.")
|
|
}
|
|
|
|
// Check that we still have the netem child qdisc
|
|
qdiscs, err = SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(qdiscs) != 2 {
|
|
t.Fatalf("2 qdisc expected, %d found", len(qdiscs))
|
|
}
|
|
_, ok = qdiscs[0].(*Htb)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
_, ok = qdiscs[1].(*Netem)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
// Replace
|
|
// First replace by keeping the same handle, class will be changed.
|
|
// Then, replace by providing a new handle, n new class will be created.
|
|
|
|
// Replace acting as Change
|
|
class = NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassReplace(class); err != nil {
|
|
t.Fatal("Failed to replace class that is existing.")
|
|
}
|
|
|
|
classes, err = SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 1 {
|
|
t.Fatalf(
|
|
"1 class expected, %d found",
|
|
len(classes),
|
|
)
|
|
}
|
|
|
|
htb, ok = classes[0].(*HtbClass)
|
|
if !ok {
|
|
t.Fatal("Class is the wrong type")
|
|
}
|
|
// Verify that the rate value has changed.
|
|
if htb.Rate != class.Rate {
|
|
t.Fatal("Rate did not get changed while changing the class.")
|
|
}
|
|
|
|
// It should work with the same handle
|
|
classattrs.Handle = MakeHandle(0xffff, 3)
|
|
class = NewHtbClass(classattrs, htbclassattrs)
|
|
if err := ClassReplace(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
classes, err = SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 2 {
|
|
t.Fatalf(
|
|
"2 classes expected, %d found",
|
|
len(classes),
|
|
)
|
|
}
|
|
|
|
htb, ok = classes[1].(*HtbClass)
|
|
if !ok {
|
|
t.Fatal("Class is the wrong type")
|
|
}
|
|
// Verify that the rate value has changed.
|
|
if htb.Rate != class.Rate {
|
|
t.Fatal("Rate did not get changed while changing the class.")
|
|
}
|
|
|
|
// Deletion
|
|
for _, class := range classes {
|
|
if err := ClassDel(class); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
classes, err = SafeClassList(link, MakeHandle(0xffff, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 0 {
|
|
t.Fatal("Failed to remove class")
|
|
}
|
|
if err := QdiscDel(qdisc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qdiscs, err = SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 0 {
|
|
t.Fatal("Failed to remove qdisc")
|
|
}
|
|
}
|
|
|
|
func TestClassHfsc(t *testing.T) {
|
|
// New network namespace for tests
|
|
tearDown := setUpNetlinkTestWithKModule(t, "sch_hfsc")
|
|
defer tearDown()
|
|
|
|
// Set up testing link and check if succeeded
|
|
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Adding HFSC qdisc
|
|
qdiscAttrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Handle: MakeHandle(1, 0),
|
|
Parent: HANDLE_ROOT,
|
|
}
|
|
hfscQdisc := NewHfsc(qdiscAttrs)
|
|
hfscQdisc.Defcls = 2
|
|
|
|
err = QdiscAdd(hfscQdisc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
qdiscs, err := SafeQdiscList(link)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(qdiscs) != 1 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
_, ok := qdiscs[0].(*Hfsc)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
|
|
// Adding some HFSC classes
|
|
classAttrs := ClassAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Parent: MakeHandle(1, 0),
|
|
Handle: MakeHandle(1, 1),
|
|
}
|
|
hfscClass := NewHfscClass(classAttrs)
|
|
hfscClass.SetLS(5e6, 10, 5e6)
|
|
|
|
err = ClassAdd(hfscClass)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
hfscClass2 := hfscClass
|
|
hfscClass2.SetLS(0, 0, 0)
|
|
hfscClass2.Attrs().Parent = MakeHandle(1, 1)
|
|
hfscClass2.Attrs().Handle = MakeHandle(1, 2)
|
|
hfscClass2.SetRsc(0, 0, 2e6)
|
|
|
|
err = ClassAdd(hfscClass2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
hfscClass3 := hfscClass
|
|
hfscClass3.SetLS(0, 0, 0)
|
|
hfscClass3.Attrs().Parent = MakeHandle(1, 1)
|
|
hfscClass3.Attrs().Handle = MakeHandle(1, 3)
|
|
|
|
err = ClassAdd(hfscClass3)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check the classes
|
|
classes, err := SafeClassList(link, MakeHandle(1, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 4 {
|
|
t.Fatal("Failed to add classes")
|
|
}
|
|
for _, c := range classes {
|
|
class, ok := c.(*HfscClass)
|
|
if !ok {
|
|
t.Fatal("Wrong type of class")
|
|
}
|
|
if class.ClassAttrs.Handle == hfscClass.ClassAttrs.Handle {
|
|
if class.Fsc != hfscClass.Fsc {
|
|
t.Fatal("HfscClass FSC don't match")
|
|
}
|
|
if class.Usc != hfscClass.Usc {
|
|
t.Fatal("HfscClass USC don't match")
|
|
}
|
|
if class.Rsc != hfscClass.Rsc {
|
|
t.Fatal("HfscClass RSC don't match")
|
|
}
|
|
}
|
|
if class.ClassAttrs.Handle == hfscClass2.ClassAttrs.Handle {
|
|
if class.Fsc != hfscClass2.Fsc {
|
|
t.Fatal("HfscClass2 FSC don't match")
|
|
}
|
|
if class.Usc != hfscClass2.Usc {
|
|
t.Fatal("HfscClass2 USC don't match")
|
|
}
|
|
if class.Rsc != hfscClass2.Rsc {
|
|
t.Fatal("HfscClass2 RSC don't match")
|
|
}
|
|
}
|
|
if class.ClassAttrs.Handle == hfscClass3.ClassAttrs.Handle {
|
|
if class.Fsc != hfscClass3.Fsc {
|
|
t.Fatal("HfscClass3 FSC don't match")
|
|
}
|
|
if class.Usc != hfscClass3.Usc {
|
|
t.Fatal("HfscClass3 USC don't match")
|
|
}
|
|
if class.Rsc != hfscClass3.Rsc {
|
|
t.Fatal("HfscClass3 RSC don't match")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Terminating the leafs with fq_codel qdiscs
|
|
fqcodelAttrs := QdiscAttrs{
|
|
LinkIndex: link.Attrs().Index,
|
|
Parent: MakeHandle(1, 2),
|
|
Handle: MakeHandle(2, 0),
|
|
}
|
|
fqcodel1 := NewFqCodel(fqcodelAttrs)
|
|
fqcodel1.ECN = 0
|
|
fqcodel1.Limit = 1200
|
|
fqcodel1.Flows = 65535
|
|
fqcodel1.Target = 5
|
|
|
|
err = QdiscAdd(fqcodel1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
fqcodel2 := fqcodel1
|
|
fqcodel2.Attrs().Handle = MakeHandle(3, 0)
|
|
fqcodel2.Attrs().Parent = MakeHandle(1, 3)
|
|
|
|
err = QdiscAdd(fqcodel2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check the amount of qdiscs
|
|
qdiscs, _ = SafeQdiscList(link)
|
|
if len(qdiscs) != 3 {
|
|
t.Fatal("Failed to add qdisc")
|
|
}
|
|
for _, q := range qdiscs[1:] {
|
|
_, ok = q.(*FqCodel)
|
|
if !ok {
|
|
t.Fatal("Qdisc is the wrong type")
|
|
}
|
|
}
|
|
|
|
// removing a class
|
|
if err := ClassDel(hfscClass3); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Check the classes
|
|
classes, err = SafeClassList(link, MakeHandle(1, 0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(classes) != 3 {
|
|
t.Fatal("Failed to delete classes")
|
|
}
|
|
// Check qdisc
|
|
qdiscs, _ = SafeQdiscList(link)
|
|
if len(qdiscs) != 2 {
|
|
t.Fatal("Failed to delete qdisc")
|
|
}
|
|
|
|
// Changing a class
|
|
hfscClass2.SetRsc(0, 0, 0)
|
|
hfscClass2.SetSC(5e6, 100, 1e6)
|
|
hfscClass2.SetUL(6e6, 50, 2e6)
|
|
hfscClass2.Attrs().Handle = MakeHandle(1, 8)
|
|
if err := ClassChange(hfscClass2); err == nil {
|
|
t.Fatal("Class change shouldn't work with a different handle")
|
|
}
|
|
hfscClass2.Attrs().Handle = MakeHandle(1, 2)
|
|
if err := ClassChange(hfscClass2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Replacing a class
|
|
// If the handle doesn't exist, create it
|
|
hfscClass2.SetSC(6e6, 100, 2e6)
|
|
hfscClass2.SetUL(8e6, 500, 4e6)
|
|
hfscClass2.Attrs().Handle = MakeHandle(1, 8)
|
|
if err := ClassReplace(hfscClass2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// If the handle exists, replace it
|
|
hfscClass.SetLS(5e6, 200, 1e6)
|
|
if err := ClassChange(hfscClass); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
}
|