Merge 7032197961 into d6d0e710bb
commit
f2bf72bfd9
@ -0,0 +1,215 @@ |
||||
// Copyright 2021 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !noethtool
|
||||
|
||||
// SFP/QSFP module EEPROM parsing for Digital Optical Monitoring (DOM) /
|
||||
// Digital Diagnostic Monitoring (DDM) data.
|
||||
//
|
||||
// Standards:
|
||||
// - SFF-8472: SFP/SFP+ DDM (A0 + A2 EEPROM pages, 512 bytes total)
|
||||
// - SFF-8636: QSFP/QSFP28 DOM (page 0, 256 bytes)
|
||||
|
||||
package collector |
||||
|
||||
import ( |
||||
"encoding/binary" |
||||
"fmt" |
||||
) |
||||
|
||||
// SFP/QSFP module identifier values (EEPROM byte 0).
|
||||
const ( |
||||
sfpIdentifierSFP = 0x03 // SFP/SFP+/SFP28 (SFF-8472)
|
||||
sfpIdentifierSFPAlt = 0x0B // SFP+ alternative identifier
|
||||
sfpIdentifierQSFP = 0x0C // QSFP (SFF-8436)
|
||||
sfpIdentifierQSFPP = 0x0D // QSFP+ (SFF-8436)
|
||||
sfpIdentifierQSFP28 = 0x11 // QSFP28 (SFF-8636)
|
||||
) |
||||
|
||||
// sfpLaneMetrics holds per-lane optical monitoring values.
|
||||
type sfpLaneMetrics struct { |
||||
txBias float64 // TX laser bias current in amperes
|
||||
txPower float64 // TX optical power in watts
|
||||
rxPower float64 // RX optical power in watts
|
||||
} |
||||
|
||||
// sfpMetrics holds parsed DOM/DDM values from a transceiver module.
|
||||
type sfpMetrics struct { |
||||
temperature float64 // Module temperature in degrees Celsius
|
||||
voltage float64 // Module supply voltage in volts
|
||||
lanes []sfpLaneMetrics // Per-lane metrics (1 lane for SFP, 4 for QSFP)
|
||||
} |
||||
|
||||
// parseModuleEeprom parses raw EEPROM bytes returned by ethtool GMODULEEEPROM
|
||||
// and extracts DOM/DDM values.
|
||||
//
|
||||
// Returns an error if the data is too short, the identifier is unrecognised, or DDM is not available.
|
||||
func parseModuleEeprom(data []byte) (sfpMetrics, error) { |
||||
if len(data) < 1 { |
||||
return sfpMetrics{}, fmt.Errorf("module EEPROM data too short (%d bytes)", len(data)) |
||||
} |
||||
|
||||
switch data[0] { |
||||
case sfpIdentifierSFP, sfpIdentifierSFPAlt: |
||||
return parseSFF8472(data) |
||||
case sfpIdentifierQSFP, sfpIdentifierQSFPP, sfpIdentifierQSFP28: |
||||
return parseSFF8636(data) |
||||
default: |
||||
return sfpMetrics{}, fmt.Errorf("unsupported module identifier 0x%02x", data[0]) |
||||
} |
||||
} |
||||
|
||||
// parseSFF8472 parses SFP/SFP+ DDM data per SFF-8472.
|
||||
func parseSFF8472(data []byte) (sfpMetrics, error) { |
||||
const ( |
||||
a0DiagnosticType = 92 // A0 page: diagnostic monitoring type byte
|
||||
ddmSupportBit = 0x40 // bit 6: DDM implemented
|
||||
|
||||
// Offsets within the full 512-byte dump (A2 page starts at 256).
|
||||
a2PageOffset = 256 |
||||
valuesOffset = a2PageOffset + 96 |
||||
|
||||
tempOffset = valuesOffset |
||||
voltageOffset = tempOffset + 2 |
||||
txBiasOffset = voltageOffset + 2 |
||||
txPowerOffset = txBiasOffset + 2 |
||||
rxPowerOffset = txPowerOffset + 2 |
||||
minLen = rxPowerOffset + 2 |
||||
) |
||||
|
||||
if len(data) < a0DiagnosticType+1 { |
||||
return sfpMetrics{}, fmt.Errorf("SFF-8472 EEPROM too short for diagnostic type byte (%d bytes)", len(data)) |
||||
} |
||||
if data[a0DiagnosticType]&ddmSupportBit == 0 { |
||||
return sfpMetrics{}, fmt.Errorf("SFP module does not support DDM (diagnostic type byte: 0x%02x)", data[a0DiagnosticType]) |
||||
} |
||||
if len(data) < minLen { |
||||
return sfpMetrics{}, fmt.Errorf("SFF-8472 EEPROM too short for DDM values (%d bytes, need %d)", len(data), minLen) |
||||
} |
||||
|
||||
temp := parseSFPTemperature(data[tempOffset:]) |
||||
voltage := parseSFPVoltage(data[voltageOffset:]) |
||||
|
||||
txBias := parseSFPBias(data[txBiasOffset:]) |
||||
txPower := parseSFPPower(data[txPowerOffset:]) |
||||
rxPower := parseSFPPower(data[rxPowerOffset:]) |
||||
|
||||
return sfpMetrics{ |
||||
temperature: temp, |
||||
voltage: voltage, |
||||
lanes: []sfpLaneMetrics{ |
||||
{txBias: txBias, txPower: txPower, rxPower: rxPower}, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
// parseSFF8636 parses QSFP/QSFP28 DOM data per SFF-8636.
|
||||
func parseSFF8636(data []byte) (sfpMetrics, error) { |
||||
// All real-time values are on Page 00h.
|
||||
const ( |
||||
// Table 6-8 Free Side Monitoring Values
|
||||
tempOffset = 22 // Temperature MSB
|
||||
voltageOffset = 26 // Supply voltage MSB
|
||||
|
||||
// Table 6-9 Channel Monitoring Values.
|
||||
numLanes = 4 |
||||
rxPowerOffset = 34 // RX power ch1 MSB
|
||||
txBiasOffset = rxPowerOffset + numLanes*2 // TX bias ch1 MSB
|
||||
txPowerOffset = txBiasOffset + numLanes*2 // TX power ch1 MSB
|
||||
|
||||
minLen = txPowerOffset + numLanes*2 |
||||
) |
||||
|
||||
if len(data) < minLen { |
||||
return sfpMetrics{}, fmt.Errorf("SFF-8636 EEPROM too short (%d bytes, need %d)", len(data), minLen) |
||||
} |
||||
|
||||
temp := parseSFPTemperature(data[tempOffset:]) |
||||
voltage := parseSFPVoltage(data[voltageOffset:]) |
||||
|
||||
lanes := make([]sfpLaneMetrics, numLanes) |
||||
for i := range numLanes { |
||||
lanes[i] = sfpLaneMetrics{ |
||||
rxPower: parseSFPPower(data[rxPowerOffset+i*2:]), |
||||
txBias: parseSFPBias(data[txBiasOffset+i*2:]), |
||||
txPower: parseSFPPower(data[txPowerOffset+i*2:]), |
||||
} |
||||
} |
||||
|
||||
return sfpMetrics{ |
||||
temperature: temp, |
||||
voltage: voltage, |
||||
lanes: lanes, |
||||
}, nil |
||||
} |
||||
|
||||
func parseSFPTemperature(b []byte) float64 { |
||||
// SFF-8472
|
||||
//
|
||||
// Table 9-1 Bit Weights (°C) for Temperature Reporting Registers
|
||||
//
|
||||
// +----------------------------------+----------------------------------+-------+-------+
|
||||
// | Most Significant Byte (byte 96) | Least Significant Byte (byte 97) | | |
|
||||
// +------+----+----+----+---+---+---+---+---+---+----+-----+-----+------+-------+-------+
|
||||
// | D7 | D6 | D5 | D4 | D3| D2| D1| D0| D7| D6| D5 | D4 | D3 | D2 | D1 | D0 |
|
||||
// +------+----+----+----+---+---+---+---+---+---+----+-----+-----+------+-------+-------+
|
||||
// | Sign | 64 | 32 | 16 | 8 | 4 | 2 | 1 |1/2|1/4|1/8 |1/16 |1/32 | 1/64 | 1/128 | 1/256 |
|
||||
// +------+----+----+----+---+---+---+---+---+---+----+-----+-----+------+-------+-------+
|
||||
//
|
||||
rawVal := int16(binary.BigEndian.Uint16(b)) |
||||
return float64(rawVal) / 256.0 |
||||
} |
||||
|
||||
func parseSFPVoltage(b []byte) float64 { |
||||
// SFF-8472
|
||||
//
|
||||
// 9.2 Internal Calibration
|
||||
//
|
||||
// ...
|
||||
// 2) Internally measured transceiver supply voltage. Represented as a 16-bit unsigned integer with the voltage
|
||||
// defined as the full 16-bit value (0-65535) with LSB equal to 100 microvolts, yielding a total range of 0 V to +6.55 V.
|
||||
rawVal := binary.BigEndian.Uint16(b) |
||||
mV := float64(rawVal) / 10 |
||||
V := mV / 1000 |
||||
return V |
||||
} |
||||
|
||||
func parseSFPBias(b []byte) float64 { |
||||
// SFF-8472
|
||||
//
|
||||
// 9.2 Internal Calibration
|
||||
//
|
||||
// ...
|
||||
// 3) Measured TX bias current in mA. Represented as a 16-bit unsigned integer with the current defined as the full
|
||||
// 16-bit value (0-65535) with LSB equal to 2 microamps, yielding a total range of 0 to 131 mA.
|
||||
rawVal := binary.BigEndian.Uint16(b) |
||||
mA := float64(rawVal) / 500 |
||||
return mA |
||||
} |
||||
|
||||
func parseSFPPower(b []byte) float64 { |
||||
// SFF-8472
|
||||
//
|
||||
// 9.2 Internal Calibration
|
||||
//
|
||||
// ...
|
||||
// 4) Measured TX output power in mW. Represented as a 16-bit unsigned integer with the power defined as the
|
||||
// full 16-bit value (0-65535) with LSB equal to 0.1 microwatts, yielding a total range of 0 to 6.5535 mW (-40 to +8.2 dBm).
|
||||
// ...
|
||||
// 5) Measured RX received optical power in mW. Value can represent either average received power or OMA
|
||||
// depending upon how bit 3 of byte 92 (A0h) is set. Represented as a 16-bit unsigned integer with the power
|
||||
// defined as the full 16-bit value (0-65535) with LSB equal to 0.1 microwatts, yielding a total range of 0 to 6.5535 mW (-40 to +8.2 dBm).
|
||||
rawVal := binary.BigEndian.Uint16(b) |
||||
mW := float64(rawVal) / 10000 |
||||
return mW |
||||
} |
||||
@ -0,0 +1,426 @@ |
||||
// Copyright 2021 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !noethtool
|
||||
|
||||
package collector |
||||
|
||||
import ( |
||||
"encoding/binary" |
||||
"fmt" |
||||
"math" |
||||
"testing" |
||||
) |
||||
|
||||
func almostEqual(a, b float64) bool { |
||||
if a == b { |
||||
return true |
||||
} |
||||
return math.Abs(a-b)/math.Max(math.Abs(a), math.Abs(b)) < 0.1 |
||||
} |
||||
|
||||
func makeSFF8472(temp int16, voltage, txBias, txPower, rxPower uint16, ddmByte byte) []byte { |
||||
data := make([]byte, 512) |
||||
data[0] = sfpIdentifierSFP |
||||
data[92] = ddmByte |
||||
binary.BigEndian.PutUint16(data[352:], uint16(temp)) |
||||
binary.BigEndian.PutUint16(data[354:], voltage) |
||||
binary.BigEndian.PutUint16(data[356:], txBias) |
||||
binary.BigEndian.PutUint16(data[358:], txPower) |
||||
binary.BigEndian.PutUint16(data[360:], rxPower) |
||||
return data |
||||
} |
||||
|
||||
func makeSFF8636(temp int16, voltage uint16, rxPower, txBias, txPower [4]uint16) []byte { |
||||
data := make([]byte, 58) |
||||
data[0] = sfpIdentifierQSFP28 |
||||
binary.BigEndian.PutUint16(data[22:], uint16(temp)) |
||||
binary.BigEndian.PutUint16(data[26:], voltage) |
||||
for i := range 4 { |
||||
binary.BigEndian.PutUint16(data[34+i*2:], rxPower[i]) |
||||
binary.BigEndian.PutUint16(data[42+i*2:], txBias[i]) |
||||
binary.BigEndian.PutUint16(data[50+i*2:], txPower[i]) |
||||
} |
||||
return data |
||||
} |
||||
|
||||
var sff8472Cases = []struct { |
||||
name string |
||||
data []byte |
||||
wantErr bool |
||||
wantTemp float64 |
||||
wantVoltage float64 |
||||
wantTxBias float64 |
||||
wantTxPower float64 |
||||
wantRxPower float64 |
||||
}{ |
||||
{ |
||||
name: "typical values", |
||||
data: makeSFF8472(25*256, 33000, 10000, 10000, 5000, 0x40), |
||||
wantTemp: 25.0, |
||||
wantVoltage: 33000 * 100e-6, |
||||
wantTxBias: 20.0, |
||||
wantTxPower: 1.0, |
||||
wantRxPower: 0.5, |
||||
}, |
||||
{ |
||||
name: "negative temperature", |
||||
data: makeSFF8472(-10*256, 33000, 5000, 8000, 4000, 0x40), |
||||
wantTemp: -10.0, |
||||
wantVoltage: 33000 * 100e-6, |
||||
wantTxBias: 10.0, |
||||
wantTxPower: 0.8, |
||||
wantRxPower: 0.4, |
||||
}, |
||||
{ |
||||
name: "fractional temperature", |
||||
data: makeSFF8472(int16(25*256+128), 33000, 0, 0, 0, 0x40), |
||||
wantTemp: 25.5, |
||||
wantVoltage: 33000 * 100e-6, |
||||
}, |
||||
{ |
||||
name: "DDM not supported", |
||||
data: makeSFF8472(0, 0, 0, 0, 0, 0x00), |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "too short for diagnostic type byte", |
||||
data: make([]byte, 50), |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "too short for DDM values", |
||||
data: func() []byte { |
||||
d := make([]byte, 200) |
||||
d[0] = sfpIdentifierSFP |
||||
d[92] = 0x40 |
||||
return d |
||||
}(), |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "empty", |
||||
data: []byte{}, |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "zero values", |
||||
data: makeSFF8472(0, 0, 0, 0, 0, 0x40), |
||||
}, |
||||
{ |
||||
name: "alt SFP identifier (0x0B)", |
||||
data: func() []byte { |
||||
d := makeSFF8472(25*256, 33000, 10000, 10000, 5000, 0x40) |
||||
d[0] = sfpIdentifierSFPAlt |
||||
return d |
||||
}(), |
||||
wantTemp: 25.0, |
||||
wantVoltage: 33000 * 100e-6, |
||||
wantTxBias: 20.0, |
||||
wantTxPower: 1.0, |
||||
wantRxPower: 0.5, |
||||
}, |
||||
} |
||||
|
||||
var sff8636Cases = []struct { |
||||
name string |
||||
data []byte |
||||
wantErr bool |
||||
wantTemp float64 |
||||
wantVoltage float64 |
||||
wantLanes [4]sfpLaneMetrics |
||||
}{ |
||||
{ |
||||
name: "typical values", |
||||
data: makeSFF8636( |
||||
25*256, 33000, |
||||
[4]uint16{5000, 4800, 4900, 5100}, |
||||
[4]uint16{10000, 9800, 10200, 10100}, |
||||
[4]uint16{9000, 8800, 9100, 9200}, |
||||
), |
||||
wantTemp: 25.0, |
||||
wantVoltage: 33000 * 100e-6, |
||||
wantLanes: [4]sfpLaneMetrics{ |
||||
{txBias: 20.0, txPower: 0.9, rxPower: 0.5}, |
||||
{txBias: 19.6, txPower: 0.88, rxPower: 0.48}, |
||||
{txBias: 20.4, txPower: 0.91, rxPower: 0.49}, |
||||
{txBias: 20.2, txPower: 0.92, rxPower: 0.51}, |
||||
}, |
||||
}, |
||||
{ |
||||
name: "negative temperature", |
||||
data: makeSFF8636( |
||||
-5*256, 33000, |
||||
[4]uint16{}, [4]uint16{}, [4]uint16{}, |
||||
), |
||||
wantTemp: -5.0, |
||||
wantVoltage: 33000 * 100e-6, |
||||
}, |
||||
{ |
||||
name: "too short", |
||||
data: make([]byte, 10), |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "empty", |
||||
data: []byte{}, |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "zero values", |
||||
data: makeSFF8636(0, 0, [4]uint16{}, [4]uint16{}, [4]uint16{}), |
||||
}, |
||||
} |
||||
|
||||
// moduleEepromCases are the table-driven test cases for parseModuleEeprom, also
|
||||
// used as fuzzing seeds.
|
||||
var moduleEepromCases = []struct { |
||||
name string |
||||
data []byte |
||||
wantErr bool |
||||
wantLanes int |
||||
}{ |
||||
{ |
||||
name: "SFP (0x03)", |
||||
data: makeSFF8472(25*256, 33000, 10000, 10000, 5000, 0x40), |
||||
wantLanes: 1, |
||||
}, |
||||
{ |
||||
name: "SFP alt (0x0B)", |
||||
data: func() []byte { |
||||
d := makeSFF8472(25*256, 33000, 10000, 10000, 5000, 0x40) |
||||
d[0] = sfpIdentifierSFPAlt |
||||
return d |
||||
}(), |
||||
wantLanes: 1, |
||||
}, |
||||
{ |
||||
name: "QSFP (0x0C)", |
||||
data: func() []byte { |
||||
d := makeSFF8636(25*256, 33000, [4]uint16{}, [4]uint16{}, [4]uint16{}) |
||||
d[0] = sfpIdentifierQSFP |
||||
return d |
||||
}(), |
||||
wantLanes: 4, |
||||
}, |
||||
{ |
||||
name: "QSFP+ (0x0D)", |
||||
data: func() []byte { |
||||
d := makeSFF8636(25*256, 33000, [4]uint16{}, [4]uint16{}, [4]uint16{}) |
||||
d[0] = sfpIdentifierQSFPP |
||||
return d |
||||
}(), |
||||
wantLanes: 4, |
||||
}, |
||||
{ |
||||
name: "QSFP28 (0x11)", |
||||
data: makeSFF8636(25*256, 33000, [4]uint16{}, [4]uint16{}, [4]uint16{}), |
||||
wantLanes: 4, |
||||
}, |
||||
{ |
||||
name: "unknown identifier", |
||||
data: []byte{0x01}, |
||||
wantErr: true, |
||||
}, |
||||
{ |
||||
name: "empty", |
||||
data: []byte{}, |
||||
wantErr: true, |
||||
}, |
||||
} |
||||
|
||||
func TestParseSFF8472(t *testing.T) { |
||||
for _, tc := range sff8472Cases { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
got, err := parseSFF8472(tc.data) |
||||
if tc.wantErr { |
||||
if err == nil { |
||||
t.Fatal("expected error, got nil") |
||||
} |
||||
return |
||||
} |
||||
if err != nil { |
||||
t.Fatalf("unexpected error: %v", err) |
||||
} |
||||
if !almostEqual(got.temperature, tc.wantTemp) { |
||||
t.Errorf("temperature: got %v, want %v", got.temperature, tc.wantTemp) |
||||
} |
||||
if !almostEqual(got.voltage, tc.wantVoltage) { |
||||
t.Errorf("voltage: got %v, want %v", got.voltage, tc.wantVoltage) |
||||
} |
||||
if len(got.lanes) != 1 { |
||||
t.Fatalf("expected 1 lane, got %d", len(got.lanes)) |
||||
} |
||||
if !almostEqual(got.lanes[0].txBias, tc.wantTxBias) { |
||||
t.Errorf("txBias: got %v, want %v", got.lanes[0].txBias, tc.wantTxBias) |
||||
} |
||||
if !almostEqual(got.lanes[0].txPower, tc.wantTxPower) { |
||||
t.Errorf("txPower: got %v, want %v", got.lanes[0].txPower, tc.wantTxPower) |
||||
} |
||||
if !almostEqual(got.lanes[0].rxPower, tc.wantRxPower) { |
||||
t.Errorf("rxPower: got %v, want %v", got.lanes[0].rxPower, tc.wantRxPower) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestParseSFF8636(t *testing.T) { |
||||
for _, tc := range sff8636Cases { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
got, err := parseSFF8636(tc.data) |
||||
if tc.wantErr { |
||||
if err == nil { |
||||
t.Fatal("expected error, got nil") |
||||
} |
||||
return |
||||
} |
||||
if err != nil { |
||||
t.Fatalf("unexpected error: %v", err) |
||||
} |
||||
if !almostEqual(got.temperature, tc.wantTemp) { |
||||
t.Errorf("temperature: got %v, want %v", got.temperature, tc.wantTemp) |
||||
} |
||||
if !almostEqual(got.voltage, tc.wantVoltage) { |
||||
t.Errorf("voltage: got %v, want %v", got.voltage, tc.wantVoltage) |
||||
} |
||||
if len(got.lanes) != 4 { |
||||
t.Fatalf("expected 4 lanes, got %d", len(got.lanes)) |
||||
} |
||||
for i, want := range tc.wantLanes { |
||||
l := got.lanes[i] |
||||
if !almostEqual(l.txBias, want.txBias) { |
||||
t.Errorf("lane %d txBias: got %v, want %v", i, l.txBias, want.txBias) |
||||
} |
||||
if !almostEqual(l.txPower, want.txPower) { |
||||
t.Errorf("lane %d txPower: got %v, want %v", i, l.txPower, want.txPower) |
||||
} |
||||
if !almostEqual(l.rxPower, want.rxPower) { |
||||
t.Errorf("lane %d rxPower: got %v, want %v", i, l.rxPower, want.rxPower) |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestParseModuleEeprom(t *testing.T) { |
||||
for _, tc := range moduleEepromCases { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
got, err := parseModuleEeprom(tc.data) |
||||
if tc.wantErr { |
||||
if err == nil { |
||||
t.Fatal("expected error, got nil") |
||||
} |
||||
return |
||||
} |
||||
if err != nil { |
||||
t.Fatalf("unexpected error: %v", err) |
||||
} |
||||
if len(got.lanes) != tc.wantLanes { |
||||
t.Errorf("lanes: got %d, want %d", len(got.lanes), tc.wantLanes) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func FuzzParseSFF8472(f *testing.F) { |
||||
for _, tc := range sff8472Cases { |
||||
f.Add(tc.data) |
||||
} |
||||
f.Fuzz(func(t *testing.T, data []byte) { |
||||
_, _ = parseSFF8472(data) //nolint:errcheck
|
||||
}) |
||||
} |
||||
|
||||
func FuzzParseSFF8636(f *testing.F) { |
||||
for _, tc := range sff8636Cases { |
||||
f.Add(tc.data) |
||||
} |
||||
f.Fuzz(func(t *testing.T, data []byte) { |
||||
_, _ = parseSFF8636(data) //nolint:errcheck
|
||||
}) |
||||
} |
||||
|
||||
func FuzzParseModuleEeprom(f *testing.F) { |
||||
for _, tc := range moduleEepromCases { |
||||
f.Add(tc.data) |
||||
} |
||||
f.Fuzz(func(t *testing.T, data []byte) { |
||||
_, _ = parseModuleEeprom(data) //nolint:errcheck
|
||||
}) |
||||
} |
||||
|
||||
func Test_parseSFPTemperature(t *testing.T) { |
||||
// Values from Table 9-4 TEC Current Format.
|
||||
tests := []struct { |
||||
b [2]byte |
||||
want float64 // celsius
|
||||
}{ |
||||
{[2]byte{0x7D, 0}, 125.0}, |
||||
{[2]byte{0x19, 0}, 25.0}, |
||||
{[2]byte{0x00, 0xFF}, 1}, |
||||
{[2]byte{0x00, 0x01}, 0.004}, |
||||
{[2]byte{}, 0}, |
||||
{[2]byte{0xFF, 0xFF}, -0.004}, |
||||
{[2]byte{0xE7, 0x00}, -25.0}, |
||||
{[2]byte{0xD8, 0x00}, -40.0}, |
||||
} |
||||
for i, tt := range tests { |
||||
tt := tt |
||||
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { |
||||
got := parseSFPTemperature(tt.b[:]) |
||||
if !almostEqual(tt.want, got) { |
||||
t.Fatalf("Expected ~ %v, got %v", tt.want, got) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func Test_parseSFPVoltage(t *testing.T) { |
||||
tests := []struct { |
||||
b [2]byte |
||||
want float64 // volts
|
||||
}{ |
||||
{[2]byte{0xFF, 0xFF}, 6.55}, |
||||
{[2]byte{0x00, 0x01}, 0.0001}, |
||||
{[2]byte{}, 0}, |
||||
} |
||||
for i, tt := range tests { |
||||
tt := tt |
||||
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { |
||||
got := parseSFPVoltage(tt.b[:]) |
||||
if !almostEqual(tt.want, got) { |
||||
t.Fatalf("Expected ~ %v, got %v", tt.want, got) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func Test_parseSFPPower(t *testing.T) { |
||||
tests := []struct { |
||||
b [2]byte |
||||
want float64 // milliwatts
|
||||
}{ |
||||
{[2]byte{0xFF, 0xFF}, 6.55}, |
||||
{[2]byte{0x00, 0x01}, 0.0001}, |
||||
{[2]byte{}, 0}, |
||||
} |
||||
for i, tt := range tests { |
||||
tt := tt |
||||
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { |
||||
got := parseSFPPower(tt.b[:]) |
||||
if !almostEqual(tt.want, got) { |
||||
t.Fatalf("Expected ~ %v, got %v", tt.want, got) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
Binary file not shown.
Loading…
Reference in new issue