/* NAME psi_test.go DESCRIPTION See Readme.md AUTHOR Saxon Milton LICENSE psi_test.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package psi import ( "bytes" "testing" ) // Some common manifestations of PSI var ( // PSI struct to represent basic pat StandardPat = PSI{ Pf: 0x00, Tid: 0x00, Ssi: true, Pb: false, Sl: 0x0d, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PAT{ Pn: 0x01, Pmpid: 0x1000, }, }, } // PSI struct to represent basic pmt without descriptors for time and location StandardPmt = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, Sl: 0x12, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PMT{ Pcrpid: 0x0100, // wrong Pil: 0, Essd: &ESSD{ St: 0x1b, Epid: 0x0100, Esil: 0x00, }, }, }, } // Std pmt with time and location descriptors, time and location fields are zeroed out StandardPmtTimeLocation = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, Sl: 0x3e, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PMT{ Pcrpid: 0x0100, Pil: PmtTimeLocationPil, Pd: []Desc{ { Dt: TimeDescTag, Dl: TimeDataSize, Dd: make([]byte, TimeDataSize), }, { Dt: LocationDescTag, Dl: LocationDataSize, Dd: make([]byte, LocationDataSize), }, }, Essd: &ESSD{ St: 0x1b, Epid: 0x0100, Esil: 0x00, }, }, }, } ) // Times as ints for testing const ( tstTime1 = 1235367435 // 0x49a2360b tstTime2 = 1735357535 // 0x676f745f ) // GPS string for testing // TODO: make these realistic const ( locationTstStr1 = "$GPGGA,123519,4807.038,N,01131.0" locationTstStr2 = "$GPGGA,183710,4902.048,N,02171.0" ) // err message const ( errCmp = "Incorrect output, for: %v wanted: %v, got: %v" ) // Test time to bytes test Data var ( timeSlice = []byte{ 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, } ) // Parts to construct bytes of pmt with time and bytes var ( pmtTimeLocationBytesPart1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag for timestamp TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data LocationDescTag, // Descriptor tag for location LocationDataSize, // Length of bytes to follow } pmtTimeLocationBytesPart2 = []byte{ 0x1b, 0xe1, 0x00, 0xf0, 0x00, } ) var ( // Bytes representing pmt with tstTime1 pmtTimeBytes1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } // Bytes representing pmt with tstTime 2 pmtTimeBytes2 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } // Bytes representing pmt with time1 and location1 pmtTimeLocationBytes1 = buildPmtTimeLocationBytes(locationTstStr1) // bytes representing pmt with with time1 and location 2 pmtTimeLocationBytes2 = buildPmtTimeLocationBytes(locationTstStr2) ) // bytesTests contains data for testing the Bytes() funcs for the PSI data struct var bytesTests = []struct { name string input PSI want []byte }{ // Pat test { name: "pat Bytes()", input: StandardPat, want: StandardPatBytes, }, // Pmt test data no descriptor { name: "pmt to Bytes() without descriptors", input: StandardPmt, want: StandardPmtBytes, }, // Pmt with time descriptor { name: "pmt to Bytes() with time descriptor", input: PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, Sl: 0x12, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PMT{ Pcrpid: 0x0100, // wrong Pil: 10, Pd: []Desc{ { Dt: TimeDescTag, Dl: TimeDataSize, Dd: TimeBytes(tstTime1), }, }, Essd: &ESSD{ St: 0x1b, Epid: 0x0100, Esil: 0x00, }, }, }, }, want: pmtTimeBytes1, }, // Pmt with time and location { name: "pmt Bytes() with time and location", input: PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, Sl: 0x12, Tss: &TSS{ Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PMT{ Pcrpid: 0x0100, // wrong Pil: 10, Pd: []Desc{ { Dt: TimeDescTag, Dl: TimeDataSize, Dd: TimeBytes(tstTime2), }, { Dt: LocationDescTag, Dl: LocationDataSize, Dd: LocationStrBytes(locationTstStr1), }, }, Essd: &ESSD{ St: 0x1b, Epid: 0x0100, Esil: 0x00, }, }, }, }, want: buildPmtTimeLocationBytes(locationTstStr1), }, } // TestBytes ensures that the Bytes() funcs are working correctly to take PSI // structs and convert them to byte slices func TestBytes(t *testing.T) { for _, test := range bytesTests { got := test.input.Bytes() // Remove crc32s got = got[:len(got)-4] if !bytes.Equal(got, test.want) { t.Errorf("unexpected error for test %v: got:%v want:%v", test.name, got, test.want) } } } // TestTimestampToBytes is a quick sanity check of the int64 time to []byte func func TestTimestampToBytes(t *testing.T) { tb := TimeBytes(tstTime1) if !bytes.Equal(timeSlice, tb) { t.Errorf(errCmp, "testTimeStampToBytes", timeSlice, tb) } } // TestTimeUpdate checks to see if we can correctly update the timstamp in pmt func TestTimeUpdate(t *testing.T) { cpy := make([]byte, len(pmtTimeBytes1)) copy(cpy, pmtTimeBytes1) cpy = addCrc(cpy) err := UpdateTime(cpy, tstTime2) cpy = cpy[:len(cpy)-4] if err != nil { t.Errorf("Update time returned err: %v", err) } if !bytes.Equal(pmtTimeBytes2, cpy) { t.Errorf(errCmp, "TestTimeUpdate", pmtTimeBytes2, cpy) } } // TestTimeGet tsts to see if we can correctly get the timestamp from a pmt func TestTimeGet(t *testing.T) { s, err := TimeFrom(pmtTimeBytes1) if err != nil { t.Errorf("Getting timestamp failed with err: %v", err) } if s != tstTime1 { t.Errorf(errCmp, "TestTimeGet", tstTime1, s) } } // TestLocationGet checks that we can correctly get location data from a pmt table func TestLocationGet(t *testing.T) { pb := StandardPmtTimeLocation.Bytes() err := UpdateLocation(pb, locationTstStr1) if err != nil { t.Errorf("Error for TestLocationGet UpdateLocation(pb, locationTstStr1): %v", err) } g, err := LocationFrom(pb) if err != nil { t.Errorf("Error for TestLocationGet LocationOf(pb): %v", err) } if g != locationTstStr1 { t.Errorf(errCmp, "TestLocationGet", locationTstStr1, g) } } // TestLocationUpdate checks to see if we can update the location string in a pmt correctly func TestLocationUpdate(t *testing.T) { cpy := make([]byte, len(pmtTimeLocationBytes1)) copy(cpy, pmtTimeLocationBytes1) cpy = addCrc(cpy) err := UpdateLocation(cpy, locationTstStr2) cpy = cpy[:len(cpy)-4] if err != nil { t.Errorf("Update time returned err: %v", err) } if !bytes.Equal(pmtTimeLocationBytes2, cpy) { t.Errorf(errCmp, "TestLocationUpdate", pmtTimeLocationBytes2, cpy) } } func TestTrim(t *testing.T) { test := []byte{0xa3, 0x01, 0x03, 0x00, 0xde} want := []byte{0xa3, 0x01, 0x03} got := trimTo(test, 0x00) if !bytes.Equal(got, want) { t.Errorf(errCmp, "TestTrim", want, got) } } // buildPmtTimeLocationBytes is a helper function to help construct the byte slices // for pmts with time and location, as the location data field is 32 bytes, i.e. quite large // to type out func buildPmtTimeLocationBytes(tstStr string) []byte { return append(append(append(make([]byte, 0), pmtTimeLocationBytesPart1...), LocationStrBytes(tstStr)...), pmtTimeLocationBytesPart2...) }