aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/org
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
committerAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
commit59416923dcb3a756eaf532cc4259f2f6625c0762 (patch)
tree9082dae6616deac8fda432b7bfd80e4a52b6d9dc /src/test/java/org
parent79a129dd6327d932133d6b9a50190d3f4927bff9 (diff)
downloadtraccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.gz
traccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.bz2
traccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.zip
Convert project to gradle
Diffstat (limited to 'src/test/java/org')
-rw-r--r--src/test/java/org/traccar/BaseTest.java34
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java324
-rw-r--r--src/test/java/org/traccar/TestIdentityManager.java72
-rw-r--r--src/test/java/org/traccar/WebDataHandlerTest.java28
-rw-r--r--src/test/java/org/traccar/calendar/CalendarTest.java59
-rw-r--r--src/test/java/org/traccar/config/ConfigTest.java19
-rw-r--r--src/test/java/org/traccar/database/DataManagerTest.java81
-rw-r--r--src/test/java/org/traccar/database/GroupTreeTest.java56
-rw-r--r--src/test/java/org/traccar/geocoder/AddressFormatTest.java33
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java88
-rw-r--r--src/test/java/org/traccar/geofence/GeofenceCircleTest.java28
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolygonTest.java52
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolylineTest.java47
-rw-r--r--src/test/java/org/traccar/geolocation/GeolocationProviderTest.java41
-rw-r--r--src/test/java/org/traccar/handler/ComputedAttributesTest.java71
-rw-r--r--src/test/java/org/traccar/handler/DistanceHandlerTest.java30
-rw-r--r--src/test/java/org/traccar/handler/FilterHandlerTest.java88
-rw-r--r--src/test/java/org/traccar/handler/MotionHandlerTest.java21
-rw-r--r--src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java30
-rw-r--r--src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java28
-rw-r--r--src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java27
-rw-r--r--src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java119
-rw-r--r--src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java128
-rw-r--r--src/test/java/org/traccar/helper/BcdUtilTest.java24
-rw-r--r--src/test/java/org/traccar/helper/BitBufferTest.java23
-rw-r--r--src/test/java/org/traccar/helper/BitUtilTest.java38
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java39
-rw-r--r--src/test/java/org/traccar/helper/DateBuilderTest.java27
-rw-r--r--src/test/java/org/traccar/helper/DateUtilTest.java30
-rw-r--r--src/test/java/org/traccar/helper/DistanceCalculatorTest.java24
-rw-r--r--src/test/java/org/traccar/helper/LocationTreeTest.java30
-rw-r--r--src/test/java/org/traccar/helper/LogTest.java14
-rw-r--r--src/test/java/org/traccar/helper/ObdDecoderTest.java26
-rw-r--r--src/test/java/org/traccar/helper/PatternBuilderTest.java20
-rw-r--r--src/test/java/org/traccar/helper/PatternUtilTest.java18
-rw-r--r--src/test/java/org/traccar/model/MiscFormatterTest.java20
-rw-r--r--src/test/java/org/traccar/notification/NotificiationMailTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java38
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java44
-rw-r--r--src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java93
-rw-r--r--src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java79
-rw-r--r--src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java99
-rw-r--r--src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java67
-rw-r--r--src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java46
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java144
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java78
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java55
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java114
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java50
-rw-r--r--src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java56
-rw-r--r--src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java390
-rw-r--r--src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java56
-rw-r--r--src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java87
-rw-r--r--src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java261
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java52
-rw-r--r--src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java49
-rw-r--r--src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java43
-rw-r--r--src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java294
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/H02FrameDecoderTest.java71
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java261
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java72
-rw-r--r--src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java69
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java64
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java60
-rw-r--r--src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java108
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java37
-rwxr-xr-xsrc/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/L100FrameDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java125
-rw-r--r--src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java100
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java137
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java110
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java73
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java79
-rw-r--r--src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java86
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java69
-rw-r--r--src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java34
-rw-r--r--src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java167
-rwxr-xr-xsrc/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java126
-rw-r--r--src/test/java/org/traccar/protocol/T57FrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java86
-rw-r--r--src/test/java/org/traccar/protocol/TekFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java94
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java134
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java44
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java202
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java282
-rw-r--r--src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java64
-rw-r--r--src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java116
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java63
-rw-r--r--src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java67
-rw-r--r--src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java53
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java90
-rw-r--r--src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java65
-rw-r--r--src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java77
-rw-r--r--src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java40
-rw-r--r--src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java60
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java128
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java83
-rw-r--r--src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java67
-rw-r--r--src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java62
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java118
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java76
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/reports/ReportUtilsTest.java390
-rw-r--r--src/test/java/org/traccar/web/WebServerTest.java29
287 files changed, 13899 insertions, 0 deletions
diff --git a/src/test/java/org/traccar/BaseTest.java b/src/test/java/org/traccar/BaseTest.java
new file mode 100644
index 000000000..0b2c616ce
--- /dev/null
+++ b/src/test/java/org/traccar/BaseTest.java
@@ -0,0 +1,34 @@
+package org.traccar;
+
+import io.netty.buffer.ByteBuf;
+import org.traccar.database.MediaManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BaseTest {
+
+ public static class MockMediaManager extends MediaManager {
+ Map<String, ByteBuf> files = new HashMap<>();
+
+ MockMediaManager() {
+ super("");
+ }
+
+ @Override
+ public String writeFile(String uniqueId, ByteBuf buf, String extension) {
+ String fileName = uniqueId + "/mock." + extension;
+ files.put(fileName, buf);
+ return fileName;
+ }
+
+ public ByteBuf readFile(String fileName) {
+ return files.get(fileName);
+ }
+ }
+
+ static {
+ Context.init(new TestIdentityManager(), new MockMediaManager());
+ }
+
+}
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
new file mode 100644
index 000000000..4d48bb763
--- /dev/null
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -0,0 +1,324 @@
+package org.traccar;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Command;
+import org.traccar.model.Position;
+
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class ProtocolTest extends BaseTest {
+
+ protected Position position(String time, boolean valid, double lat, double lon) throws ParseException {
+
+ Position position = new Position();
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(time));
+ position.setValid(valid);
+ position.setLatitude(lat);
+ position.setLongitude(lon);
+
+ return position;
+ }
+
+ private String concatenateStrings(String... strings) {
+ StringBuilder builder = new StringBuilder();
+ for (String s : strings) {
+ builder.append(s);
+ }
+ return builder.toString();
+ }
+
+ protected ByteBuf concatenateBuffers(ByteBuf... buffers) {
+ ByteBuf result = Unpooled.buffer();
+ for (ByteBuf buf : buffers) {
+ result.writeBytes(buf);
+ }
+ return result;
+ }
+
+ protected ByteBuf binary(String... data) {
+ return Unpooled.wrappedBuffer(DataConverter.parseHex(concatenateStrings(data)));
+ }
+
+ protected String text(String... data) {
+ return concatenateStrings(data);
+ }
+
+ protected ByteBuf buffer(String... data) {
+ return Unpooled.copiedBuffer(concatenateStrings(data), StandardCharsets.ISO_8859_1);
+ }
+
+ protected DefaultFullHttpRequest request(String url) {
+ return request(HttpMethod.GET, url);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url, ByteBuf data) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url, data);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url, HttpHeaders headers) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url, Unpooled.buffer(), headers, new DefaultHttpHeaders());
+ }
+
+ protected void verifyNotNull(BaseProtocolDecoder decoder, Object object) throws Exception {
+ assertNotNull(decoder.decode(null, null, object));
+ }
+
+ protected void verifyNull(Object object) {
+ assertNull(object);
+ }
+
+ protected void verifyNull(BaseProtocolDecoder decoder, Object object) throws Exception {
+ assertNull(decoder.decode(null, null, object));
+ }
+
+ protected void verifyAttribute(BaseProtocolDecoder decoder, Object object, String key, Object expected) throws Exception {
+ Position position = (Position) decoder.decode(null, null, object);
+ switch (key) {
+ case "speed":
+ assertEquals(expected, position.getSpeed());
+ break;
+ case "course":
+ assertEquals(expected, position.getCourse());
+ break;
+ default:
+ assertEquals(expected, position.getAttributes().get(key));
+ break;
+ }
+ }
+
+ protected void verifyAttributes(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), false, true, null);
+ }
+
+ protected void verifyPosition(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), true, false, null);
+ }
+
+ protected void verifyPosition(BaseProtocolDecoder decoder, Object object, Position position) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), true, false, position);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), true, null);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, boolean checkLocation, Object object) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), checkLocation, null);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, Object object, Position position) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), true, position);
+ }
+
+ private void verifyDecodedList(Object decodedObject, boolean checkLocation, Position expected) {
+
+ assertNotNull("list is null", decodedObject);
+ assertTrue("not a list", decodedObject instanceof List);
+ assertFalse("list is empty", ((List) decodedObject).isEmpty());
+
+ for (Object item : (List) decodedObject) {
+ verifyDecodedPosition(item, checkLocation, false, expected);
+ }
+
+ }
+
+ private void verifyDecodedPosition(Object decodedObject, boolean checkLocation, boolean checkAttributes, Position expected) {
+
+ assertNotNull("position is null", decodedObject);
+ assertTrue("not a position", decodedObject instanceof Position);
+
+ Position position = (Position) decodedObject;
+
+ if (checkLocation) {
+
+ if (expected != null) {
+
+ if (expected.getFixTime() != null) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ assertEquals("time", dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime()));
+ }
+ assertEquals("valid", expected.getValid(), position.getValid());
+ assertEquals("latitude", expected.getLatitude(), position.getLatitude(), 0.00001);
+ assertEquals("longitude", expected.getLongitude(), position.getLongitude(), 0.00001);
+
+ } else {
+
+ assertNotNull(position.getServerTime());
+ assertNotNull(position.getFixTime());
+ assertTrue("year > 1999", position.getFixTime().after(new Date(915148800000L)));
+ assertTrue("time < +25 hours",
+ position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000);
+
+ assertTrue("latitude >= -90", position.getLatitude() >= -90);
+ assertTrue("latitude <= 90", position.getLatitude() <= 90);
+
+ assertTrue("longitude >= -180", position.getLongitude() >= -180);
+ assertTrue("longitude <= 180", position.getLongitude() <= 180);
+
+ }
+
+ assertTrue("altitude >= -12262", position.getAltitude() >= -12262);
+ assertTrue("altitude <= 18000", position.getAltitude() <= 18000);
+
+ assertTrue("speed >= 0", position.getSpeed() >= 0);
+ assertTrue("speed <= 869", position.getSpeed() <= 869);
+
+ assertTrue("course >= 0", position.getCourse() >= 0);
+ assertTrue("course <= 360", position.getCourse() <= 360);
+
+ assertNotNull("protocol is null", position.getProtocol());
+
+ }
+
+ Map<String, Object> attributes = position.getAttributes();
+
+ if (checkAttributes) {
+ assertFalse("no attributes", attributes.isEmpty());
+ }
+
+ if (attributes.containsKey(Position.KEY_INDEX)) {
+ assertTrue(attributes.get(Position.KEY_INDEX) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_HDOP)) {
+ assertTrue(attributes.get(Position.KEY_HDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_VDOP)) {
+ assertTrue(attributes.get(Position.KEY_VDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_PDOP)) {
+ assertTrue(attributes.get(Position.KEY_PDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_SATELLITES)) {
+ assertTrue(attributes.get(Position.KEY_SATELLITES) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_SATELLITES_VISIBLE)) {
+ assertTrue(attributes.get(Position.KEY_SATELLITES_VISIBLE) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_RSSI)) {
+ assertTrue(attributes.get(Position.KEY_RSSI) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_ODOMETER)) {
+ assertTrue(attributes.get(Position.KEY_ODOMETER) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_RPM)) {
+ assertTrue(attributes.get(Position.KEY_RPM) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_FUEL_LEVEL)) {
+ assertTrue(attributes.get(Position.KEY_FUEL_LEVEL) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_POWER)) {
+ assertTrue(attributes.get(Position.KEY_POWER) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_BATTERY)) {
+ assertTrue(attributes.get(Position.KEY_BATTERY) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_BATTERY_LEVEL)) {
+ int batteryLevel = ((Number) attributes.get(Position.KEY_BATTERY_LEVEL)).intValue();
+ assertTrue(batteryLevel <= 100 && batteryLevel >= 0);
+ }
+
+ if (attributes.containsKey(Position.KEY_CHARGE)) {
+ assertTrue(attributes.get(Position.KEY_CHARGE) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_IGNITION)) {
+ assertTrue(attributes.get(Position.KEY_IGNITION) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_MOTION)) {
+ assertTrue(attributes.get(Position.KEY_MOTION) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_ARCHIVE)) {
+ assertTrue(attributes.get(Position.KEY_ARCHIVE) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ assertTrue(attributes.get(Position.KEY_DRIVER_UNIQUE_ID) instanceof String);
+ }
+
+ if (attributes.containsKey(Position.KEY_STEPS)) {
+ assertTrue(attributes.get(Position.KEY_STEPS) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_ROAMING)) {
+ assertTrue(attributes.get(Position.KEY_ROAMING) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_HOURS)) {
+ assertTrue(attributes.get(Position.KEY_HOURS) instanceof Number);
+ }
+
+ if (position.getNetwork() != null && position.getNetwork().getCellTowers() != null) {
+ for (CellTower cellTower : position.getNetwork().getCellTowers()) {
+ checkInteger(cellTower.getMobileCountryCode(), 0, 999);
+ checkInteger(cellTower.getMobileNetworkCode(), 0, 999);
+ checkInteger(cellTower.getLocationAreaCode(), 1, 65535);
+ checkInteger(cellTower.getCellId(), 0, 268435455);
+ }
+ }
+
+ }
+
+ private void checkInteger(Object value, int min, int max) {
+ assertNotNull("value is null", value);
+ assertTrue("not int or long", value instanceof Integer || value instanceof Long);
+ long number = ((Number) value).longValue();
+ assertTrue("value too low", number >= min);
+ assertTrue("value too high", number <= max);
+ }
+
+ protected void verifyCommand(
+ BaseProtocolEncoder encoder, Command command, ByteBuf expected) {
+ verifyFrame(expected, encoder.encodeCommand(command));
+ }
+
+ protected void verifyFrame(ByteBuf expected, Object object) {
+ assertNotNull("buffer is null", object);
+ assertTrue("not a buffer", object instanceof ByteBuf);
+ assertEquals(ByteBufUtil.hexDump(expected), ByteBufUtil.hexDump((ByteBuf) object));
+ }
+
+}
diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java
new file mode 100644
index 000000000..0f7405dbd
--- /dev/null
+++ b/src/test/java/org/traccar/TestIdentityManager.java
@@ -0,0 +1,72 @@
+package org.traccar;
+
+import org.traccar.database.IdentityManager;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+public final class TestIdentityManager implements IdentityManager {
+
+ private static Device createDevice() {
+ Device device = new Device();
+ device.setId(1);
+ device.setName("test");
+ device.setUniqueId("123456789012345");
+ return device;
+ }
+
+ @Override
+ public long addUnknownDevice(String uniqueId) {
+ return 1;
+ }
+
+ @Override
+ public Device getById(long id) {
+ return createDevice();
+ }
+
+ @Override
+ public Device getByUniqueId(String uniqueId) {
+ return createDevice();
+ }
+
+ @Override
+ public Position getLastPosition(long deviceId) {
+ return null;
+ }
+
+ @Override
+ public boolean isLatestPosition(Position position) {
+ return true;
+ }
+
+ @Override
+ public boolean lookupAttributeBoolean(
+ long deviceId, String attributeName, boolean defaultValue, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public String lookupAttributeString(
+ long deviceId, String attributeName, String defaultValue, boolean lookupConfig) {
+ return "alarm,result";
+ }
+
+ @Override
+ public int lookupAttributeInteger(
+ long deviceId, String attributeName, int defaultValue, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public long lookupAttributeLong(
+ long deviceId, String attributeName, long defaultValue, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public double lookupAttributeDouble(
+ long deviceId, String attributeName, double defaultValue, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+}
diff --git a/src/test/java/org/traccar/WebDataHandlerTest.java b/src/test/java/org/traccar/WebDataHandlerTest.java
new file mode 100644
index 000000000..cfbd71f23
--- /dev/null
+++ b/src/test/java/org/traccar/WebDataHandlerTest.java
@@ -0,0 +1,28 @@
+package org.traccar;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class WebDataHandlerTest extends ProtocolTest {
+
+ @Test
+ public void testFormatRequest() throws Exception {
+
+ Config config = new Config();
+ config.setString(Keys.FORWARD_URL, "http://localhost/?fixTime={fixTime}&gprmc={gprmc}&name={name}");
+
+ Position position = position("2016-01-01 01:02:03.000", true, 20, 30);
+
+ WebDataHandler handler = new WebDataHandler(config, Context.getIdentityManager(), null, null);
+
+ assertEquals(
+ "http://localhost/?fixTime=1451610123000&gprmc=$GPRMC,010203.000,A,2000.0000,N,03000.0000,E,0.00,0.00,010116,,*05&name=test",
+ handler.formatRequest(position));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java
new file mode 100644
index 000000000..56406d4b8
--- /dev/null
+++ b/src/test/java/org/traccar/calendar/CalendarTest.java
@@ -0,0 +1,59 @@
+package org.traccar.calendar;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Test;
+import org.traccar.model.Calendar;
+
+import net.fortuna.ical4j.data.ParserException;
+
+import static org.junit.Assert.assertTrue;
+
+public class CalendarTest {
+
+ @Test
+ public void testCalendar() throws IOException, ParserException, ParseException, SQLException {
+ String calendarString = "BEGIN:VCALENDAR\n" +
+ "PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\n" +
+ "VERSION:2.0\n" +
+ "BEGIN:VTIMEZONE\n" +
+ "TZID:Asia/Yekaterinburg\n" +
+ "BEGIN:STANDARD\n" +
+ "TZOFFSETFROM:+0500\n" +
+ "TZOFFSETTO:+0500\n" +
+ "TZNAME:YEKT\n" +
+ "DTSTART:19700101T000000\n" +
+ "END:STANDARD\n" +
+ "END:VTIMEZONE\n" +
+ "BEGIN:VEVENT\n" +
+ "CREATED:20161213T045151Z\n" +
+ "LAST-MODIFIED:20161213T045242Z\n" +
+ "DTSTAMP:20161213T045242Z\n" +
+ "UID:9d000df0-6354-479d-a407-218dac62c7c9\n" +
+ "SUMMARY:Every night\n" +
+ "RRULE:FREQ=DAILY\n" +
+ "DTSTART;TZID=Asia/Yekaterinburg:20161130T230000\n" +
+ "DTEND;TZID=Asia/Yekaterinburg:20161201T070000\n" +
+ "TRANSP:OPAQUE\n" +
+ "END:VEVENT\n" +
+ "END:VCALENDAR";
+ Calendar calendar = new Calendar();
+ calendar.setData(calendarString.getBytes());
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX");
+
+ Date date = format.parse("2016-12-13 22:59:59+05");
+ assertTrue(!calendar.checkMoment(date));
+ date = format.parse("2016-12-13 23:00:01+05");
+ assertTrue(calendar.checkMoment(date));
+
+ date = format.parse("2016-12-13 06:59:59+05");
+ assertTrue(calendar.checkMoment(date));
+ date = format.parse("2016-12-13 07:00:01+05");
+ assertTrue(!calendar.checkMoment(date));
+ }
+}
diff --git a/src/test/java/org/traccar/config/ConfigTest.java b/src/test/java/org/traccar/config/ConfigTest.java
new file mode 100644
index 000000000..13d0ffb04
--- /dev/null
+++ b/src/test/java/org/traccar/config/ConfigTest.java
@@ -0,0 +1,19 @@
+package org.traccar.config;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import org.traccar.config.Config;
+
+public class ConfigTest {
+
+ @Test
+ public void testFormat() {
+ assertEquals("DATABASE_URL", Config.getEnvironmentVariableName("database.url"));
+ assertEquals("DATABASE_CHECK_CONNECTION", Config.getEnvironmentVariableName("database.checkConnection"));
+ assertEquals("DATABASE_MAX_POOL_SIZE", Config.getEnvironmentVariableName("database.maxPoolSize"));
+ assertEquals("DEVICE_MANAGER_LOOKUP_GROUPS_ATTRIBUTE", Config.getEnvironmentVariableName("deviceManager.lookupGroupsAttribute"));
+ assertEquals("COMMAND_FALLBACK_TO_SMS", Config.getEnvironmentVariableName("command.fallbackToSms"));
+ assertEquals("STATUS_TIMEOUT", Config.getEnvironmentVariableName("status.timeout"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/database/DataManagerTest.java b/src/test/java/org/traccar/database/DataManagerTest.java
new file mode 100644
index 000000000..23043e96e
--- /dev/null
+++ b/src/test/java/org/traccar/database/DataManagerTest.java
@@ -0,0 +1,81 @@
+package org.traccar.database;
+
+import org.junit.Test;
+import org.traccar.model.Attribute;
+import org.traccar.model.Device;
+import org.traccar.model.Driver;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.ManagedUser;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class DataManagerTest {
+
+ @Test
+ public void constructObjectQuery() {
+ assertEquals("SELECT * FROM tc_users",
+ DataManager.constructObjectQuery(DataManager.ACTION_SELECT_ALL, User.class, false));
+ assertEquals("DELETE FROM tc_groups WHERE id = :id",
+ DataManager.constructObjectQuery(DataManager.ACTION_DELETE, Group.class, false));
+ assertEquals("SELECT * FROM tc_positions WHERE id = :id",
+ DataManager.constructObjectQuery(DataManager.ACTION_SELECT, Position.class, false));
+
+ String insertDevice = DataManager.constructObjectQuery(DataManager.ACTION_INSERT, Device.class, false);
+ assertFalse(insertDevice.contains("class"));
+ assertFalse(insertDevice.contains("id"));
+ assertFalse(insertDevice.contains("status"));
+ assertFalse(insertDevice.contains("geofenceIds"));
+
+ String updateDeviceStatus = DataManager.constructObjectQuery("update", Device.class, true);
+ assertTrue(updateDeviceStatus.contains("lastUpdate"));
+
+ String updateUser = DataManager.constructObjectQuery(DataManager.ACTION_UPDATE, User.class, false);
+ assertFalse(updateUser.contains("class"));
+ assertFalse(updateUser.contains("password"));
+ assertFalse(updateUser.contains("salt"));
+
+ String updateUserPassword = DataManager.constructObjectQuery(DataManager.ACTION_UPDATE, User.class, true);
+ assertFalse(updateUserPassword.contains("name"));
+ assertTrue(updateUserPassword.contains("hashedPassword"));
+ assertTrue(updateUserPassword.contains("salt"));
+
+ String insertPosition = DataManager.constructObjectQuery(DataManager.ACTION_INSERT, Position.class, false);
+ assertFalse(insertPosition.contains("type"));
+ assertFalse(insertPosition.contains("outdated"));
+
+ }
+
+ @Test
+ public void constructPermissionsQuery() {
+ assertEquals("SELECT userId, deviceId FROM tc_user_device",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, User.class, Device.class));
+
+ assertEquals("SELECT userId, managedUserId FROM tc_user_user",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, User.class, ManagedUser.class));
+
+ assertEquals("SELECT deviceId, driverId FROM tc_device_driver",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, Device.class, Driver.class));
+
+ assertEquals("SELECT groupId, geofenceId FROM tc_group_geofence",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, Group.class, Geofence.class));
+
+ assertEquals("INSERT INTO tc_user_device (userId, deviceId) VALUES (:userId, :deviceId)",
+ DataManager.constructPermissionQuery(DataManager.ACTION_INSERT, User.class, Device.class));
+
+ assertEquals("DELETE FROM tc_user_user WHERE userId = :userId AND managedUserId = :managedUserId",
+ DataManager.constructPermissionQuery(DataManager.ACTION_DELETE, User.class, ManagedUser.class));
+
+ assertEquals("INSERT INTO tc_device_geofence (deviceId, geofenceId) VALUES (:deviceId, :geofenceId)",
+ DataManager.constructPermissionQuery(DataManager.ACTION_INSERT, Device.class, Geofence.class));
+
+ assertEquals("DELETE FROM tc_group_attribute WHERE groupId = :groupId AND attributeId = :attributeId",
+ DataManager.constructPermissionQuery(DataManager.ACTION_DELETE, Group.class, Attribute.class));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/database/GroupTreeTest.java b/src/test/java/org/traccar/database/GroupTreeTest.java
new file mode 100644
index 000000000..b547aab60
--- /dev/null
+++ b/src/test/java/org/traccar/database/GroupTreeTest.java
@@ -0,0 +1,56 @@
+package org.traccar.database;
+
+import org.junit.Test;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+
+public class GroupTreeTest {
+
+ private static Group createGroup(long id, String name, long parent) {
+ Group group = new Group();
+ group.setId(id);
+ group.setName(name);
+ group.setGroupId(parent);
+ return group;
+ }
+
+ private static Device createDevice(long id, String name, long parent) {
+ Device device = new Device();
+ device.setId(id);
+ device.setName(name);
+ device.setGroupId(parent);
+ return device;
+ }
+
+ @Test
+ public void testGetDescendants() {
+ Collection<Group> groups = new ArrayList<>();
+ groups.add(createGroup(1, "First", 0));
+ groups.add(createGroup(2, "Second", 1));
+ groups.add(createGroup(3, "Third", 2));
+ groups.add(createGroup(4, "Fourth", 2));
+ groups.add(createGroup(5, "Fifth", 4));
+
+ Collection<Device> devices = new ArrayList<>();
+ devices.add(createDevice(1, "One", 3));
+ devices.add(createDevice(2, "Two", 5));
+ devices.add(createDevice(3, "One", 5));
+
+ GroupTree groupTree = new GroupTree(groups, devices);
+
+ assertEquals(4, groupTree.getGroups(1).size());
+ assertEquals(3, groupTree.getGroups(2).size());
+ assertEquals(0, groupTree.getGroups(3).size());
+ assertEquals(1, groupTree.getGroups(4).size());
+
+ assertEquals(3, groupTree.getDevices(1).size());
+ assertEquals(1, groupTree.getDevices(3).size());
+ assertEquals(2, groupTree.getDevices(4).size());
+ }
+
+}
diff --git a/src/test/java/org/traccar/geocoder/AddressFormatTest.java b/src/test/java/org/traccar/geocoder/AddressFormatTest.java
new file mode 100644
index 000000000..0cc5168ef
--- /dev/null
+++ b/src/test/java/org/traccar/geocoder/AddressFormatTest.java
@@ -0,0 +1,33 @@
+package org.traccar.geocoder;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class AddressFormatTest {
+
+ private void test(Address address, String format, String expected) {
+ assertEquals(expected, new AddressFormat(format).format(address));
+ }
+
+ @Test
+ public void testFormat() {
+
+ Address address = new Address();
+ address.setCountry("NZ");
+ address.setSettlement("Auckland");
+ address.setStreet("Queen St");
+ address.setHouse("1A");
+
+ test(address, "%h %r %t %d %s %c %p", "1A Queen St Auckland NZ");
+ test(address, "%h %r %t", "1A Queen St Auckland");
+ test(address, "%h %r, %t", "1A Queen St, Auckland");
+ test(address, "%h %r, %t %p", "1A Queen St, Auckland");
+ test(address, "%t %d %c", "Auckland NZ");
+ test(address, "%t, %d, %c", "Auckland, NZ");
+ test(address, "%d %c", "NZ");
+ test(address, "%d, %c", "NZ");
+ test(address, "%p", "");
+ }
+
+}
diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
new file mode 100644
index 000000000..85d9bf62f
--- /dev/null
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -0,0 +1,88 @@
+package org.traccar.geocoder;
+
+import java.util.Locale;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class GeocoderTest {
+
+ static {
+ Locale.setDefault(Locale.US);
+ }
+
+ @Ignore
+ @Test
+ public void testGoogle() {
+ Geocoder geocoder = new GoogleGeocoder(null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(31.776797, 35.211489, null);
+ assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", address);
+ }
+
+ @Ignore
+ @Test
+ public void testNominatim() {
+ Geocoder geocoder = new NominatimGeocoder(null, null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(40.7337807, -73.9974401, null);
+ assertEquals("35 West 9th Street, NYC, New York, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGisgraphy() {
+ Geocoder geocoder = new GisgraphyGeocoder(new AddressFormat());
+ String address = geocoder.getAddress(48.8530000, 2.3400000, null);
+ assertEquals("Rue du Jardinet, Paris, Île-de-France, FR", address);
+ }
+
+ @Ignore
+ @Test
+ public void testOpenCage() {
+ Geocoder geocoder = new OpenCageGeocoder(
+ "http://api.opencagedata.com/geocode/v1", "SECRET", 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("Charleston Road, California, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGeocodeFarm() {
+ Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("Estrella Avenue, Arcadia, California, United States", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGeocodeXyz() {
+ Geocoder geocoder = new GeocodeXyzGeocoder(null, 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("605 ESTRELLA AVE, ARCADIA, California United States of America, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testBan() {
+ Geocoder geocoder = new BanGeocoder(0, new AddressFormat("%f [%d], %c"));
+ String address = geocoder.getAddress(48.8575, 2.2944, null);
+ assertEquals("8 Avenue Gustave Eiffel 75007 Paris [75, Paris, Île-de-France], FR", address);
+ }
+
+ @Ignore
+ @Test
+ public void testHere() {
+ Geocoder geocoder = new HereGeocoder("", "", null, 0, new AddressFormat());
+ String address = geocoder.getAddress(48.8575, 2.2944, null);
+ assertEquals("6 Avenue Gustave Eiffel, Paris, Île-de-France, FRA", address);
+ }
+
+ @Ignore
+ @Test
+ public void testMapmyIndia() {
+ Geocoder geocoder = new MapmyIndiaGeocoder("", "", 0, new AddressFormat("%f"));
+ String address = geocoder.getAddress(28.6129602407977, 77.2294557094574, null);
+ assertEquals("New Delhi, Delhi. 1 m from India Gate pin-110001 (India)", address);
+ }
+}
diff --git a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
new file mode 100644
index 000000000..259a8fb77
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
@@ -0,0 +1,28 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofenceCircleTest {
+
+ @Test
+ public void testCircleWkt() throws ParseException {
+ String test = "CIRCLE (55.75414 37.6204, 100)";
+ GeofenceGeometry geofenceGeometry = new GeofenceCircle();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsCircle() throws ParseException {
+ String test = "CIRCLE (55.75414 37.6204, 100)";
+ GeofenceGeometry geofenceGeometry = new GeofenceCircle();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(55.75477, 37.62025));
+ assertTrue(!geofenceGeometry.containsPoint(55.75545, 37.61921));
+ }
+}
diff --git a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java
new file mode 100644
index 000000000..94b73af3a
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java
@@ -0,0 +1,52 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofencePolygonTest {
+
+ @Test
+ public void testPolygonWkt() throws ParseException {
+ String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsPolygon() throws ParseException {
+ String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(55.75476, 37.61915));
+ assertTrue(!geofenceGeometry.containsPoint(55.75545, 37.61921));
+
+ }
+
+ @Test
+ public void testContainsPolygon180() throws ParseException {
+ String test = "POLYGON ((66.9494 179.838, 66.9508 -179.8496, 66.8406 -180.0014))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(66.9015, -180.0096));
+ assertTrue(geofenceGeometry.containsPoint(66.9015, 179.991));
+ assertTrue(!geofenceGeometry.containsPoint(66.8368, -179.8792));
+
+ }
+
+ @Test
+ public void testContainsPolygon0() throws ParseException {
+ String test = "POLYGON ((51.1966 -0.6207, 51.1897 0.4147, 50.9377 0.5136, 50.8675 -0.6082))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(51.0466, -0.0165));
+ assertTrue(geofenceGeometry.containsPoint(51.0466, 0.018));
+ assertTrue(!geofenceGeometry.containsPoint(50.9477, 0.5836));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
new file mode 100644
index 000000000..1e9dcb7c3
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
@@ -0,0 +1,47 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofencePolylineTest {
+
+ @Test
+ public void testPolylineWkt() throws ParseException {
+ String test = "LINESTRING (55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsPolyline1Interval() throws ParseException {
+ String test = "LINESTRING (56.83777 60.59833, 56.83766 60.5968)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 35);
+ assertTrue(geofenceGeometry.containsPoint(56.83801, 60.59748));
+ ((GeofencePolyline) geofenceGeometry).setDistance(15);
+ assertTrue(!geofenceGeometry.containsPoint(56.83801, 60.59748));
+ }
+
+ @Test
+ public void testContainsPolyline3Intervals() throws ParseException {
+ String test = "LINESTRING (56.836 60.6126, 56.8393 60.6114, 56.83887 60.60811, 56.83782 60.5988)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 15);
+ assertTrue(geofenceGeometry.containsPoint(56.83847, 60.60458));
+ assertTrue(!geofenceGeometry.containsPoint(56.83764, 60.59725));
+ assertTrue(!geofenceGeometry.containsPoint(56.83861, 60.60822));
+
+ }
+
+ @Test
+ public void testContainsPolylineNear180() throws ParseException {
+ String test = "LINESTRING (66.9494 179.838, 66.9508 -179.8496)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 25);
+ assertTrue(geofenceGeometry.containsPoint(66.95, 180.0));
+ assertTrue(!geofenceGeometry.containsPoint(66.96, 180.0));
+ assertTrue(!geofenceGeometry.containsPoint(66.9509, -179.83));
+ }
+}
diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
new file mode 100644
index 000000000..2729052d6
--- /dev/null
+++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.geolocation;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class GeolocationProviderTest extends BaseTest {
+
+ @Ignore
+ @Test
+ public void test() throws Exception {
+ testLocationProvider();
+ }
+
+ public void testLocationProvider() throws Exception {
+ MozillaGeolocationProvider provider = new MozillaGeolocationProvider(null);
+
+ Network network = new Network(CellTower.from(208, 1, 2, 1234567));
+
+ provider.getLocation(network, new GeolocationProvider.LocationProviderCallback() {
+ @Override
+ public void onSuccess(double latitude, double longitude, double accuracy) {
+ assertEquals(60.07254, latitude, 0.00001);
+ assertEquals(30.30996, longitude, 0.00001);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ fail();
+ }
+ });
+
+ Thread.sleep(Long.MAX_VALUE);
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
new file mode 100644
index 000000000..a76d8169b
--- /dev/null
+++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
@@ -0,0 +1,71 @@
+package org.traccar.handler;
+
+import java.util.Date;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.model.Attribute;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class ComputedAttributesTest {
+
+ @Test
+ public void testComputedAttributes() {
+
+ ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null, null);
+
+ Date date = new Date();
+ Position position = new Position();
+ position.setTime(date);
+ position.setSpeed(42);
+ position.setValid(false);
+ position.set("adc1", 128);
+ position.set("booleanFlag", true);
+ position.set("adc2", 100);
+ position.set("bitFlag", 7);
+ position.set("event", 42);
+ position.set("result", "success");
+ Attribute attribute = new Attribute();
+
+ attribute.setExpression("adc1");
+ assertEquals(128, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("!booleanFlag");
+ assertEquals(false, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("adc2 * 2 + 50");
+ assertEquals(250, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("(bitFlag & 4) != 0");
+ assertEquals(true, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("if (event == 42) \"lowBattery\"");
+ assertEquals("lowBattery", handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("speed > 5 && valid");
+ assertEquals(false, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("fixTime");
+ assertEquals(date, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("math:pow(adc1, 2)");
+ assertEquals(16384.0, handler.computeAttribute(attribute, position));
+
+ // modification tests
+ attribute.setExpression("adc1 = 256");
+ handler.computeAttribute(attribute, position);
+ assertEquals(128, position.getInteger("adc1"));
+
+ attribute.setExpression("result = \"fail\"");
+ handler.computeAttribute(attribute, position);
+ assertEquals("success", position.getString("result"));
+
+ attribute.setExpression("fixTime = \"2017-10-18 10:00:01\"");
+ handler.computeAttribute(attribute, position);
+ assertEquals(date, position.getFixTime());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/DistanceHandlerTest.java b/src/test/java/org/traccar/handler/DistanceHandlerTest.java
new file mode 100644
index 000000000..f7c6e42cd
--- /dev/null
+++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java
@@ -0,0 +1,30 @@
+package org.traccar.handler;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class DistanceHandlerTest {
+
+ @Test
+ public void testCalculateDistance() {
+
+ DistanceHandler distanceHandler = new DistanceHandler(new Config(), null);
+
+ Position position = distanceHandler.handlePosition(new Position());
+
+ assertEquals(0.0, position.getAttributes().get(Position.KEY_DISTANCE));
+ assertEquals(0.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE));
+
+ position.set(Position.KEY_DISTANCE, 100);
+
+ position = distanceHandler.handlePosition(position);
+
+ assertEquals(100.0, position.getAttributes().get(Position.KEY_DISTANCE));
+ assertEquals(100.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/FilterHandlerTest.java b/src/test/java/org/traccar/handler/FilterHandlerTest.java
new file mode 100644
index 000000000..ad8d244a6
--- /dev/null
+++ b/src/test/java/org/traccar/handler/FilterHandlerTest.java
@@ -0,0 +1,88 @@
+package org.traccar.handler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import java.util.Date;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class FilterHandlerTest extends BaseTest {
+
+ private FilterHandler passingHandler = new FilterHandler(new Config());
+ private FilterHandler filteringHandler;
+
+ @Before
+ public void before() {
+ Config config = new Config();
+ config.setString(Keys.FILTER_INVALID, String.valueOf(true));
+ config.setString(Keys.FILTER_ZERO, String.valueOf(true));
+ config.setString(Keys.FILTER_DUPLICATE, String.valueOf(true));
+ config.setString(Keys.FILTER_FUTURE, String.valueOf(5 * 60));
+ config.setString(Keys.FILTER_APPROXIMATE, String.valueOf(true));
+ config.setString(Keys.FILTER_STATIC, String.valueOf(true));
+ config.setString(Keys.FILTER_DISTANCE, String.valueOf(10));
+ config.setString(Keys.FILTER_MAX_SPEED, String.valueOf(500));
+ config.setString(Keys.FILTER_SKIP_LIMIT, String.valueOf(10));
+ config.setString(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE, String.valueOf(true));
+ filteringHandler = new FilterHandler(config);
+ }
+
+ private Position createPosition(
+ long deviceId,
+ Date time,
+ boolean valid,
+ double latitude,
+ double longitude,
+ double altitude,
+ double speed,
+ double course) {
+
+ Position position = new Position();
+ position.setDeviceId(deviceId);
+ position.setTime(time);
+ position.setValid(valid);
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ position.setAltitude(altitude);
+ position.setSpeed(speed);
+ position.setCourse(course);
+ return position;
+ }
+
+ @Test
+ public void testFilter() {
+
+ Position position = createPosition(0, new Date(), true, 10, 10, 10, 10, 10);
+
+ assertNotNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ position = createPosition(0, new Date(Long.MAX_VALUE), true, 10, 10, 10, 10, 10);
+
+ assertNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ position = createPosition(0, new Date(), false, 10, 10, 10, 10, 10);
+
+ assertNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ }
+
+ @Test
+ public void testSkipAttributes() {
+
+ Position position = createPosition(0, new Date(), true, 10, 10, 10, 0, 10);
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+
+ assertNotNull(filteringHandler.handlePosition(position));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/MotionHandlerTest.java b/src/test/java/org/traccar/handler/MotionHandlerTest.java
new file mode 100644
index 000000000..9e0859664
--- /dev/null
+++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java
@@ -0,0 +1,21 @@
+package org.traccar.handler;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.traccar.model.Position;
+
+public class MotionHandlerTest {
+
+ @Test
+ public void testCalculateMotion() {
+
+ MotionHandler motionHandler = new MotionHandler(0.01);
+
+ Position position = motionHandler.handlePosition(new Position());
+
+ assertEquals(false, position.getAttributes().get(Position.KEY_MOTION));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
new file mode 100644
index 000000000..3f0823245
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
@@ -0,0 +1,30 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.config.Config;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class AlertEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testAlertEventHandler() {
+
+ AlertEventHandler alertEventHandler = new AlertEventHandler(new Config(), new TestIdentityManager());
+
+ Position position = new Position();
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ Map<Event, Position> events = alertEventHandler.analyzePosition(position);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_ALARM, event.getType());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
new file mode 100644
index 000000000..0ccf9f6b4
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
@@ -0,0 +1,28 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class CommandResultEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testCommandResultEventHandler() throws Exception {
+
+ CommandResultEventHandler commandResultEventHandler = new CommandResultEventHandler();
+
+ Position position = new Position();
+ position.set(Position.KEY_RESULT, "Test Result");
+ Map<Event, Position> events = commandResultEventHandler.analyzePosition(position);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_COMMAND_RESULT, event.getType());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
new file mode 100644
index 000000000..dade20fb8
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
@@ -0,0 +1,27 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class IgnitionEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testIgnitionEventHandler() {
+
+ IgnitionEventHandler ignitionEventHandler = new IgnitionEventHandler(new TestIdentityManager());
+
+ Position position = new Position();
+ position.set(Position.KEY_IGNITION, true);
+ position.setValid(true);
+ Map<Event, Position> events = ignitionEventHandler.analyzePosition(position);
+ assertNull(events);
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
new file mode 100644
index 000000000..f57c16635
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
@@ -0,0 +1,119 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.reports.model.TripsConfig;
+
+public class MotionEventHandlerTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ @Test
+ public void testMotionWithPosition() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.set(Position.KEY_MOTION, true);
+ position.set(Position.KEY_TOTAL_DISTANCE, 0);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ Position nextPosition = new Position();
+
+ nextPosition.setTime(date("2017-01-01 00:02:00"));
+ nextPosition.set(Position.KEY_MOTION, true);
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNull(events);
+
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 600);
+ events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ nextPosition.setTime(date("2017-01-01 00:06:00"));
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+ events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(event);
+ event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+ @Test
+ public void testMotionWithStatus() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(new Date(System.currentTimeMillis() - 360000));
+ position.set(Position.KEY_MOTION, true);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState);
+
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+ @Test
+ public void testStopWithPositionIgnition() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, true, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.set(Position.KEY_MOTION, false);
+ position.set(Position.KEY_IGNITION, true);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(true);
+ deviceState.setMotionPosition(position);
+
+ Position nextPosition = new Position();
+ nextPosition.setTime(date("2017-01-01 00:02:00"));
+ nextPosition.set(Position.KEY_MOTION, false);
+ nextPosition.set(Position.KEY_IGNITION, false);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_STOPPED, event.getType());
+ assertFalse(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
new file mode 100644
index 000000000..515f37b5d
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
@@ -0,0 +1,128 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class OverspeedEventHandlerTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private void testOverspeedWithPosition(boolean notRepeat, long geofenceId) throws ParseException {
+ Config config = new Config();
+ config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat));
+ config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15));
+ config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false));
+ OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null);
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.setSpeed(50);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setOverspeedState(false);
+
+ Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, position, 40, geofenceId);
+ assertNull(events);
+ assertFalse(deviceState.getOverspeedState());
+ assertEquals(position, deviceState.getOverspeedPosition());
+ assertEquals(geofenceId, deviceState.getOverspeedGeofenceId());
+
+ Position nextPosition = new Position();
+ nextPosition.setTime(date("2017-01-01 00:00:10"));
+ nextPosition.setSpeed(55);
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+
+ nextPosition.setTime(date("2017-01-01 00:00:20"));
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType());
+ assertEquals(50, event.getDouble("speed"), 0.1);
+ assertEquals(40, event.getDouble(OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT), 0.1);
+ assertEquals(geofenceId, event.getGeofenceId());
+
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+
+ nextPosition.setTime(date("2017-01-01 00:00:30"));
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+
+ if (notRepeat) {
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+ } else {
+ assertNotNull(deviceState.getOverspeedPosition());
+ assertEquals(geofenceId, deviceState.getOverspeedGeofenceId());
+ }
+
+ nextPosition.setTime(date("2017-01-01 00:00:40"));
+ nextPosition.setSpeed(30);
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+ assertFalse(deviceState.getOverspeedState());
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+ }
+
+ private void testOverspeedWithStatus(boolean notRepeat) {
+ Config config = new Config();
+ config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat));
+ config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15));
+ config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false));
+ OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null);
+
+ Position position = new Position();
+ position.setTime(new Date(System.currentTimeMillis() - 30000));
+ position.setSpeed(50);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setOverspeedState(false);
+ deviceState.setOverspeedPosition(position);
+
+ Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, 40);
+
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType());
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+ }
+
+ @Test
+ public void testOverspeedEventHandler() throws Exception {
+ testOverspeedWithPosition(false, 0);
+ testOverspeedWithPosition(true, 0);
+
+ testOverspeedWithPosition(false, 1);
+ testOverspeedWithPosition(true, 1);
+
+ testOverspeedWithStatus(false);
+ testOverspeedWithStatus(true);
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BcdUtilTest.java b/src/test/java/org/traccar/helper/BcdUtilTest.java
new file mode 100644
index 000000000..86a32f725
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BcdUtilTest.java
@@ -0,0 +1,24 @@
+package org.traccar.helper;
+
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class BcdUtilTest {
+
+ @Test
+ public void testReadInteger() {
+ byte[] buf = {0x01, (byte) 0x90, 0x34};
+ int result = BcdUtil.readInteger(Unpooled.wrappedBuffer(buf), 5);
+ assertEquals(1903, result);
+ }
+
+ @Test
+ public void testReadCoordinate() {
+ byte[] buf = {0x03, (byte) 0x85, 0x22, 0x59, 0x34};
+ double result = BcdUtil.readCoordinate(Unpooled.wrappedBuffer(buf));
+ assertEquals(38.870989, result, 0.00001);
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BitBufferTest.java b/src/test/java/org/traccar/helper/BitBufferTest.java
new file mode 100644
index 000000000..c2abad36d
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BitBufferTest.java
@@ -0,0 +1,23 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class BitBufferTest {
+
+ @Test
+ public void test() {
+ BitBuffer buffer = new BitBuffer();
+
+ buffer.write(0b100100);
+ buffer.write(0b110110);
+ buffer.write(0b111111);
+ buffer.write(0b111111);
+
+ assertEquals(0b100, buffer.readUnsigned(3));
+ assertEquals(-7, buffer.readSigned(4));
+ assertEquals(0b10110, buffer.readUnsigned(5));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BitUtilTest.java b/src/test/java/org/traccar/helper/BitUtilTest.java
new file mode 100644
index 000000000..90431bf55
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BitUtilTest.java
@@ -0,0 +1,38 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BitUtilTest {
+
+ @Test
+ public void testCheck() {
+ assertFalse(BitUtil.check(0, 0));
+ assertTrue(BitUtil.check(1, 0));
+ assertFalse(BitUtil.check(2, 0));
+ }
+
+ @Test
+ public void testBetween() {
+ assertEquals(0, BitUtil.between(0, 0, 0));
+ assertEquals(1, BitUtil.between(1, 0, 1));
+ assertEquals(2, BitUtil.between(2, 0, 2));
+ assertEquals(2, BitUtil.between(6, 0, 2));
+ }
+
+ @Test
+ public void testFrom() {
+ assertEquals(1, BitUtil.from(1, 0));
+ assertEquals(0, BitUtil.from(1, 1));
+ }
+
+ @Test
+ public void testTo() {
+ assertEquals(2, BitUtil.to(2, 2));
+ assertEquals(0, BitUtil.to(2, 1));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java
new file mode 100644
index 000000000..5737b9ff5
--- /dev/null
+++ b/src/test/java/org/traccar/helper/ChecksumTest.java
@@ -0,0 +1,39 @@
+package org.traccar.helper;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+public class ChecksumTest {
+
+ @Test
+ public void testCrc8() {
+ ByteBuf buf = Unpooled.copiedBuffer("123456789", StandardCharsets.US_ASCII);
+
+ assertEquals(0xF7, Checksum.crc8(Checksum.CRC8_EGTS, buf.nioBuffer()));
+ assertEquals(0xD0, Checksum.crc8(Checksum.CRC8_ROHC, buf.nioBuffer()));
+ }
+
+ @Test
+ public void testCrc16() {
+ ByteBuf buf = Unpooled.copiedBuffer("123456789", StandardCharsets.US_ASCII);
+
+ assertEquals(0xBB3D, Checksum.crc16(Checksum.CRC16_IBM, buf.nioBuffer()));
+ assertEquals(0x4B37, Checksum.crc16(Checksum.CRC16_MODBUS, buf.nioBuffer()));
+ assertEquals(0x906e, Checksum.crc16(Checksum.CRC16_X25, buf.nioBuffer()));
+ assertEquals(0x29b1, Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.nioBuffer()));
+ assertEquals(0x2189, Checksum.crc16(Checksum.CRC16_KERMIT, buf.nioBuffer()));
+ assertEquals(0x31c3, Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer()));
+ }
+
+ @Test
+ public void testLuhn() {
+ assertEquals(7, Checksum.luhn(12345678901234L));
+ assertEquals(0, Checksum.luhn(63070019470771L));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DateBuilderTest.java b/src/test/java/org/traccar/helper/DateBuilderTest.java
new file mode 100644
index 000000000..b6323cc1d
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DateBuilderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateBuilderTest {
+
+ @Test
+ public void testDateBuilder() throws ParseException {
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(2015, 10, 20).setTime(1, 21, 11);
+
+ assertEquals(dateFormat.parse("2015-10-20 01:21:11"), dateBuilder.getDate());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DateUtilTest.java b/src/test/java/org/traccar/helper/DateUtilTest.java
new file mode 100644
index 000000000..ec42e71ae
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DateUtilTest.java
@@ -0,0 +1,30 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateUtilTest {
+
+ @Test
+ public void testCorrectDate() throws ParseException {
+
+ DateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ assertEquals(f.parse("2015-12-31 23:59:59"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2016-01-01 23:59:59"), Calendar.DAY_OF_MONTH));
+
+ assertEquals(f.parse("2016-01-01 00:00:02"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2016-01-01 00:00:02"), Calendar.DAY_OF_MONTH));
+
+ assertEquals(f.parse("2016-01-01 00:00:02"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2015-12-31 00:00:02"), Calendar.DAY_OF_MONTH));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java
new file mode 100644
index 000000000..a7457b6c4
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java
@@ -0,0 +1,24 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class DistanceCalculatorTest {
+
+ @Test
+ public void testDistance() {
+ assertEquals(
+ DistanceCalculator.distance(0.0, 0.0, 0.05, 0.05), 7863.0, 10.0);
+ }
+
+ @Test
+ public void testDistanceToLine() {
+ assertEquals(DistanceCalculator.distanceToLine(
+ 56.83801, 60.59748, 56.83777, 60.59833, 56.83766, 60.5968), 33.0, 5.0);
+
+ assertEquals(DistanceCalculator.distanceToLine(
+ 56.83753, 60.59508, 56.83777, 60.59833, 56.83766, 60.5968), 105.0, 5.0);
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/LocationTreeTest.java b/src/test/java/org/traccar/helper/LocationTreeTest.java
new file mode 100644
index 000000000..21604144a
--- /dev/null
+++ b/src/test/java/org/traccar/helper/LocationTreeTest.java
@@ -0,0 +1,30 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class LocationTreeTest {
+
+ @Test
+ public void testLocationTree() {
+
+ List<LocationTree.Item> items = new ArrayList<>();
+ items.add(new LocationTree.Item(1, 1, "a"));
+ items.add(new LocationTree.Item(3, 2, "b"));
+ items.add(new LocationTree.Item(1, 3, "c"));
+ items.add(new LocationTree.Item(4, 3, "d"));
+
+ LocationTree tree = new LocationTree(items);
+
+ assertEquals("a", tree.findNearest(new LocationTree.Item(1f, 1f)).getData());
+ assertEquals("d", tree.findNearest(new LocationTree.Item(10f, 10f)).getData());
+ assertEquals("c", tree.findNearest(new LocationTree.Item(1f, 2.5f)).getData());
+ assertEquals("a", tree.findNearest(new LocationTree.Item(1.5f, 1.5f)).getData());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/LogTest.java b/src/test/java/org/traccar/helper/LogTest.java
new file mode 100644
index 000000000..853eb05c9
--- /dev/null
+++ b/src/test/java/org/traccar/helper/LogTest.java
@@ -0,0 +1,14 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class LogTest {
+
+ @Test
+ public void testLog() {
+ assertEquals("test - Exception (LogTest:11 < ...)", Log.exceptionStack(new Exception("test")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java
new file mode 100644
index 000000000..1ffe68c8b
--- /dev/null
+++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ObdDecoderTest {
+
+ @Test
+ public void testDecode() {
+
+ assertEquals(83, ObdDecoder.decode(0x01, "057b").getValue());
+ assertEquals(1225, ObdDecoder.decode(0x01, "0C1324").getValue());
+ assertEquals(20, ObdDecoder.decode(0x01, "0D14").getValue());
+ assertEquals(64050, ObdDecoder.decode(0x01, "31fa32").getValue());
+ assertEquals(25, ObdDecoder.decode(0x01, "2F41").getValue());
+
+ }
+
+ @Test
+ public void testDecodeCodes() throws Exception {
+ assertEquals("P0D14", ObdDecoder.decodeCodes("0D14").getValue());
+ assertEquals("dtcs", ObdDecoder.decodeCodes("0D14").getKey());
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/PatternBuilderTest.java b/src/test/java/org/traccar/helper/PatternBuilderTest.java
new file mode 100644
index 000000000..4c76bc463
--- /dev/null
+++ b/src/test/java/org/traccar/helper/PatternBuilderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class PatternBuilderTest {
+
+ @Test
+ public void testPatternBuilder() {
+ assertEquals("\\$GPRMC", new PatternBuilder().text("$GPRMC").toString());
+ assertEquals("(\\d{2}\\.[0-9a-fA-F]+)", new PatternBuilder().number("(dd.x+)").toString());
+ assertEquals("a(?:bc)?", new PatternBuilder().text("a").text("b").text("c").optional(2).toString());
+ assertEquals("a|b", new PatternBuilder().expression("a|b").toString());
+ assertEquals("ab\\|", new PatternBuilder().expression("ab|").toString());
+ assertEquals("|", new PatternBuilder().or().toString());
+ assertEquals("\\|\\d|\\d\\|", new PatternBuilder().number("|d|d|").toString());
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/PatternUtilTest.java b/src/test/java/org/traccar/helper/PatternUtilTest.java
new file mode 100644
index 000000000..77660078a
--- /dev/null
+++ b/src/test/java/org/traccar/helper/PatternUtilTest.java
@@ -0,0 +1,18 @@
+package org.traccar.helper;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class PatternUtilTest {
+
+ @Ignore
+ @Test
+ public void testCheckPattern() {
+
+ assertEquals("ab", PatternUtil.checkPattern("abc", "abd").getPatternMatch());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/model/MiscFormatterTest.java b/src/test/java/org/traccar/model/MiscFormatterTest.java
new file mode 100644
index 000000000..eb93d5b38
--- /dev/null
+++ b/src/test/java/org/traccar/model/MiscFormatterTest.java
@@ -0,0 +1,20 @@
+package org.traccar.model;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class MiscFormatterTest {
+
+ @Test
+ public void testToString() throws Exception {
+
+ Position position = new Position();
+ position.set("a", "1");
+ position.set("b", "2");
+ position.set("a", "3");
+
+ assertEquals("<info><a>3</a><b>2</b></info>", MiscFormatter.toXmlString(position.getAttributes()));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/notification/NotificiationMailTest.java b/src/test/java/org/traccar/notification/NotificiationMailTest.java
new file mode 100644
index 000000000..b82bec02e
--- /dev/null
+++ b/src/test/java/org/traccar/notification/NotificiationMailTest.java
@@ -0,0 +1,59 @@
+package org.traccar.notification;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.util.Properties;
+
+public class NotificiationMailTest {
+
+ private static final String FROM = "notification@traccar.org";
+ private static final String TO = "anton@traccar.org";
+
+ private static final String BODY = "Test email body.";
+ private static final String SUBJECT = "Test";
+
+ private static final String SMTP_USERNAME = "username";
+ private static final String SMTP_PASSWORD = "password";
+
+ private static final String HOST = "email-smtp.us-west-2.amazonaws.com";
+
+ private static final int PORT = 25;
+
+ @Ignore
+ @Test
+ public void test() throws Exception {
+
+ Properties props = System.getProperties();
+ props.put("mail.transport.protocol", "smtps");
+ props.put("mail.smtp.port", PORT);
+
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+ props.put("mail.smtp.starttls.required", "true");
+
+ Session session = Session.getInstance(props);
+
+ MimeMessage msg = new MimeMessage(session);
+ msg.setFrom(new InternetAddress(FROM));
+ msg.setRecipient(Message.RecipientType.TO, new InternetAddress(TO));
+ msg.setSubject(SUBJECT);
+ msg.setContent(BODY, "text/plain");
+
+ Transport transport = session.getTransport();
+
+ try {
+ transport.connect(HOST, SMTP_USERNAME, SMTP_PASSWORD);
+ transport.sendMessage(msg, msg.getAllRecipients());
+ } finally {
+ transport.close();
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java
new file mode 100644
index 000000000..599dd6190
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java
@@ -0,0 +1,38 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AdmProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AdmProtocolDecoder decoder = new AdmProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "010042033836313331313030323639343838320501000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073"));
+
+ verifyPosition(decoder, binary(
+ "01002e40041c0744009dfe6742c6c860427402000000f4ff077752c8f55b000000000b4132010213430100041e"));
+
+ verifyPosition(decoder, binary(
+ "01002680336510002062A34C423DCF8E42A50B1700005801140767E30F568F2534107D220000"));
+
+ verifyPosition(decoder, binary(
+ "010022003300072020000000000000000044062A330000000000107F10565D4A8310"));
+
+ verifyPosition(decoder, binary(
+ "0100268033641080207AA34C424CCF8E4239030800005B01140755E30F560000F00F70220000"));
+
+ verifyPosition(decoder, binary(
+ "01002680336510002062A34C423DCF8E42A50B1700005801140767E30F568F2534107D220000"));
+
+ verifyPosition(decoder, binary(
+ "01002200333508202000000000000000007F0D9F030000000000E39A1056E24A8210"));
+
+ verifyNotNull(decoder, binary(
+ "01008449443d3120536f66743d30783531204750533d313036382054696d653d30383a35393a32302031302e30392e31372056616c3d30204c61743d36312e36373738204c6f6e3d35302e3832343520563d3020536174436e743d342b3720537461743d30783030313020496e5f616c61726d3d30783030000000000000000000000000"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
new file mode 100644
index 000000000..cb0a31ceb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Anatoliy Golubev (darth.naihil@gmail.com)
+ *
+ * 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.
+ */
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class AdmProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ AdmProtocolEncoder encoder = new AdmProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_DEVICE_STATUS);
+ assertEquals("STATUS\r\n", encoder.encodeCommand(command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "INPUT 0");
+ assertEquals("INPUT 0\r\n", encoder.encodeCommand(command));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java
new file mode 100644
index 000000000..9cb9f695a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AisProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AisProtocolDecoder decoder = new AisProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "!AIVDM,2,1,8,A,53UlSb01l>Ei=H4KF218PTpv222222222222221?8h=766gB0<Ck11DTp888,0*14s:MTb827ebc7686b,c:1481688227737*4d\\\r\n" +
+ "!AIVDM,2,2,8,A,88888888888,2*24\r\n" +
+ "!AIVDM,1,1,,A,13T=Qr0P001cmmLEf;A00?wN0PSU,0*29\r\n" +
+ "!AIVDM,1,1,,A,35Qf023Ohi1n5gdDRLW5FSQP00u@,0*18\r\n" +
+ "!AIVDM,1,1,,A,B3P@f>0000K6J;5KAIT03wpUkP06,0*5D\\s:MTb827ebc7686b,c:1481688230418*45\\\r\n" +
+ "!AIVDM,1,1,,B,B52Q8a@00Ul`9N5@ssbmCwr5oP06,0*36\r\n" +
+ "!AIVDM,1,1,,A,1815<S@01VQnKGlE0sk:WHcT0@O4,0*78\r\n" +
+ "!AIVDM,1,1,,A,35N7G;5OhQG?oJfE`G`cM9E`0001,0*6C\r\n" +
+ "!AIVDM,1,1,,B,13Ug;r0P011cqHJEevuEiOwf0L3h,0*6A\r\n" +
+ "!AIVDM,1,1,,A,13MKsr?0001dJC2Ee4W;jnal08Qj,0*00\r\n\r\n"));
+
+ verifyPositions(decoder, text(
+ "!AIVDM,1,1,,A,H3FUli4T000000000000001p0400,0*6E\\s:MTb827eba584a8,c:1481688176110*46\\\r\n" +
+ "!AIVDM,1,1,,B,13UhUh0P01QcoRTEdtB>4?v<2D=j,0*54\r\n\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java
new file mode 100644
index 000000000..47ea8137e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AlematicsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AlematicsProtocolDecoder decoder = new AlematicsProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$T,2,64,866050035975497,20180726103446,20180726103514,23.033305,72.558032,0,0,41,5.4,4,0,0,0.000,12.960,0,"));
+
+ verifyPosition(decoder, text(
+ "$T,2,65,866050035975497,20180726103646,20180726103736,23.033305,72.558032,0,0,41,5.4,4,0,0,0.000,12.976,0,0"));
+
+ verifyPosition(decoder, text(
+ "$T,2,552,868259020159698,20170515060949,20170515060949,25.035277,121.561986,0,202,78,1.0,8,1,0,0.000,12.768,1629,38,12770,4109,9"));
+
+ verifyPosition(decoder, text(
+ "$T,2,553,868259020159698,20170515061019,20170515061019,25.035295,121.561981,0,202,79,1.0,8,1,0,0.000,12.768,1629,38,12772,4109,8"));
+
+ verifyPosition(decoder, text(
+ "$T,4,4,868259020159698,20170515061033,20170515061033,25.035303,121.561975,0,202,81,1.7,6,1,0,0.000,12.770,1629,0,$S,A1,1,,12345.67,88.4,301.5,,2593.25,12.4,89.2,,5999.44,789.572,2345.67,,10763,1024,5,"));
+
+ verifyPosition(decoder, text(
+ "$T,2,554,868259020159698,20170515061049,20170515061049,25.035309,121.561976,0,202,82,1.1,7,1,0,0.000,12.768,1629,38,12770,4109,9"));
+
+ verifyPosition(decoder, text(
+ "$T,4,5,868259020159698,20170515061058,20170515061058,25.035308,121.561976,0,202,82,1.2,7,1,0,0.000,12.772,1629,0,$S,A1,1,,12345.67,88.4,301.5,,2593.25,12.4,89.2,,5999.44,789.572,2345.67,,10763,1024,5,"));
+
+ verifyPosition(decoder, text(
+ "$T,50,592,868259020159698,20170515062915,20170515062915,25.035005,121.561555,0,31,89,3.7,5,1,0,0.000,12.752,1629,38,12752,4203,6"));
+
+ verifyPosition(decoder, text(
+ "$T,50,594,868259020159698,20170515062928,20170515062928,25.035151,121.561671,0,31,93,1.8,5,0,0,0.000,12.752,1629,38,12756,4205,6"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java
new file mode 100644
index 000000000..e3aa0550b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AnytrekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AnytrekProtocolDecoder decoder = new AnytrekProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "78783500300086428703204121160085015111050C0A0D20C6FD24A102FF8EAC0C01001404000000FFFFFFFF131702210000000000000000000D0A"));
+
+ verifyPosition(decoder, binary(
+ "787835003000867279033457792c009801001209080a3408c81b2a7d0305b88b0c00001ccb0000000f00000002b90174f30b000000000000000d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java
new file mode 100644
index 000000000..c32abe6a8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ApelProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ApelProtocolDecoder decoder = new ApelProtocolDecoder(null);
+
+ /*byte[] buf1 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x13,0x00,0x44,0x34,0x2A,0x3E,0x53,0x3A,0x38,0x36,0x31,0x37,0x38,0x35,0x30,0x30,0x35,0x32,0x30,0x35,0x30,0x37,0x39};
+ verifyNull(decoder, text( ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf1)));*/
+
+ //0c002900f12a00000f003235303032363533343135313036340f0033353638393530333632373938313101002000000000
+ //5c00380046e6a95136b693277f11b41a00172709f2ff03160002b9bc630007000000000000000000000000000000c31071090000880500000000000000000000
+ //5c00380072e7a95136b693277f11b41a00172709f2ff03160002b9bc630007000000000000000000000000000000c31071090000880500000000000000000000
+
+ //7900040069ea030000000000
+ //8300c20003006aea03005c003800223aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c3107209000089050000000000006bea03005c003800403aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c3107209000089050000000000006cea03005c0038005e3aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c31072090000890500000000000000000000
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
new file mode 100644
index 000000000..581f7696f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class AplicomFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AplicomFrameDecoder decoder = new AplicomFrameDecoder();
+
+ assertEquals(
+ binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"),
+ decoder.decode(null, null, binary("33353733303030373030393233333644C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C")));
+
+ assertEquals(
+ binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"),
+ decoder.decode(null, null, binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java
new file mode 100644
index 000000000..0b4180bd0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java
@@ -0,0 +1,93 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AplicomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AplicomProtocolDecoder decoder = new AplicomProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "434946010A0100075253F85F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEEA0000FEE90000F0030000F0040000FEF10000FEF20000FEF50000FEFC0000FEC10000FEE500"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667c0e462019800b05f7700005b7d3bdd00000000000000000000000000000000805e420fd60a0d57fffffb002141100000011001000c0932200000000000000a18136fcafefffffb002141100000011001000c0932200000000000000a1812e6dbfefffffb002141100000011001000c746578746167204e000008e3980bfefffffb002141100000011001000ce2009000170400890310f290e873fefffffb002141100000011001000ce2009000170400890290f28e409afefffffb002141100000011001000c0932200000000000000a1812e6dbfefffffb002141100000011001000ce2009000170400890280f28d4e2dfefffffb002141100000011001000c0001460000000000000021427a6cfefffffb002141100000011001000c0001460000000000000021606a6efefffffb002141100000011001000c746578746167204e000008badcc4fefffffb002141100000011001000c00014600000000000000218c0843fefffffb002141100000011001000ce2009000170400890270f6d45e09fefffffb002141100000011001000ce2801160600002053cd57a16549efe"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667ca8e6b003200b45f7700f05b6565e75b6565e6031f845100c6f0e70c00001483387c0fe60001000a263433233031303138373433303746230d0a"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667ca8e6b002d00305f7700d45b307a9a5b307a97031f840a00c6f05b0600000083000000263433233031303138373433303746230d0a"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667c4316500e903ffdfbc00f059aebeb659aebeb302e3f5860065fe32120000ae0000000e47000000000000000000000000000127cd0000014c00000000000000ff010a002900000000000000014542016d0001010090070e140144f667c4316559ae620402e3f7f300660714c0010d15ff0f3332373937313100000000000000000000002a01010737341d331fffcf0103020b8601060c0001a5860001a58600000000010b1001ca01ca7d007d007c7cffffffffffff010a240000ffff0000000100010001ffff0000ffffffffffffffffffffffffffff00000002ffff010c06fec6febffeec"));
+
+ verifyAttributes(decoder, binary(
+ "45c20144f667c06ff9005d0161ef17000104596da2dc4b10c0c01d99020d6c04004cba7a010d44463030303235333731363238303030000000000000000000000000000000000000000000000000000001010d44463030303235333731363238303030000000000000031c"));
+
+ verifyAttributes(decoder, binary(
+ "45c20144f667c07287008c01ffff6d01000059368963d0340a0616207d7f4b10c0c019e6000039d7000039d71f40ffff5001574442393036363035533132333435363700014142432d33343520202020202000011231303331373139343039303030303031000000000000000000000000000000000000000000000000000001011231303331373139343039303030303031000000000000005a"));
+
+ verifyAttributes(decoder, binary(
+ "46c30144f667c1711f00340007ff750058b8f77701037c06b8000000330033000000000b760000425e0100640000b3a90185d5823155000131070204000219641004"));
+
+ verifyAttributes(decoder, binary(
+ "46c30144f667c1711f00340007ff75005891601401025707b50236003b003b003500000a9300006bd50100640000a5250167d2f9034c01010107020400021a901004"));
+
+ verifyAttributes(decoder, binary(
+ "48C1014143B4493145004900203F6D014B5557C20003000015060110FF00C800000000000000003D01141E283C500100260404010200000000000000000000000000C8000000000000010200110019001E0064019003E8"));
+
+ verifyAttributes(decoder, binary(
+ "48c10144b9de54e6b2008700205f710a57d23ec957d23b8d00000000300d0106ff00000000000000000000000000000000000000000000000000000000000000010a141e28323c46505a646e7801000f020104ff000000000000000000010102000f020104ff000000000000000000010103000f020104ff000000000000000000010105000f020104ff0000000000000000000101"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd1970230d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd9170250d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6de0970270d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "44c3014645e8ecff3c00ea03ffffbc00f457d68a6557d68a6303bb55fa018843da1100009881000000000000000000000000000000000000000000000000000000000000000000000000000000ff0056007600000000000000014542016d0001010095070e14014645e8ecff3c57d68a6403bb55fa018843dac0010d14ff050102030405060708090a0b0c0d0e0f10112a01010730343f3c1ff5cf01020700007d007d23010103022f2e01060c67452301efcdab8967452301010b10000000007d007d007d7dffffffffffff010a2400000000000000010000000000000000ffffffffffffffff00010001ffff00000000ffff010c02fec6"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b01f056d3e62856d3e626031f845f00c6ee440800000000000000000017bd1cb30000"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b00f056d3e64756d3e646031f845f00c6ee440800000000000000000017bd1cb30001"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66001f00221f0b01f456ba1e0d56ba1e0b031f842200c6ef550c000000000017bd1cb30004"));
+
+ verifyAttributes(decoder, binary(
+ "44c3014645e8e9bada003e03fff7070055a4f24200000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00000001000000000000000044c3014645e8e9bada003e03fff77bff55a4f24300000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00300002000000000000000044c3014645e8e9bada003e03fff7690655a4f24500000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff003000030000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e9d29a002d0022ff6d00f455893b4d55893b4c027a7e1500189d710800009e0000000000000000000000023300000000000000009d"));
+
+ verifyPosition(decoder, binary(
+ "44C20146B710C158DA002100B09F0700C054CA0EA254CA0E9C03BE0BF6015D7069070000142A600000000000000001"));
+
+ verifyPosition(decoder, binary(
+ "44C20143720729D6840043031fff7191C0450ef906450ef90603b20b8003b20b80066465b3870ce30f010ce30ce3003200001520000000030aa200003b13000000320300000bcb17acff0099000186a002"));
+
+ verifyPosition(decoder, binary(
+ "440129D684002b0700C0450ef906450ef90603b20b8003b20b80066465b3870ce30f010ce30ce300003b130300000bcb170a"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e9152e008900b09f7700f4558c07e8558c07e703be0bd8015d6faf0e0000003240000000000000000f4349460107010007558c07e70000000000000002d209df028f05fffe00000000000000002eff13fe11fe1a00011000000000000010ff11ff3cff11008c00080060f41b0043502015000000000000020d0000030d0000040c0000040d0000050c0000050d0000058c"));
+
+ verifyPosition(decoder, binary(
+ "44c20144563508385a009500b09f7700c0555ea99e555ea9b103bb569f01883ff50b00002a30f000000000000013074349460108010007555ea99e000000000000003f0000ae017605b3ff00000000010000006700d900d500000003000000000000006700d900d500000087002500c4ff0000435020150000000040512001000000000000020d0000030d0000040c0000040d0000050c0000050d0000058c0000060c"));
+
+ verifyPosition(decoder, binary(
+ "44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java
new file mode 100644
index 000000000..ac0858e82
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AppelloProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AppelloProtocolDecoder decoder = new AppelloProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,867273024233699,UTCTIME,0.000000,0.000000,0,0,0,0,L,262:3:c703:4bf8:64:255|262:3:c703:9a18:45|262:3:c703:e838:33|262:3:c703:7190:20|262:3:c704:d896:17|,02,44,,31,,4.20,0,0,86/44/24,,,,26,02264DFF6E16:69|4C09D408554E:79|4C09D408554F:79|E0885DE705E5:81|E2885DE705E7:81|246511122CCC:83|,34925"));
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,867273024233699,UTCTIME,0.000000,0.000000,0,0,0,0,L,262:3:c703:4bf8:64:1|262:3:c703:9a18:44|262:3:c703:e838:33|262:3:c703:7190:21|262:3:c704:d896:18|262:3:c703:71a6:13|262:3:c703:253d:13|,02,44,,22,,4.20,0,0,86/44/24,,,,30,02264DFF6E16:67|E0885DE705E5:87|B4A5EF284B94:88|E2885DE705E7:85|4C09D408554E:78|3481C4D71B13:43|,24033"));
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,860719028336968,UTCTIME,-12.112660,-77.045189,0,0,3,-0,L,716,10,049C,2A47,23,,4.22,,53,999/00/00,,,,,,59826,"));
+
+ verifyPosition(decoder, text(
+ "FOLLOWIT,860719028336968,160211221959,-12.112660,-77.045258,1,0,6,116,F,716,17,4E85,050C,29,,4.22,,39,999/00/00,,,,,,46206,"));
+
+ verifyPosition(decoder, text(
+ "FOLLOWIT,359586019278139,130809160321,22.340218,114.030737,60,120,05,152,F,460,01,2533,720B,31,out,3.90,1,192,20/00/00,12.5,100%,80,45,1CFA68BB754E:60|2CFA68BB754E:100|3CFA68BB754E:100|4CFA68BB754E:100|5CFA68BB754E:100|,46672"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
new file mode 100644
index 000000000..ad13c60f6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AppletProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AppletProtocolDecoder decoder = new AppletProtocolDecoder(null);
+
+ DefaultHttpHeaders headers = new DefaultHttpHeaders();
+
+ headers.add("HOST", "192.168.0.1:8080");
+ headers.add("X-Admin-Protocol", "globalplatform-remote-admin/1.0");
+ headers.add("X-Admin-From", "8943170080001406197F");
+ headers.add("User-Agent", "oma-scws-admin-agent/1.1");
+ headers.add("From", "8943170080001406197F");
+
+ verifyNull(decoder, request(HttpMethod.POST, "/pli?=", headers));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java
new file mode 100644
index 000000000..793b2c646
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java
@@ -0,0 +1,79 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AquilaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeA() throws Exception {
+
+ AquilaProtocolDecoder decoder = new AquilaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170215089,20,18.462809,73.824188,170613182744,A,01,123456,*37"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170215089,1,18.462809,73.824188,170613182744,A,19,0,0,256,4,4.860000,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,259,3731,*37"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170222318,101,22.846016,75.949104,170321103506,A,0,0,244991,0,10,0.860000,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,12483,294,*3D"));
+
+ verifyNull(decoder, text(
+ "$$CLIENT_1ZF,170222318,15,2_00AP,70.35.195.185,5089,internet,T1:10 S,T2:1 M,Ad1:9164061023,Ad2:9164061023,TOF:0 S,,OSC:75 KM,OST:0 S,GPS:YES,Ignition:ON,*75"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170222318,1,22.836912,75.942215,170321110736,A,11,12,247196,103,10,0.810000,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,13325,306,*36"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,130329214,1,12.962985,77.576484,140127165433,A,22,0,0,140,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1253,420,1,0,1,0,0,1,P(01:410104000102|05:410521|0C:410C0000|0D:410D65|21:4121161C),D(P0121|B2105),-895,745,-145,300,*26"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1NS,101010119,1,22.845943,75.949059,170202184000,A,27,0,0,120,31141,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,399,3709,0,0,0,0,0,0,P(01:|04:|05:|0B:|0C:|0D:|10:|1C:|21:|23:|30:|31:|1F:|11:|00:|00:|),D(),-89,44,-1062,0,*49"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1DT,151028368,1,19.108438,72.925308,160628154920,A,22,0,0,131,3503,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*1D"));
+
+ verifyNull(decoder, text(
+ "$$CLIENT_1DT,160319372,1,28.549541,77.249802,160628140743,A,23,0,-65025,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,*0D"));
+
+ verifyPosition(decoder, text(
+ "$$SRINI_1MS,141214807,1,12.963515,77.533844,150925161628,A,27,0,8,0,68,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,*43"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,130329214,1,12.962985,77.576484,140127165433,A,22,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,*26"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1WP,141216511,3,12.963123,77.534012,150908163534,A,31,0,0,0,7,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*28"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1WP,141216511,3,12.963212,77.533989,150908164041,V,31,0,0,0,8,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*2A"));
+
+ }
+
+ @Test
+ public void testDecodeB() throws Exception {
+
+ AquilaProtocolDecoder decoder = new AquilaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$Header,nliven,EMR,861693034634154,NM,09112017155133,A,12.976495,N,77.549713,E,906.0,0.0,23,G,KA01I2000,+919844098440*4B"));
+
+ verifyPosition(decoder, text(
+ "$EPB,iTriangle1,EMR,864495034445822,SP,03082018110730,A,22.829292,N,75.935806,E,543.0,0.0,0,G,KA01G1234,+9164061023*13"));
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS_2,NR,1,L,864495034490141,KA01I2000,1,19042018,102926,22.846401,N,75.948952,E,0.0,311,5,578.0,3.80,3.67,AirTel,0,1,12.5,4.3,1,C,14,404,93,0456,16db,29,ebd8,0458,28,3843,18ab,25,072e,18ab,22,35da,0458,0000,00,031181,0.0,0.0,0,()*34"));
+
+ verifyPosition(decoder, text(
+ "$Header,nliven,1_37T02B0164MAIS,BR,6,L,861693034634154,KA01I2000,1,09112017,160702,12.976593,N,77.549782,E,25.1,344,15,911.0,1.04,0.68,Airtel,1,1,11.8,3.8,1,C,24,404,45,61b4,9ad9,31,9adb,61b4,35,ffff,0000,33,ffff,0000,31,ffff,0000,0001,00,000014,0.0,0.1,4,()*1E"));
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS_2,NR,1,L,864495034490141,KA01I2000,1,31032018,122247,22.845999,N,75.949005,E,0.0,44,16,545.0,1.19,0.65,AirTel,1,1,12.0,4.3,0,C,13,404,93,0456,16db,27,16dd,0456,22,3843,18ab,19,ebd8,0458,14,072c,18ab,0101,00,003735,0.0,0.0,0,()*48"));
+
+ verifyNull(decoder, text(
+ "$Header,nliven,KA01I2000,861693034634154,1_37T02B0164MAIS,AIS140,12.976545,N,77.549759,E*50"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java
new file mode 100644
index 000000000..8498bb7f9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Ardi01ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Ardi01ProtocolDecoder decoder = new Ardi01ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "013227003054776,20141010052719,24.4736042,56.8445807,110,289,40,7,5,78,-1"),
+ position("2014-10-10 05:27:19.000", true, 56.84458, 24.47360));
+
+ verifyPosition(decoder, text(
+ "013227003054776,20141010052719,24.4736042,56.8445807,110,289,40,7,5,78,-1"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java
new file mode 100644
index 000000000..18901d9a6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArknavProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArknavProtocolDecoder decoder = new ArknavProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "358266016278447,05*827,000,L001,V,4821.6584,N,01053.8650,E,000.0,000.0,00.0,08:46:04 17-03-16,9.5A,D7,0,79,0,,,,"),
+ position("2016-03-17 08:46:04.000", false, 48.36097, 10.89775));
+
+ verifyPosition(decoder, text(
+ "123456789012345,05*850,000,L001,A,2459.3640,N,12125.2958,E,000.0,224.8,00.8,07:47:26 09-09-05,9.00,D3,0,C4,1,,,,"));
+
+ verifyPosition(decoder, text(
+ "123456789012345,05*850,000,L001,A,2459.3640,N,12125.2958,E,000.0,224.8,00.8,07:47:26 09-09-05,9.00,D3,0,C4,1,,,00000000,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java
new file mode 100644
index 000000000..dfe1435d1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArknavX8ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArknavX8ProtocolDecoder decoder = new ArknavX8ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "351856045213782,241111"));
+
+ verifyPosition(decoder, text(
+ "1G,181213092101,A,0347.0756N,09842.7435E,0.0,183,1.1,11008000"));
+
+ verifyAttributes(decoder, text(
+ "2G,181213092101,08,4084.0,00.04,04.01,000396255.0"));
+
+ verifyNull(decoder, text(
+ "2R,090214235955,00,,00.04,03.76,001892024.9"));
+
+ verifyNull(decoder, text(
+ "351856040005407,240101"));
+
+ verifyPosition(decoder, text(
+ "1R,110509053244,A,2457.9141N,12126.3321E,220.0,315,10.0,00000000"));
+
+ verifyNull(decoder, text(
+ "2R,110509053244,837493,,998372,,,"));
+
+ verifyPosition(decoder, text(
+ "1G,110509053245,A,2457.9141N,12126.3192E,3.1,35,2.0,00000001"));
+
+ verifyPosition(decoder, text(
+ "1G,110509053246,A,2457.9121N,12126.3415E,2.0,288,1.7,00000000"));
+
+ verifyPosition(decoder, text(
+ "1M,110509053247,A,2457.9118N,12126.3522E,1.0,55,2.2,00000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
new file mode 100644
index 000000000..6b075facc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArnaviProtocolDecoder decoder = new ArnaviProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$AV,V4,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,80.0,56.1,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
+
+ verifyNull(decoder, text(
+ "$AV,V3DI,85164,20707,-1,19,0008C56A,000879AC,0C000002,863071013041618,89997077111301204297,*0B"));
+
+ verifyNull(decoder, text(
+ "$AV,V6SD,85164,20708,-1,3,6,37,33,*52"));
+
+ verifyAttributes(decoder, text(
+ "$AV,V4,85164,20709,1148,418,-1,0,1,192,0,0,0,0,0,0,,,000023,0000.0000N,00000.0000E,0.0,0.0,060180,0,0,32767,*4F"));
+
+ verifyNull(decoder, text(
+ "$AV,V3GSMINFO,85164,-1,20450,KMOBILE,1,2,1,23,0,40101,cc3,1,ce19,401,16,65304,5613,72,,SF*7F"));
+
+ verifyAttributes(decoder, text(
+ "$AV,V4,85164,20451,1146,418,-1,1,1,192,0,0,0,0,0,0,,,104340,0000.0000N,00000.0000E,0.0,0.0,060219,11,0,32767,,SF*47"));
+
+ verifyNull(decoder, text(
+ "$AV,V6SD,85164,20452,-1,3,3,5,6769,,SF*5D"));
+
+ verifyPosition(decoder, text(
+ "$AV,V2,32768,12487,2277,203,-1,0,0,193,0,0,1,13,200741,5950.6773N,03029.1043E,0.0,0.0,121012,*6E"));
+
+ verifyPosition(decoder, text(
+ "$AV,V3,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java
new file mode 100644
index 000000000..453ec9da5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AstraProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AstraProtocolDecoder decoder = new AstraProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "4b00700529c0c265976b8202cba9ff00676d864554a9c30000000020073401006436000300030008000000000000a0000100001920c43d00009600428302cba9ff00676d864554aa3e000000002007240100643b000300020008000000000000b0000100001920c43d00009600420f0e"));
+
+ verifyPositions(decoder, binary(
+ "4b00320524c1da58769e6d0322617effe874024453065600a800000100080000643e0000000000000000000000069500e7bb"));
+
+ verifyPositions(decoder, binary(
+ "4b013c02213aec35c501ad031368b8ffcd1ad043e5c4500c79000100003101005c490c001c0009020200020015069600ae03136801ffcd1af143e5c452125e000100003101005c491200090011010000020015068500af0313629effcd1f4b43e5c45d1e46000100003101005c491e00080409040500040015068700b0031359d5ffcd35ad43e5c47b2a3b000001003101005c492a1b1a0d0b0f0b00080015068700b103134984ffcd4b1e43e5c4913354000100003101005c49340b0103090606000f0015067700b203132e1affcd5a5a43e5c4af3348000001003101005c4935070a08000a070017001505f700b30313192cffcd7af143e5c4cd3733000001003101005c4937091206050a0800200015058600b403130debffcda88743e5c4eb2c3e000001003101005c493707030601080600290015058600b377"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java
new file mode 100644
index 000000000..c5829d588
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class At2000FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ At2000FrameDecoder decoder = new At2000FrameDecoder();
+
+ verifyFrame(
+ binary("01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"),
+ decoder.decode(null, null, binary("01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8")));
+
+ verifyFrame(
+ binary("893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e"),
+ decoder.decode(null, null, binary("893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java
new file mode 100644
index 000000000..9e2f180d2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java
@@ -0,0 +1,59 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assume.assumeTrue;
+
+public class At2000ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ assumeTrue(Boolean.parseBoolean(System.getenv("CI")));
+
+ At2000ProtocolDecoder decoder;
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f00000000000000000000000000003335373435343037313632373539388b57ec3a6ec7e3310a1ceb0a70fd751b8f2e7be6df1d6dcd80129f66fff0ea1c"));
+
+ verifyNull(decoder, binary(
+ "89000000000000000000000000000000"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f0000000000000000000000000000333537343534303731363036313936ddf189075add9a32d97b54073025963e65849a3a59940d05fd8db655fc84bc6d"));
+
+ verifyPositions(decoder, binary(
+ "893b01000000000000000000000000003b40bcdab6387d829146701d8cb53daaa87b84d24b40cb24fd86da5d3f5f02b0f6f603c43c5a513418a0b2bdcaba603dbe737687cfe9082c57668eb6789d2b029a35aeac6a609558b96de5d7ad37917c902efc878ca9aff474f9d5d2417191285b8d5749bd3ffa86cc99096ce24c1f6ac350ae9adf3d5c788f80b4e3d3dc2dbb8abc1414ea1b52fdb55b2bb8af223ec528245f99d451b715e5774c5397db645d9ae441e645f8dae70230b728e81f51240868712d6f426fd694dbad8026fcf487c268939f04593ad86391cc829b1a1bdac8804ff7507544a69dc0b1b3927d7344e8a5b26fa56825283b3e476330b36d15011e1647ebd9f2ef71844ed32c0dc050457bfbd79160e6d1d8cda00a0927c8957631770e98eb20735aa46b0b18502baf4c45d2623ee51a4320cf3018010e7bbf8bc0dd79eb28e88b727ea67e980b8a91"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f000000000000000000000000000033353734353430373136303631393637f5441a9862260117858237fe3160388490f0df7d46c09112ee087235a92101"));
+
+ verifyPositions(decoder, binary(
+ "89043203000000000000000000000000d01ff1df1b56ba9185bb741ddc253f3cee093b1f8193100cd95777b5288a6f29d1b343a952f882ce8825679f7e27ad88ed7898bff92f716acadfc3c17fa8c1a6b9d0934e8f042433a183776c06c1acd73efb4b9f19030debb4dceb161fb3e6630757d25c3e995b7cf5f446318dcc1677eb215d1af49f11cd7300598bcdc40cc25466ed2391d836c782e44bc04a332e902b2b34f5597a542af4ca670cdfc18d87ce2a225c3e6f2f32359d4914c6df09aa5ee306c229260d4a56da53f93398bc8a6e77095305ee214cf605de20d3876a993fb810486f75bcd514c12442bf4dc3fbe7963b20d5100b5ecff1c1aef4c4b3736a04e245d50f538327db21d55270b279db5ac5a9658876bae3d9b5026b8975bb2bf4d100b8492760d66ae31f27bf9c525c2d794860eabca9c788b91152dbce79f336daaf6a7a9547bf1dd8e3334c891f4548fd6d112ebf45125c2a8abd3a786ebbcfdd03101b524bbf465f14a9a424305ce7de56ffca85b4657fc8c03e4349c0ca6be64d1cf595ee91f8173678ef2267dae54dd00028450c48d9b74c925af0f245d409d8773238dce5832747587f53a12155869c1d464eb0630f94cf8dceb76aa39995411d4ce7743b1501692425afec498535526067e79f568b7f71ee47d8b4929118d57b13d56cdbfd26582d579dee"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f0000000000000000000000000000333537343534303731363035353033dd529a1eb5df9f3b6d320b38250e03306692957e8c2127d8e381a717f639b4c9"));
+
+ verifyPositions(decoder, binary(
+ "89898701000000000000000000000000ae99e38f13d44f536769eb4930a6826dfebe5b98a6048941e89b17c9cdcb276be4af7c0d188d07c90d6e94aa9efcb465fe7aeaff4d85caf837483b4e9b32fbbacfbc4e175eebf57a27f552a64fc3419565d2dfbea668511a08d5a526342fad0e93b20c4449ad8defab4a9ac68cf7dad86971eb2cd96810d9d6a9c56e07fd90e4c28cfc53a069b63efe37a0523a69b607a2dc011ba17b177c5332c04be1faeeabed24539b3b790fa8a8610ab3633e0140ed79690fcae9dea43c7daad780d95a511d8f4875e621bcfe7516a03b80eb3c473ffd4bc1eda298dfa7d994a2cfeaa5d24c190d52d72fd90975a2e6f9ed3b95017133952262f91787c46839738a80c333dc53ee4d8afe75315d801efe17bc7309f30cfce64906bf70e6844c835781cbb64b49e9315ca3c2cd39d00a03cc7178a4ebc5df230dcdfd44ec588791d488f96bb6ff4007a753f552bda4d1766632aa3ec5eb38feb23ed6efb8f382a7f22b70adc9cb533c09bf749190c36d63b572c1acfc3a59138d51273835ab13c4689df01e3d2c2dd1829e00aac5c56b5d51e60d6731833f82c7464d88df663ca28a20eedeecb60f3704ae78281838caa116184e414db459768321bbfa1e83ad59fe168eb81f3b41cfe0e39c8aa78cbbe5825620bf053a1cb62e04d4cdf17ca2dc9305d47c"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"));
+
+ verifyPositions(decoder, binary(
+ "893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
new file mode 100644
index 000000000..3a26bb7a7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AtrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AtrackFrameDecoder decoder = new AtrackFrameDecoder();
+
+ verifyFrame(
+ binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"),
+ decoder.decode(null, null, binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a")));
+
+ verifyFrame(
+ binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a"),
+ decoder.decode(null, null, binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a")));
+
+ verifyFrame(
+ binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a"),
+ decoder.decode(null, null, binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a")));
+
+ verifyFrame(
+ binary("244F4B0D0A"),
+ decoder.decode(null, null, binary("244F4B0D0A")));
+
+ verifyFrame(
+ binary("fe0200014104d8f196820001"),
+ decoder.decode(null, null, binary("fe0200014104d8f196820001")));
+
+ verifyFrame(
+ binary("40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000"),
+ decoder.decode(null, null, binary("40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
new file mode 100644
index 000000000..3a9382086
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
@@ -0,0 +1,99 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AtrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null);
+
+ verifyPositions(decoder, buffer(
+ "@P,3A34,146,41431,353816057242284,20180622015809,20180622015809,20180622015809,9720689,4014230,61,2,0,20,1,0,0,0,0,2000,2000,12160,42,624,002,20009,20014,\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,1126,121,104547,358901048091554,20180412143513,20180412143514,20180413060000,16423389,48178700,108,2,6.5,9,0,0,0,0,0,2000,2000,\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,434E,124,104655,358901048091554,20180412143706,20180412143706,20180413060107,16423389,48178700,108,121,6.5,10,0,0,0,0,0,2000,2000,\r\n"));
+
+ verifyPositions(decoder, binary(
+ "4050b5ed004a2523000310c83713f8c05a88b43e5a88b43f5a88b43f021e0ad5fffdc0a800f3020003059100080000000000000007d007d046554c533a463d3230393120743d3137204e3d3039303100"));
+
+ verifyAttributes(decoder, buffer(
+ "$INFO=358683066267395,AX7,Rev.0.61 Build.1624,358683066267395,466924131626767,89886920041316267670,144,0,9,1,12,1,0\r\n"));
+
+ decoder.setLongDate(true);
+
+ verifyPositions(decoder, binary(
+ "0203b494003c00eb00014104d8dd3a3e07de011b0b1f0307de011b0b1f0307de011b0b1f0300307f28030574d30000020000000600160100020000000007d007d000"));
+
+ decoder.setLongDate(false);
+
+ decoder.setCustom(true);
+
+ verifyPositions(decoder, buffer(
+ "@P,9493,402,143,356961075931165,1546830150,1546830151,1546830151,-88429209,44271154,54,10,0,10,1,0,0,0,1858AE010000,2000,2000,\u001A,%CI%FL%ML%VN%PD%FC%EL%ET%AT%MF%MV%BV%DT%GN%GV%ME%RL%RP%SA%SM%TR%IA%MP,0,0,2T1KR32E28C706185,0,1,0,7,251,89,118,41,0,00A5001A040800A5001A040B00A5001A040C00A5001A040900A4001C040D00A50019040900A60019040900A4001B040B00A5001A040900A7001A040E\u001A,008CFE7C03C4\u001A,356961075931165,0,0,12,0,18,5,0\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,FD34,720,12256,357520076794151,1535445349,1535445354,1535500603,106784149,-6283086,105,2,138,0,3,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,0,0,0,0,0,0,0,1011677,0,138,0,0,0\r\n",
+ "1535445376,1535445374,1535500603,106783763,-6282981,105,101,138,6,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,141,41,60,12,0,0,7,1011677,0,138,0,0,0\r\n",
+ "1535445380,1535445378,1535500603,106783763,-6282981,105,103,138,6,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,135,41,61,12,0,0,9,1011677,0,138,0,0,0\r\n",
+ "1535445415,1535445415,1535500603,106783763,-6282981,105,2,138,7,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,135,41,61,12,0,21,10,1011677,0,138,0,0,0\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,0EBB,687,1917,357298070426498,1534718280,1534718279,1534739774,-88643875,44210148,270,2,57128,6,1,130,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718283,1534718283,1534739774,-88645243,44210145,269,2,57129,6,1,129,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718286,1534718285,1534739774,-88646581,44210136,269,2,57130,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718289,1534718288,1534739774,-88647911,44210123,269,2,57131,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718292,1534718291,1534739774,-88649229,44210111,269,2,57132,6,1,124,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,136,16\r\n"));
+
+ verifyPositions(decoder, binary(
+ "4050b63b02c401af000144a77a21281d5b79d8ef5b79d8ef5b79d8f0fab84831029f35580056020003144d00080100130000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000053005f3839303132363038383132313532343737353900000000fd078e0085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383331322c2d38382e35383631383920000013ff4e04190023ff13041100a3ffdc0402009affde03fc00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00131b2b005b79d8f05b79d8f05b79d8f0fab8488e029f356f0043020003144d00080100170000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000052005f3839303132363038383132313532343737353900000000fd09190085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383333352c2d38382e35383630393820000013ff4e04190023ff1304110017ff0c0424000cff30041a00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00171c2b00"));
+
+ verifyPositions(decoder, binary(
+ "405ad77c01670410000144a77a21281d5b74d2335b74d2335b74d233fabaf3bc02a38d3d010c0200030f8e000701001a0000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41c0900003254314b523332453238433730363138350000000000004800543839303132363038383132313532343737353900000000ec06a50089002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237323935372c2d38382e34313132303120000075ff4903fb006fff63040a004dff5d04080030ffa10407003b001304060026000503f9001e002504020078ff6204000073ff7d03f9007aff6903f3ffc0001804040000000144a77a21281d00073f0c001a182400"));
+
+ verifyPositions(decoder, buffer(
+ "@P,6254,235,989,356961075931165,1534381563,1534381564,1534381564,-88429188,44271225,70,2,200563,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,18,9,0,0,2T1KR32E28C706185,0,0,0,54,8901260881215247759,252,489,123"));
+
+ verifyPositions(decoder, buffer(
+ "@P,27A6,663,707,356961075931165,1534211298,1534211297,1534211437,-88429190,44271135,288,2,200235,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,9,0,0,2T1KR32E28C706185,0,0,0,80,8901260881215247759,251,59,124\r\n",
+ "1534211353,1534211357,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,79,8901260881215247759,251,60,124\r\n",
+ "1534211417,1534211417,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,78,8901260881215247759,251,56,124\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,CA4B,122,1,358683064932578,1533633976,1533633975,1533633975,121562641,25082649,72,0,1638,15,0,0,1,0,,2000,2000,,%CI%MV%BV,143,0\r\n"));
+
+ verifyPositions(decoder, binary(
+ "405025e30096eb730001452efaf6a7d6562fe4f8562fe4f7562fe52c02a006d902273f810064650000e0f5000a0100000000000007d007d000254349255341254d5625425625475125415400090083002a1a000001a8562fe4f8562fe4f7562fe52c02a006d902273f810064020000e0f5000a0100000000000007d007d000254349255341254d5625425625475125415400090083002a1a000001a8"));
+
+ decoder.setCustom(false);
+
+ verifyNull(decoder, binary(
+ "fe0200014104d8f196820001"));
+
+ verifyPositions(decoder, binary(
+ "40503835003300070001441c3d8ed1c400000000000000c9000000c900000000000000000000020000000003de0100000000000007d007d000"),
+ position("1970-01-01 00:00:00.000", true, 0.00000, 0.00000));
+
+ verifyPositions(decoder, binary(
+ "4050993f005c000200014104d8f19682525666c252568c3c52568c63ffc8338402698885000002000009cf03de0100000000000007d007d000525666c252568c5a52568c63ffc8338402698885000002000009cf03de0100000000000007d007d000"));
+
+ verifyPositions(decoder, binary(
+ "40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000"));
+
+ verifyPositions(decoder, binary(
+ "40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000000000000000"));
+
+ verifyAttributes(decoder, buffer(
+ "$OK\r\n"));
+
+ verifyAttributes(decoder, buffer(
+ "$ERROR=101\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java
new file mode 100644
index 000000000..aeea48967
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AuroProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AuroProtocolDecoder decoder = new AuroProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "M0028T0000816398975I357325031465123E00001W*****110620150437000068DA#RD01DA240000000001+100408425+013756121100620152137231112240330004400"));
+
+ verifyPosition(decoder, text(
+ "M0029T0000816398975I357325031465123E00001W*****110620150439000068DA#RD01DA240000000001+100407886+013755936100620152138221952123100003400"));
+
+ verifyPosition(decoder, text(
+ "M0030T0000816398975I357325031465123E00001W*****110620150441000068DA#RD01DA240000000000+100408391+013756125100620152140102362238320034400"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java
new file mode 100644
index 000000000..fa8f3a071
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AustinNbProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AustinNbProtocolDecoder decoder = new AustinNbProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 16:31:01;52,1133308410645;21,1000003814697;310;120;2292;1;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 16:55:00;52,1636123657227;21,0827789306641;0;90;4000;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 17:32:01;52,1711120605469;21,0866680145264;70;90;1199;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 16:31:01;52,1133308410645;21,1000003814697;310;360;2292;1;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 16:55:00;52,1636123657227;21,0827789306641;0;360;4000;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 17:32:01;52,1711120605469;21,0866680145264;70;360;1199;0;ORANGE"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java
new file mode 100644
index 000000000..9e17b437f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class AutoFonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoFonProtocolDecoder decoder = new AutoFonProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "10556103592310314825728F"));
+
+ verifyPosition(decoder, binary(
+ "02080000251848470afa010262daa690013aa4046da83745f8812560df010001126a"));
+
+ verifyPosition(decoder, binary(
+ "111E00000000000000000100007101010B0C020302010B0C0005A053FFFFFFFF02010B0C00276047FFFFFFFF1F5600FA000176F218C7850C0B0B0C203A033DBD46035783EF009E00320014FFFF45"));
+
+ //verifyPosition(decoder, binary(
+ // "12060000007501010B0C00089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF02007601010B0C00269CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF4A007601010B0C01089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF04007501010B0C01269CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF80007601010B0C02089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFFA6007501010B0C02231F5600FA000176F218C70000000000000000000000000000000000000003E7FFFF9629"));
+
+ verifyNull(decoder, binary(
+ "41035151305289931441139602662095148807"));
+
+ verifyNull(decoder, binary(
+ "41032125656985547543619173484002123481"));
+
+ verifyPosition(decoder, binary(
+ "023E00001E004D411EFA01772F185285009C48041F1E366C2961380F26B10B00911C"),
+ position("2010-01-27 04:00:08.000", true, 54.73838, 56.10343));
+
+ verifyPosition(decoder, binary(
+ "023E00001E004D411EFA01772F185285009C48041F1E366C2961380F26B10B00911C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java
new file mode 100644
index 000000000..f71fcd8eb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AutoGradeProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoGradeProtocolDecoder decoder = new AutoGradeProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(000000001637868324027912356171116A2250.7611N07556.9425E000.9024427197.36\u008eA0000B0000C0000D0000E0000K0000L0000M0000N0000O0000)"));
+
+ verifyPosition(decoder, text(
+ "(000000007322865733022629240170415A1001.1971N07618.1375E0.000145312128.59?A0024B0024C0000D0000E0000K0000L0000M0000N0000O0000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..5daafcc40
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AutoTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoTrackProtocolDecoder decoder = new AutoTrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "f1f1f1f1330c00201007090006de7200000000daa3"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java
new file mode 100644
index 000000000..d3be0b6d6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AvemaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AvemaProtocolDecoder decoder = new AvemaProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "8,20180927150956,19.154864,49.124862,7,56,0,12,3,0.0,0,0.02,14.01,0,0,26,0,219-2,65534,10255884,0.01"));
+
+ verifyPosition(decoder, text(
+ "1130048939,20120224000129,121.447487,25.168025,0,0,0,0,3,0.0,1,0.02V,14.88V,0,1,24,4,46608,F8BC,F9AD,CID0000028"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java
new file mode 100644
index 000000000..0972e3fd4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class Avl301ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Avl301ProtocolDecoder decoder = new Avl301ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "244c0f086058500087335500010d0a"));
+
+ verifyNull(decoder, binary(
+ "24480d1001c3065c0d00010d0a"));
+
+ verifyPosition(decoder, binary(
+ "24242c0f041710001d0e060146944904ff4ac40000148f0651044b001a081001be06590daa00000108a30d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
new file mode 100644
index 000000000..400ba7e12
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BceProtocolDecoder decoder = new BceProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "3ab90b71bc1503000300c10bff11"));
+
+ verifyPositions(decoder, binary(
+ "789622d1cb1303003401a53365b70f4a9babc0ffd700c04400f0b6c741e63933428f1c431c015468de43f18221341b007e0ae20001430a698f003f008d000000000031f85900000000f0831c018400000000000000000000000000000000000209000000000000000000000000000000030065f70f4a9babc0ffd700c0440069bcc741e73733427f1c431a01a9378343f1829c391b00a80be2000170056da7003e007c000000000031c04e00000000f0831c01810000000000000000000000000000000000060100000000000000000000000000000003006537104a9babc0ffd700c0440051c1c74129363342721c421801e4809543f18210341b00710ae2000170056da7003e0072000000000031c04800000000b8841c017e00000000000000000000000000000000000306000000000000000000000000000000030069"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004200a0003fd769c568ffc3db0079161d420683a9414918b1150000000000d102660167040000000000009f06357f0000a401042ea415e10232000000000000000000000051"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004200a0003ff76cc568ffc3db00bd151d423c8ca9410a18af150000000000d1023a0160040000000000009f06427f0000a401042ea416e1003e00000000000000000000009a"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004202a5863f57f8b868ffc3db0001712642b70b9d41221946200246d23342d1023e016404000000000000a0065a7f0000a4010496f277e3064300000000000000000000003f97f8b868ffc3db0074712642ae0a9d412919452102fff19042d102a4026304000000000000a006487f0000a4010496f277e3064300000000000000000000003fb7f8b868ffc3db00c6712642000a9d413019442002a6074542d102300165040000000000009f064f7f0000a4010496f277e3064300000000000000000000003fd7f8b868ffc3db002872264245099d413518421f02bea35e42d1021e0164040000000000009f06377f0000a4010496f277e3064300000000000000000000003fe7f8b868ffc3db0061722642e3089d413a28421f02a05ff641d102580163040000000000009f06577f0000a4010496f277e3064300000000000000000000003f17f9b868ffc3db0021732642a3079d414119411d02d69fcc42d102440165040000000000009f06437f0000a4010496f277e3064300000000000000000000003f37f9b868ffc3db00ae732642b4069d414628421b02e0629742d1024c0167040000000000009f06557f0000a4010496f277e3064300000000000000000000003f57f9b868ffc3db0044742642ae059d414c28421a027540a342d102860163040000000000009f065b7f0000a4010496f277e3064300000000000000000000003f97f9b868ffc3db007275264256039d4153284417029e1f2f43d1024a016704000000000000a0064e7f0000a4010496f277e306430000000000000000000000db"));
+
+ verifyPositions(decoder, binary(
+ "2d41abfa2e4501004e02a5a0068609f96a009106260af96a00a006260af96a009106960af96a00a306a60af96a008f06b60af96a009106960cf96a00a03e0715f96affc300804000e6a23a4230ccc441001f47850200000000a0000000bd6542651a110d004b1000000000a401045a56bf4d02480000000000000000061623f96a00a0062623f96a00913ea728f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000bd6542651a110d004a1000000000a401045a56bf4d02480000000000000000069639f96a00a006a639f96a00913e373cf96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d024800000000000000003ed74ff96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a111b004a1000000000a401045a56bf4d01480000000000000000061650f96a00a0062650f96a00913e6763f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d01480000000000000000069666f96a00a006a666f96a00913e0777f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d0148000000000000000006067df96a00a006167df96a0091063687f96a00a3064687f96a008f065687f96a0091063689f96a00a03e978af96affc300804000e6a23a4230ccc441001f87850200000000a0000000ad6527651a110d004a1000000000a401045a56bf4d024800000000000000000e"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601003302a5e8327764726bff432fc52a420e2c93410028afd2070000000080024a0005040000000000008e06547f0000a401043cf21f390e54328764726bff432fc52a420e2c93410028afd2070000000080024c0005040000000000008e064f7f0000a401043cf21f390e54329764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008d064f7f0000a401043cf21f390e5432a764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06587f0000a401043cf21f390e5432b764726bff432fc52a420e2c93410028afd207000000008002460005040000000000008e06557f0000a401043cf21f390e5432c764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06347f0000a401043cf21f390e5432d764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008e06547f0000a401043cf21f390e5432e764726bff432fc52a420e2c93410028afd207000000008002540002040000000000008e06477f0000a401043cf21f390e5432f764726bff432fc52a420e2c93410028afd207000000008002540004040000000000008d064f7f0000a401043cf21f390e54320765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e064d7f0000a401043cf21f390e54321765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e06467f0000a401043cf21f390e544200a0003f3743c96bffc3db0060c81c42d885ab41002aaf060000000000d102380167040000000000008a064f7f0000a4010412a46b330033000000000000000000000025"));
+
+ verifyPositions(decoder, binary(
+ "ca07629c834601002702a58f3c278ff96a0bc000a0c00140bc3a42508bc541002a70a905000000009000c101a40103d904440e003000000000000000000000000000000000000001013c878ff96a0bc000a0c00140bc3a42508bc541002970a905000000009000c301a40103d904440e003000000000000000000000000000000000000001013cb7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c401a40103d904440e003500000000000000000000000000000000000001013cc7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c301a40103d904440e003500000000000000000000000000000000000001013cd7f2f96a0bc000a0c00114bc3a42a48fc5410029027e03000000009000c301a40103d904440e003000000000000000000000000000000000000001013c670dfa6a0bc000a0c001f1bb3a42418dc541002a484904000000009000c001a40103d904440e003a00000000000000000000000000000000000001013c770dfa6a0bc000a0c001f1bb3a42418dc5410028484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c470efa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c5711fa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000c101a40103d904440e003000000000000000000000000000000000000001013f00a0003cc795866b0bc000a0c00144bc3a423a90c541003697cb03000000008000cf01a40103d9040d0f0030000000000000000000000000000000000000010100"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java
new file mode 100644
index 000000000..bdcc1f9e8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class BceProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ BceProtocolEncoder encoder = new BceProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_DATA, "1");
+
+ verifyCommand(encoder, command, binary("79df0d86487000000600410aff00550048"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java
new file mode 100644
index 000000000..9a24ece1f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BlackKiteProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BlackKiteProtocolDecoder decoder = new BlackKiteProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01150003313131313131313131313131313131209836055605BA"));
+
+ verifyPositions(decoder, binary(
+ "0136000331313131313131313131313131313120523905563000010000000100000033000000003400004000004500004600005000005100009F76"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
new file mode 100644
index 000000000..d661a10f2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
@@ -0,0 +1,67 @@
+package org.traccar.protocol;
+
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BoxProtocolDecoder decoder = new BoxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "H,BT,358281002435893,081028142432,F5813D19,6D6E6DC2"));
+
+ verifyNull(decoder, text(
+ "H,BT,N878123,080415081234,D63E6DD9,6D6E6DC2,8944100300825505377"));
+
+ verifyPosition(decoder, text(
+ "L,190227043304,G,25.68773,48.59788,71,53,261.42,1,23;A,0.03;D,0.06;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,081028142429,G,52.51084,-1.70849,0,170,0,1,0"));
+
+ verifyPosition(decoder, text(
+ "L,081028142432,G,52.51081,-1.70849,0,203,0,16,0"));
+
+ verifyNull(decoder, text(
+ "L,080528112501,AI1,145.56"));
+
+ verifyNull(decoder, text(
+ "E,1"));
+
+ verifyPosition(decoder, text(
+ "L,150728150130,G,24.68312,46.67526,0,140,0,3,20;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155815,G,24.68311,46.67528,0,140,0,6,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155833,G,24.68311,46.67528,11,140,0,52,23;A,0.79;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155934,G,24.68396,46.67489,0,282,0.12,1,21;A,1.27;D,1.23;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160033,G,24.68414,46.67485,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160133,G,24.68388,46.675,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160233,G,24.68377,46.67501,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160333,G,24.684,46.67488,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155855,G,24.68413,46.67482,0,282,0.14,53,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160400,G,24.68413,46.67482,0,282,0.14,7,20;A,0;D,0;I,0;END,25,326,150728155814"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java
new file mode 100644
index 000000000..9f841fb8c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class C2stekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ C2stekProtocolDecoder decoder = new C2stekProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "PA$863083038046613$D#181123#162850#1#+37.92684#+23.75933#0.62#200.1#0.0#3768#000#9#00#sz-w1001#B0907839$AP"));
+
+ verifyPosition(decoder, text(
+ "PA$353990030327618$D#091117#020928#1#22.537222#114.020948#0.00#0.0#42.1#4183#011#1#101#Wsz-wl001#B0101940#C+3.0,-5.0,+2.0$AP"));
+
+ verifyNull(decoder, text(
+ "PA$863083030602124$20$AP"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java
new file mode 100644
index 000000000..5ed3f5dc8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CalAmpProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CalAmpProtocolDecoder decoder = new CalAmpProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "83051633033459010101028afd59ae7c1459ae7c140b06bbce2c01520e0000d916000001b900450900005affa50f091f00260d040000000f24000001b90000000000003714"));
+
+ verifyPosition(decoder, binary(
+ "83092701131797081078220107010200dc583d4d3f583d4d3f19c70502cd1d512d00005f180000008500ec0800101eff980f090100313102000000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "8305133303910501010102004557E5AB2457E3B3E01FD828DBFE9E3465000028C90000004201310704001EFFA12F0B22081BCA05000000000000000F87000E8E2F00EA029E0000082D"));
+
+ verifyPosition(decoder, binary(
+ "8305313301481601010102045557ea2eba57ea2eba1ebf06db005f2e5e0000220c00000000000006200000ff8f000a00000bca06000000000000000f1b000f35ef00ea02900000087000000060"));
+
+ verifyPosition(decoder, binary(
+ "8305454205067001010102008157a9bddc57a96aaa17cdb98fccc1a457000056ac00000000000007250000ff8f000e00082711570000000000ffff101b00003148000010680000000000000000000000050000000000000000000005b0000000000000289600000000000000000000069b00000000000008e400000000000000000000000000000003000000010000070f"));
+
+ verifyPosition(decoder, binary(
+ "83052132052924010101020001575c590300000000000000000000000000000000000000000000002c0000ff8f0000030801010000"));
+
+ verifyPosition(decoder, binary(
+ "830543321494860101010a0080560b5a5e0eadd0291becf3c500f005090f1f3305000003010040c0a600000000000000008b12a102"));
+
+ verifyPosition(decoder, binary(
+ "830543321494860101010a0c215608b6680ead5ada1bed88d300000049801f000500000300003cf33200000000000000008b0ce101"));
+
+ verifyNull(decoder, binary(
+ "830545321041830101010300010000333862000023c301000000004532104183ffffff353816051610691f420040163953294fffffffffffffffff8996604211639032949f4f54413a317c303b302c317c343b302c34004f5441535441543a302c302c302c302c302c222200564255533a342c322e302e302c343533323130343138332c5630312e30332e30312e34302c5630312e30332e30312e33312c2c0056494e2d494e464f3a56494e3d31464d5a5537324539355a4137303032362c4445562d5245474e3d55532c535256522d5245474e3d555300"));
+
+ verifyPosition(decoder, binary(
+ "8308353301059723580f01020102088952d994c352d994c4134fa767c4c482e20000c12700000d29006e1002019affc90f061d00060c0000"));
+
+ verifyPosition(decoder, binary(
+ "8308355233050116134f01020102445652d993e152d993e1124c728cc68f0647000023c00000000000000e02019affc90f071c0015020000"));
+
+ verifyPosition(decoder, binary(
+ "830545420185450101010200075517fb335516c5c40fb1aea4cf4cbf250000000000000000008900260015ffb10f001108110a0000"));
+
+ verifyPosition(decoder, binary(
+ "830543321494750101010A00085492798A0EC4F9E71BDA3B81005600040F1F33050000030000000076000000000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..c8db07ee3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CarTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CarTrackProtocolDecoder decoder = new CarTrackProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$$020040????????&A0000"));
+
+ verifyPosition(decoder, text(
+ "$$020040????????&A9955&B011939.000,A,4436.3804,N,02606.9434,E,0.00,0.00,190317,,,A*64|0.9|&C0100000000&D01830=?6&E00000001&Y00000000"));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000"),
+ position("2009-01-03 10:29:04.000", true, 22.55109, 114.08240));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000&Y00100020"));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000"));
+
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java
new file mode 100644
index 000000000..412901d90
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java
@@ -0,0 +1,46 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CarscopProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CarscopProtocolDecoder decoder = new CarscopProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "*170821223045UB00HSO"));
+
+ verifyPosition(decoder, text(
+ "*170821223121UB05ORANGE000731512061825V0000.0000N00000.0000E000.0040331309.62"));
+
+ verifyPosition(decoder, text(
+ "*170724163029UB05ORANGE000000010061825V0000.0000N00000.0000E000.0040331309.62"));
+
+ verifyNull(decoder, text(
+ "*160618233129UB00HSO"));
+
+ verifyNull(decoder, text(
+ "*160618232614UD00232614A5009.1747N01910.3829E0.000160618298.2811000000L000000"));
+
+ verifyNull(decoder, text(
+ "*160618232529UB05CW9999C00000538232529A5009.1747N01910.3829E0.000160618298.2811000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*040331141830UB05123456789012345061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"),
+ position("2004-03-31 06:18:25.000", true, 29.56689, 106.45424));
+
+ verifyPosition(decoder, text(
+ "*040331141830UB04999999984061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*040331141830UA012Hi-jack061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*150817160254UB05CC8011400042499160254A2106.8799S14910.2583E000.0150817158.3511111111L000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java
new file mode 100644
index 000000000..27f503b34
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java
@@ -0,0 +1,144 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CastelProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CastelProtocolDecoder decoder = new CastelProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "40403a00043231335750323031373030363135360000000000a00200000100000101201100344a474446364545374a4230373632363056ff0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040560004323133474c3230313630303033363400000000004002a122a05a5423a05abe0f2a000000000007f1f90014000000040001640011170003001e000505210b210c210d210f2101062b58ef02001a25950d0a"));
+
+ verifyAttributes(decoder, binary(
+ "404057000431303031313132353239393837000000000000004002C1F06952F0F169529C9111000000000069830000470000000400036401014C01030078000505210C210D210F21102101073BE8030064280AEB930D0A"));
+
+ verifyPosition(decoder, binary(
+ "40405f000536303331353030303335313200000000000000004001040212102a2f72b29302a0af8512b40787018e000000000043e4ae000000007ca0f7224d5049503632305f56312e312e30004d5049502d3632302056322e300072140d0a"));
+
+ verifyPosition(decoder, binary(
+ "24245000363137313135313243333133360000000000000040011b011207133ac49a390464514a15000000008e480c00000917000000000000ffafffaf00010000ffff7800ffffffffffffff003c0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40408200033231334c32303137303031313039000000000000100136477b5964477b590400000000000000dc410f000000000204000709207910008304011c07110e110dd41a160714a95a0f00001e058c4944442d3231334c2056312e312e3120323031372d30352d3038004944442d3231334c2056312e312e300000006da10d0a"));
+
+ verifyPositions(decoder, binary(
+ "40408200033231334c323031373030303131370000000000001001000c6759a10d67590a9e1200000000000e3e0000000000020000000e4e791c000004010d0711060515083017086cd1181f000040067d4944442d3231334c2056312e312e3120323031372d30352d3038004944442d3231334c2056312e312e3000000066e30d0a"));
+
+ verifyAttributes(decoder, binary(
+ "404043000432313345503230313630303035383500000000004006a2021d5810031d58ae940400da050000f6040000070000000400076401680000000001001bd20d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d0000432313345503230313630303035383500000000001001831c1c58b1fc1c58ae94040012220000f60400005800000000000763016800008484004944445f3231335730315f532056312e302e37004944445f3231335730315f482056312e302e370032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f0121022101260127012861780d0a"));
+
+ verifyNull(decoder, binary(
+ "404029000432313345503230313630303035383500000000009001ffffffff0000b4fc1c582b6e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40406000043231334550323031363030303538350000000000400708000000831c1c58f4fb1c58ae94040012220000f604000058000000200007630168000084c401040b10090c3532db3f07f07f7520090100000101010e00000000c7920d0a"));
+
+ verifyNull(decoder, binary(
+ "404042000432313345503230313630303035383500000000001002831c1c58b7fc1c58ae94040012220000f604000058000000000007630168000084840072a20d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d0000432313345503230313630303035383500000000001001831c1c5805fe1c58ae94040012220000f60400005800000000000763016800008484004944445f3231335730315f532056312e302e37004944445f3231335730315f482056312e302e370032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701284eb10d0a"));
+
+ verifyAttributes(decoder, binary(
+ "40405700043231334e583230313630303131373700000000004002c458ce572159ce57a9e2020082030000500c00000f0000000400036401240e0403023c000505210c210d210f21102101075b14030121330269430d0a"));
+
+ verifyNull(decoder, binary(
+ "40407800043231334e583230313630303131373700000000004004fa52ce574b53ce57cad1020041020000050c00000d0000000400036401240b0503001b042105210c210d210f211021112113211c211f212121232124212c212d213021312133213e2141214221452149214a214c214f215021384e0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040a600043231334e583230313630303131373700000000004005fa52ce575053ce57cad102006b020000050c00000d0000000400036401240b050300001b042105210c210d210f211021112113211c211f212121232124212c212d213021312133213e2141214221452149214a214c214f215021015bd604301f500600000653000000bc0bffff78250000ff2d98642401000f8080e038000f0f0000000000000077b10d0a"));
+
+ verifyAttributes(decoder, binary(
+ "40404300043231334e583230313630303131373700000000004006fa52ce574e53ce57cad1020053020000050c00000d0000000400036401240b0503000000feec0d0a"));
+
+ verifyPosition(decoder, binary(
+ "40403600043231334e583230313630303033343600000000004009ad31a457050810061a35b29bf80ae6da83180300320bbe32580d0a40403600043231334e583230313630303033343600000000004009ad31a457050810061a35b29bf80ae6da83180300320bbe32580d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d400043535333133350000000000000000000000000000100196d499574bd899570000000000000000010000000000000000000000002410000000004944445f3231334730325f532056322e332e345f4e004944445f3231334730325f482056322e332e345f4e0032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701285b410d0a"));
+
+ verifyPosition(decoder, binary(
+ "24243f00676e6768656636313031313132393030313734002001840d0000d2deb556020602100b35360456cf09e6ebac0200000000030000000001abc10d0a"));
+
+ verifyPosition(decoder, binary(
+ "24243f00676e6768656636313031313132393030313734002001840d000000dfb556020602100b36298256cf0956ebac020000990c7f0000000001b4830d0a"));
+
+ verifyPositions(decoder, binary(
+ "4040590004313030303030303030303800000000000000000040010072f53f56c25240560000000078b00900000000009c3100000000030100011900030001090b0f080106c04fe40b4037310c0060e001ff018d01e05e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40405900043231334e5832303135303030303336000000000040010073dd735600df7356b9220000270b000000000000000000000400000000240e03000201120c0f0a19050c1e5808ca35530dd902540d9c010000e5300d0a"));
+
+ verifyPositions(decoder, binary(
+ "404055000431303031313132353239393837000000000000001002C1F0695230086A529C911100000000000F890000A60500000000036301014CFF000001190A0D0539191480D60488C5721800000000BF8A640D0A"));
+
+ verifyPositions(decoder, binary(
+ "40406000043130303131313235323939383700000000000000400705000000C1F0695249F469529C9111000000000069830000D80040000400036401014C04030001190A0D04201E1480D60488C5721800000000AF0101060F000F00EA1E0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "404057000431303031313132353239393837000000000000004002C1F06952F0F169529C9111000000000069830000470000000400036401014C01030078000505210C210D210F21102101073BE8030064280AEB930D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043130303131313235323939383700000000000000400101C1F06952E7F069529C9111000000000069830000070000000400036401014C00030001190A0D0412041480D60488C57218000000009F01E803ED9A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "4040B9000431303031313132353239393837000000000000004005C1F069521BF169529C9111000000000069830000130000000400036401014C0003000022032104210521062107210C210D210E210F2110211121132115211C211F21212124212E212F2130213121322133213C214221432144214521472149214A214C214D214E210100643B6232E803003E64280A3C24FE00010E010F00D5805A483C640000000000010000E02E000000066400000500000000A7710D0A"));
+
+ verifyAttributes(decoder, binary(
+ "404043000431303031313132353239393837000000000000004006C1F0695209F169529C91110000000000698300000D0000000400036401014C00030000009AF40D0A"));
+
+ verifyNull(decoder, binary(
+ "404086000431303031313132353239393837000000000000004004C1F0695200F169529C91110000000000698300000D0000000400036401014C00030022032104210521062107210C210D210E210F2110211121132115211C211F21212124212E212F2130213121322133213C214221432144214521472149214A214C214D214E219AE90D0A"));
+
+ verifyPositions(decoder, binary(
+ "40407F000431303031313132353239393837000000000000001001C1F06952FDF069529C91110000000000698300000C0000000000036401014C00030001190A0D04121A1480D60488C5721800000000AF4944445F3231364730325F532056312E322E31004944445F3231364730325F482056312E322E31000000DF640D0A"));
+
+ verifyPositions(decoder, binary(
+ "404044000c3631313135303030303935360000000000000000420600011e0a0f0b1312864fcd08c07a13030100640acf000004000a000000000000007ba083a66ad80d0a"));
+
+ verifyPosition(decoder, binary(
+ "40405c000c363131313530303030393536000000000000000040011c0a0f0e362dca53cd0860831303000000000300000000ff000000000000007ba083a650542d3639305f56312e312e320050542d3639302056312e32008a020d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040450004323132474c31313433303035303033000000000040082ca89b55a6a99b555c57000000000000c40200000b0000001400036401111f000302f5533bd653f10d0a"));
+
+ verifyNull(decoder, binary(
+ "40404d0004323132474c3131343330303530303300000000004007120000002ca89b55cba99b555c57000000000000c40200000b0000000000036401111f000102000101170000000068850d0a"));
+
+ verifyNull(decoder, binary(
+ "4040420004323132474c31313433303035303033000000000010022ca89b55cca99b555c57000000000000cf0200000b0000000000036401111f0000020013be0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040870004323132474c31313433303035303033000000000040052ca89b55e3a89b555c57000000000000c4020000040000001400036401111f0003000012042105210b210c210d210f211021112113211c2121212321242133213421422146214f212b50663603003ce9030dff060000600dffffc25865ffff9e02b43624000000003cbc0d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d00004323132474c31313433303035303033000000000010013ec09b5596c29b555c57000000000000de0200000f0000000000036401111f000000004944445f3231334730325f532056322e322e36004944445f3231334730325f482056322e322e360032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701288a690d0a"));
+
+ verifyNull(decoder, binary(
+ "40404d0004323132474c3131343330303530303300000000004007050000003ec09b5564c29b555c57000000000000de0200000f0000002000036401111f0000020001010e00000000237e0d0a"));
+
+ verifyNull(decoder, binary(
+ "40401F00043130303131313235323939383700000000000000100303320D0A"));
+
+ verifyPositions(decoder, binary(
+ "40407F000431303031313132353239393837000000000000001001C1F06952FDF069529C91110000000000698300000C0000000000036401014C00030001190A0D04121A1480D60488C5721800000000AF4944445F3231364730325F532056312E322E31004944445F3231364730325F482056312E322E31000000DF640D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043130303131313235323939383700000000000000400101C1F06952E7F069529C9111000000000069830000070000000400036401014C00030001190A0D0412041480D60488C57218000000009F01E803ED9A0D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043335343034333035303834343134330000000000400100f61a7355c11b7355710000000b00000000000000000000000400000000240e0200020106060f100b2d5a78a7076ec0fb1d00008c065f010000ac220d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java
new file mode 100644
index 000000000..bcb93a010
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CastelProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ CastelProtocolEncoder encoder = new CastelProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("40402000013132333435363738393031323334350000000000458301a94a0d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java
new file mode 100644
index 000000000..28236c87f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CautelaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CautelaProtocolDecoder decoder = new CautelaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "20,010907000000,14,02,18,16.816667,96.166667,1325,S,*2E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
new file mode 100644
index 000000000..769760fa5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CellocatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CellocatorProtocolDecoder decoder = new CellocatorProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "4d4350470041420f000402021226d8a70221d801010000000001000000000000000000000000c4d90000000ca7a741ff0096dd15a40700000000000000001619130c01e307e4"));
+
+ verifyPosition(decoder, binary(
+ "4D434750008AD01500080103011804000000460020000000005E750000000000000000000000C34300040204DA4DA30367195703E803000000000000000001030F0802E10778"));
+
+ verifyPosition(decoder, binary(
+ "4D434750008AD01500080102011804000000360060000000005E750000000000000000000000C24300040204DA4DA30367195703E80300000000000000003B020F0802E107DF"));
+
+ verifyPosition(decoder, binary(
+ "4D4347500006000000081A02021204000000210062300000006B00E100000000000000000000E5A100040206614EA303181A57034E1200000000000000001525071403D60749"));
+
+ verifyPosition(decoder, binary(
+ "4d434750000101000008011f041804000000200100000000005e750000000000000000000000548500040204da4da30367195703e80300000000000000002014151007dd07f7"));
+
+ verifyPosition(decoder, binary(
+ "4d434750005e930100080102041804000000200f20000000005e7500000000000000000000005af400040204da4da30367195703e8030000000000000000021a111e08dd0760"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
new file mode 100644
index 000000000..89850fb5f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CellocatorProtocolEncoderTest extends ProtocolTest {
+
+ @Ignore
+ @Test
+ public void testEncode() throws Exception {
+
+ CellocatorProtocolEncoder encoder = new CellocatorProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 0);
+ command.set(Command.KEY_DATA, "1");
+
+ verifyCommand(encoder, command, binary("4D434750000000000000000000000303101000000000000026"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java
new file mode 100644
index 000000000..a386b6b47
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java
@@ -0,0 +1,78 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CguardProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CguardProtocolDecoder decoder = new CguardProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "IDRO:354868050655283"));
+
+ verifyPosition(decoder, text(
+ "NV:190225 144543:55.776505:37.729000:0:10:0:0"));
+
+ verifyAttributes(decoder, text(
+ "BC:190225 144543:CSQ1:64:NSQ1:0:NSQ2:1:BAT1:100"));
+
+ verifyAttributes(decoder, text(
+ "BC:190225 142653:CSQ1:80:NSQ1:0:NSQ2:1:BAT1:3.53"));
+
+ verifyPosition(decoder, text(
+ "NV:170409 031456:56.808553:60.595476:0:NAN:0"));
+
+ verifyAttributes(decoder, text(
+ "BC:170409 031456:CSQ1:64:NSQ1:17:PWR1:0"));
+
+ verifyPosition(decoder, text(
+ "NV:161007 122043:55.812730:37.733689:3.62:NAN:244.05:143.4"));
+
+ verifyPosition(decoder, text(
+ "NV:161007 122044:55.812732:37.733670:3.97:NAN:260.95:143.9"));
+
+ verifyAttributes(decoder, text(
+ "BC:161007 122044:CSQ1:77:NSQ1:18:BAT1:100"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:48.582590:0:NAN:0:110.1"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:-148.582590:0:NAN:0:110.1"));
+
+ verifyAttributes(decoder, text(
+ "BC:160711 044023:CSQ1:48:NSQ1:7:NSQ2:1:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyAttributes(decoder, text(
+ "BC:160711 044524:CSQ1:61:NSQ1:18:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyNull(decoder, text(
+ "VERSION:3.3"));
+
+ verifyPosition(decoder, text(
+ "NV:160420 101902:55.799425:37.674033:0.94:NAN:213.59:156.6"));
+
+ verifyAttributes(decoder, text(
+ "BC:160628 081024:CSQ1:32:NSQ1:10:BAT1:100"));
+
+ verifyAttributes(decoder, text(
+ "BC:160628 081033:NSQ2:0"));
+
+ verifyPosition(decoder, text(
+ "NV:160630 151537:55.799913:37.674267:0.7:NAN:10.21:174.9"));
+
+ verifyAttributes(decoder, text(
+ "BC:160630 153316:BAT1:76"));
+
+ verifyAttributes(decoder, text(
+ "BC:160630 153543:NSQ2:0"));
+
+ verifyNull(decoder, text(
+ "PING"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java
new file mode 100644
index 000000000..146e13ae8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class CityeasyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CityeasyProtocolDecoder decoder = new CityeasyProtocolDecoder(null);
+
+ verifyNotNull(decoder, binary(
+ "545400853575570249020100033b3430342c34352c31303638312c31313632312c33352c31303638312c31313632322c32332c31303638312c32383938332c32332c31303638312c31313632332c32312c31303638312c32333338312c31372c31303638312c32323538332c31372c31303638312c32363434312c31330000000d352e0d0a"));
+
+ verifyNull(decoder, binary(
+ "54540019357557024902010002520704100000000bbe700d0a"));
+
+ verifyNull(decoder, binary(
+ "5454001735755702490201434a01000000000c24280d0a"));
+
+ verifyNull(decoder, binary(
+ "545400153520000000000100010000000111000D0A"));
+
+ verifyNull(decoder, binary(
+ "54540019357557024902000002520704300000000376390d0a"));
+
+ verifyPosition(decoder, binary(
+ "5454006135200000000001000332303134313131303039353430392C412C342C4E2C32322E3533373232382C452C3131342E3032323737342C302E312C312E392C35302E363B3436302C302C31303137332C343635322C34310000000B63130D0A"),
+ position("2014-11-10 09:54:09.000", true, 22.53723, 114.02277));
+
+ verifyPosition(decoder, binary(
+ "5454006135200000000001000432303134313131303039353330362C412C352C4E2C32322E3533373233352C452C3131342E3032323838312C302E322C312E362C35342E313B3436302C302C31303137332C343635322C343100000045EC620D0A"));
+
+ verifyPosition(decoder, binary(
+ "5454009035755702490200000332303135303732393033303834352c412c362c4e2c31322e3833353735362c452c37372e3638373039362c302e332c312e322c3931302e303b3430342c34352c31303638312c31313632312c34332c31303638312c31313632332c32312c31303638312c32323538332c32302c31303638312c32333338312c31380000000267370d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java
new file mode 100644
index 000000000..7c03b7d5b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CityeasyProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ CityeasyProtocolEncoder encoder = new CityeasyProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+6");
+
+ verifyCommand(encoder, command, binary("5353001100080001680000000B60820D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java
new file mode 100644
index 000000000..83722ef97
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ContinentalProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ContinentalProtocolDecoder decoder = new ContinentalProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "5356003216001eb48505025b4001e90f7f18ce0f00522200400001015b4001e9000e820100000c24000100014e0400736a7a"),
+ position("2018-07-06 23:57:29.000", true, -23.46609, -46.54497));
+
+ verifyPosition(decoder, binary(
+ "5356002A1100003039030243A68B5700FEB5AB00FD715F012700000143A68B57000E000000000C2F00000130"),
+ position("2005-12-19 10:28:39.000", true, -23.49027, -46.55138));
+
+ verifyPosition(decoder, binary(
+ "5356002a0d0010a12403025a9ea47f00feb48400fd6e63000c0000015a9ea480000e000100000c000000"));
+
+ verifyPosition(decoder, binary(
+ "5356002a0d0010a1240302581b944100febed800fd9fa30139001300581c73fa000e000000000d000001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java
new file mode 100644
index 000000000..7ffad8015
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CradlepointProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CradlepointProtocolDecoder decoder = new CradlepointProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "356526070063940,0,4337.19009,N,11612.34705,W,0.0,277.2,AT&T,,,-79,,-14.0,"));
+
+ verifyPosition(decoder, text(
+ "356526070063940,1,4337.19008,N,11612.34705,W,0.0,277.2,AT&T,,,-79,,-14.0,"));
+
+ verifyPosition(decoder, text(
+ "+14063964266,162658,4333.62404,N,11636.23469,W,0.0,,Verizon Wireless,LTE,-107,-74,-16,,100.68.169.178"));
+
+ verifyPosition(decoder, text(
+ "+12084014675,162658,4337.174385,N,11612.338373,W,0.0,,Verizon,,-71,-44,-11,,"));
+
+ verifyPosition(decoder, text(
+ "353547063544681,170515,3613.25,N,11559.14,W,0.0,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "353547060558130,170519,4337.17,N,11612.34,W,0.0,294.7,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "+12084014675,162658,4337.174385,N,11612.338373,W,0.0,,Verizon,,-71,-44,-11,,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java
new file mode 100644
index 000000000..ddb5aca18
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DishaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DishaProtocolDecoder decoder = new DishaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$A#A#864161028848856#A#053523#010216#2232.7733#N#08821.1940#E#002.75#038.1#09#00.8#1800#0#000#0000#9999#11.7#285.7#0001*"));
+
+ verifyPosition(decoder, text(
+ "$A#A#864161028848856#A#182134#090116#2232.0191#N#08821.3278#E#001.74#231.4#04#01.5#1300#0#000#0000#9999#54.4#6407.7#0000*"),
+ position("2016-01-09 18:21:34.000", true, 22.53365, 88.35546));
+
+ verifyPosition(decoder, text(
+ "$A#A#353943046615971#A#064219#281113#1836.7267#N#07347.4177#E#000.00#280.4#09#00.8#3000#2#100#0000#8888#86.5#3919.1#0000*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
new file mode 100644
index 000000000..e9d98d6d3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DmtHttpProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DmtHttpProtocolDecoder decoder = new DmtHttpProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"SerNo\":131693,\"IMEI\":\"356692063643328\",\"ICCID\":\"8944538523010771676\",\"ProdId\":33,\"FW\":\"33.4.1.27\",\"Records\":[{\"SeqNo\":125,\"Reason\":11,\"DateUTC\":\"2017-05-11 05:58:44\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14641,\"3\":2484,\"4\":26,\"5\":10868},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":0,\"13\":309,\"14\":9921,\"15\":3},\"FType\":7}]},{\"SeqNo\":128,\"Reason\":11,\"DateUTC\":\"2017-05-11 17:59:45\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14607,\"3\":2752,\"4\":26,\"5\":11062},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":1,\"13\":325,\"14\":10881,\"15\":3},\"FType\":7}]},{\"SeqNo\":130,\"Reason\":9,\"DateUTC\":\"2017-05-11 19:30:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":3,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14599,\"3\":2731,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":329,\"14\":11121,\"15\":3},\"FType\":7}]},{\"SeqNo\":131,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:32:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14403,\"3\":2783,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":330,\"14\":11181,\"15\":3},\"FType\":7}]},{\"SeqNo\":133,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:36:15\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14319,\"3\":2898,\"4\":23,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":3,\"13\":331,\"14\":11241,\"15\":3},\"FType\":7}]}]}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
new file mode 100644
index 000000000..ae5e9353c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DmtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DmtProtocolDecoder decoder = new DmtProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0255003300001b00003335333232393032373533393235310038393931353030303030303030313330343539340000000403041910780603"));
+
+ verifyPositions(decoder, false, binary(
+ "02551040000eaca40d00d2b8e562c51f9912f39a6bee00007e420091090903070100000000008b1065360000000000007fd401c4fcf2feffffffffffffffffee0000003f1b"));
+
+ verifyPositions(decoder, false, binary(
+ "02551080000eada40d00d2b8e58ac51f9912f39a6bee00007e42007e090709070000000000009010fc330000000000007fc201a0fc04ffffffffffffffffffe5000000c5d00eaea40d00d2b8e58ac51f9912f39a6bee00007e42007e09070207000000000000851008340000000000007fc201a0fc04ff0000000000000000e5000000c96d"));
+
+ verifyNull(decoder, binary(
+ "025500310038f90100333533333233303831363639373330003839363130313835303031383234383434363330002202010900000000"));
+
+ verifyNull(decoder, binary(
+ "0255220000"));
+
+ verifyPositions(decoder, false, binary(
+ "025504d80352000602000052185c0803001552185c0842ee19eaba2524682d000d060973112b0302080100000000000300060901421003e40604140007190b300000000c030000000de80100000ec90e00000f0700000052000702000069185c0803001569185c089ac019ea0ad223682300fb02047d152f03020801000000000003000609013f1003fc0604140007190b300000000c030000000de90100000ecb0e00000f0700000052000802000092185c0803001592185c0800a619eaa5e7226821009c0506880e250302080100000000000300060901411003f30604140007190b300000000c030000000dea0100000ef10e00000f07000000520009020000a9185c08030015a9185c0818ae19ea1e62226826001e05038e0e2203020801000000000003000609013f1003030704140007190b300000000c030000000deb0100000ef60e00000f0700000052000a020000c0185c08030015c0185c0893b619ea7fd321681a00640403860f1d0302080100000000000300060901401003ff0604140007190b300000000c030000000dec0100000ef80e00000f0700000052000b020000d7185c08030015d7185c08e08519eab7c921682300fd04035510270302080100000000000300060901401003ea0604140007190b300000000c030000000ded0100000efa0e00000f0700000052000c020000ee185c08030015ee185c08f61719ea61e221682c004c0503540f190302080100000000000300060901421003dd0604140007190b300000000c030000000dee0100000efc0e00000f0700000052000d02000005195c0803001505195c0836b518eac9f221683000fa0107740e2d03020801000000000003000609013f1003fe0604140007190b300000000c030000000def0100000efe0e00000f0700000052000e0200001d195c080300151d195c08d1b518ea2d6721682300980502870e1d0302080100000000000300060901411003ed0604140007190b300000000c030000000df00100000e000f00000f0700000052000f02000034195c0803001534195c086acd18ea742b2168400006020500132903020801000000000003000609013d10030d0704140007190b300000000c030000000df10100000e030f00000f070000005200100200004d195c080300154d195c08dfba18eab81721684e003000093b0e1e03020801000000000003000609013e1003130704140007190b300000000c030000000df20100000e050f00000f0700000052001102000065195c0803001565195c081db318ea871f216822000400080416250302080100000000000300060901401003060704140007190b300000000c030000000df30100000e090f00000f07000000"));
+
+ verifyPositions(decoder, false, binary(
+ "025504e9032f000d000000000000001501222700524553455420446172742033342e322e312e3920666c6167733d312057443d303f000e0000000000000015013214004e6f2041646d696e20706172616d7320666f756e64202d207573696e672064656661756c7473202b204175746f41504e37000f00000000000000090015000000000000000000000000000000000000000000020805000000000007000609012b1002400003700e37001000000000000000090015000000000000000000000000000000000000000000020801000000000007000609012b1002400003700e37001100000000000000090015000000000000000000000000000000000000000000020800000000000007000609012b1002400003700e37001200000000000000020015000000000000000000000000000000000000000000020800000000000006000609012b1002400003700e370013000000000000000f001500000000000000000000000000000000000000000002080000000000000200060901271002370003670e2e0014000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343700150000000000000017001500000000000000000000000000000000000000000002080000000000000200060901071002300003d60e2a00160000000000000015011d130054756d626c65722074726967676572656420636f735e32203c20302e0017000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343700180000000000000017001500000000000000000000000000000000000000000002080000000000000200060901071002300003f70e2a00190000000000000015011d130054756d626c65722074726967676572656420636f735e32203c203026001a000000000000001501190b0047534d3a20544350206261642053594e432063686172732e001b000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343a001c0000000000000017001500000000000000000000000000000000000000000002080000000000000200060c01c90f02300003e20f041f002a001d0000000000000015011d130054756d626c65722074726967676572656420636f735e32203c20302e001e000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343a001f0000000000000017001500000000000000000000000000000000000000000002080000000000000200060c01d80f02300003ff0f0418002a00200000000000000015011d130054756d626c65722074726967676572656420636f735e32203c2030"));
+
+ verifyNull(decoder, binary(
+ "025500310038f90100333533333233303831363639373330003839363130313435363839393333303030303835002202010900000000"));
+
+ verifyPositions(decoder, binary(
+ "0255043D003D004746000096D684020B001502D48402F043F4EC2A6909452B001F00050011230302080000000000000A00060F041D0001FE0F021E0005000003BF08"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java
new file mode 100644
index 000000000..107a2a435
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DwayProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DwayProtocolDecoder decoder = new DwayProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "AA55,36,10024,1,171025,161055,36.0294,-79.7881,201, 2.5,111,1000,0000,00000,3578,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,115,318,1,171024,195059,28.0153,-82.4761,3, 1.0,319,1000,0000,00000,4244,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,117,318,1,171025,153758,28.0152,-82.4759,19, 0.6,319,1000,0000,10000,4242,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,1,123456,1,140101,101132,22.5500,113.6770,75,70.5,320,1100,0011,1110,3950,33000,24000,12345678"));
+
+ verifyNull(decoder, text(
+ "AA55,HB"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..b26991ae7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java
@@ -0,0 +1,55 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EasyTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EasyTrackProtocolDecoder decoder = new EasyTrackProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*ET,358155100054249,HB,A,100b06,053318,803a0b51,03d507c9,0017,0000,00400000,07,100,0000,1435,63"));
+
+ verifyNull(decoder, text(
+ "*ET,358155100054249,MQ"));
+
+ verifyNull(decoder, text(
+ "*ET,358155100054249,TX,A,100b06,053230"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100054249,HB,A,100b06,053212,803a0b20,03d507a2,0054,0000,40400000,06,100,0000,1435,44"));
+
+ verifyNull(decoder, text(
+ "*ET,135790246811221,GZ,0001,0005"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,0413FA4E,0000,0000,00000000,20,4,0000,00F123"),
+ position("2010-09-13 16:28:13.000", true, 22.62689, 114.03021));
+
+ verifyNull(decoder, text(
+ "*ET,358155100048430,CC,0.0,V,100603,141817,80d77ae8,81ab1ffd,0000,6b08,40000000,19,99,0000,fa9,918"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,050915,0C2A27,00CE5954,04132263,0000,0000,01000000,20,4,0000,001254"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,0413FA4E,0000,0000,00000000,20,4,0000,00F123,100"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,8413FA4E,0000,0000,00000000,20,4,0000,00F123,100"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100003016,HB,A,0d081e,07381e,8038ee09,03d2e9be,004f,0000,40c00000,0f,100,0000,00037c,29"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100003016,HB,A,0d081e,073900,8038ee2f,03d2e9fd,0114,0000,40c00000,12,100,0000,00037c,32"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,HB,A,050915,0C2A27,00CE5954,04132263,0000,0000,01000000,20,4,0000,00F123,100,200"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
new file mode 100644
index 000000000..e3cff9525
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
@@ -0,0 +1,114 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class EelinkProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EelinkProtocolDecoder decoder = new EelinkProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "454C0027E753035254407167747167670100180002035254407167747100200205020500010432000086BD"));
+
+ verifyAttributes(decoder, binary(
+ "676707006502df5c89fde800bc3fa8030302005555045b555555057a5555550b225555550c105c55550d115555550e7e5555550f4555555510017b5555112b5555551f01ed5555208005b0012100005555407ad000004237f5555589000000498a0000aef78b00000000"));
+
+ verifyAttribute(decoder, binary(
+ "676712003400e45c5b0ade02012e03702d87064546aa24066a1086018a0000002dc1a0ffffffff0afd074d000000000000000000000000fce0"),
+ Position.PREFIX_TEMP + 2, -50.0);
+
+ verifyAttribute(decoder, binary(
+ "6767120043000e5c37387c0304e4e1b4f8194fa800160013009408012e03702d8706453c6e5b066f115f05710000001b067f8d248d240313020500000000000000000000000001cc"),
+ Position.PREFIX_TEMP + 2, 28.75);
+
+ verifyPosition(decoder, binary(
+ "676714002414B05AD43A7D03026B92B10C395499FFD7000000000701CC00002495000014203604067B"));
+
+ verifyNotNull(decoder, binary(
+ "676714004F14B0E68CAFE58AA8E68AA5E8ADA621E5B9BFE4B89CE79C81E6B7B1E59CB3E5B882E58D97E5B1B1E58CBAE696B0E8A5BFE8B7AF3138EFBC88E8B79DE5AE87E998B3E5A4A7E58EA63230E7B1B3EFBC89"));
+
+ verifyPosition(decoder, binary(
+ "676780005a000001000000004c61743a4e33312e38333935352c4c6f6e3a5738322e36313334362c436f757273653a302e30302c53706565643a302e30306b6d2f682c4461746554696d653a323031372d31322d30322031313a32393a3433"));
+
+ verifyPosition(decoder, binary(
+ "676780005E5788014C754C754C61743A4E32332E3131313734330A4C6F6E3A453131342E3430393233380A436F757273653A302E30300A53706565643A302E31374B4D2F480A446174652054696D653A323031352D30392D31332032303A32313A3230"));
+
+ verifyPosition(decoder, binary(
+ "454C0050EAE2035254407167747167671200410021590BD93803026B940D0C3952AD0021000000000501CC0001A53F0170F0AB1305890F11000000000000C2D0001C001600000000000000000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "676701000c007b03525440717505180104"));
+
+ verifyPosition(decoder, binary(
+ "6767120048000559c1829213059a7400008e277d000c000000000800cc00080d2a000034df3cf0b429dd82cad3048910320000000000007b7320d005ba0000000019a000000000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "6767050020213b59c6aecdff41dce70b8b977d00000001fe000a36e30078fe010159c6aecd"));
+
+ verifyPosition(decoder, binary(
+ "676705002102b459ae7388fcd360d7034332b1000000028f000a4f64002eb101010159ae7388"));
+
+ verifyPosition(decoder, binary(
+ "676702001c02b259ae7387fcd360d6034332b2000000028f000a4f64002eb10101"));
+
+ verifyPosition(decoder, binary(
+ "6767050022001F59643640000000000000000000000001CC0000249500142000015964A6C0006E"));
+
+ verifyAttributes(decoder, binary(
+ "67670300040021006E"));
+
+ verifyPosition(decoder, binary(
+ "676705002200255964369D000000000000000000000001CC0000249500142000025964A71D006A"));
+
+ verifyAttributes(decoder, binary(
+ "67670300040028006A"));
+
+ verifyPosition(decoder, binary(
+ "676712002d066c592cca6803002631a60b22127700240046005c08020d000301af000da0fd12007f11ce05820000001899c0"));
+
+ verifyPosition(decoder, binary(
+ "676702002509f65868507603a1e92e03cf90fe000000019f000117ee00111e0120631145003101510000"));
+
+ verifyAttributes(decoder, binary(
+ "676712001e0092579714d60201f90001785003a301cd1a006a118504f2000000000000"));
+
+ verifyPosition(decoder, binary(
+ "676712003400505784cc0b130246479b07d05a06001800000000070195039f046100002cc52e6466b391604a4900890e7c00000000000006ca"));
+
+ verifyPosition(decoder, binary(
+ "676714002b00515784cc24130246479b07d05a06001800010000060195039f046100002cc52f6466b391604a49020089"));
+
+ verifyNull(decoder, binary(
+ "676701000c002603541880486128290120"));
+
+ verifyPosition(decoder, binary(
+ "676704001c01a4569ff2dd0517a0f7020b0d9a06011000d8001e005b0004450183"));
+
+ verifyPosition(decoder, binary(
+ "676705002200ba569fc3520517a0d8020b0f740f007100d8001e005b0004460101569fd162001f"));
+
+ verifyPosition(decoder, binary(
+ "676702002500bb569fc3610517a091020b116000001900d8001e005b00044601001f1170003200000000"));
+
+ verifyPosition(decoder, binary(
+ "676704001c00b7569fc3020517a2d7020b08e100000000d8001e005b0004460004"));
+
+ verifyNull(decoder, binary(
+ "676701000b001b035418804661834901"));
+
+ verifyAttributes(decoder, binary(
+ "6767030004001A0001"));
+
+ verifyAttributes(decoder, binary(
+ "6767070088001050E2281400FFFFFFFF02334455660333445566043344556605AA00000007334455660A334455660B334455660C4E2000000DAA0000000E334455660F3344556610AAAA000011334455661C334455661F334455662133445566423344556646334455664D334455665C334455665E33445566880000000089000000008A000000008B00000000"));
+
+ verifyPosition(decoder, binary(
+ "676702001b03c5538086df0190c1790b3482df0f0157020800013beb00342401"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
new file mode 100644
index 000000000..e4502f919
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class EelinkProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(new EelinkProtocolEncoder(false), command, binary("676780000f0000010000000052454c41592c3123"));
+
+ verifyCommand(new EelinkProtocolEncoder(true), command, binary("454c001eb41a0123456789012345676780000f0000010000000052454c41592c3123"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java
new file mode 100644
index 000000000..237c849c5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EgtsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EgtsFrameDecoder decoder = new EgtsFrameDecoder();
+
+ verifyFrame(
+ binary("0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2"),
+ decoder.decode(null, null, binary("0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java
new file mode 100644
index 000000000..2210893e7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EgtsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EgtsProtocolDecoder decoder = new EgtsProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0100010b002200c06401f21700c1640171360d00010101140071360d000238363539303500000000000000000047fc"));
+
+ verifyNull(decoder, binary(
+ "0100000b002400a0d601f01900030081030000000101011600030000004238363434393530333436343333373600014cdc"));
+
+ verifyPositions(decoder, binary(
+ "0100000b002700030e01211800030e8573890100845e980f0202101500f85d980fb37f50aae9653c2b193708317b00000001c51b"));
+
+ verifyPositions(decoder, binary(
+ "0100010b00a308c26401029808c3640171360d000202101800e19a7b0fcfb4c49a0bfdb87a911801b70000000010d90000180400021c0000120300000000101800f39a7b0f2fc9c39a9bf2b87a914001b50000000010da0000180400021c0000120300000000101800fa9a7b0fc663c39a21eeb87a914001b60000000010da0000180400021c0000120300000000101800069b7b0f56d8c29a26ebb87a919600ab0000000010da0000180400021c00001203000000001018000a9b7b0fb2c5c29a19f4b87a915a007d0000000010da0000180400021c0000120300000000101800089b7b0f68ccc29a21eeb87a9164008f0000000010da0000180400021c0000120300000000101800079b7b0fa0d1c29aa4ecb87a918200980000000010da0000180400021c00001203000000001018000b9b7b0f34c4c29ad3f7b87a915a00670000000010da0000180400021c00001203000000001018000f9b7b0f3dbec29aaf0fb97a91c8005e0000000010dc0000180400021c0000120300000000101800199b7b0f42bbc29a0855b97a9178006b0000000010db0000180400021c00001203000000001018001b9b7b0fc8b6c29a3c5db97a916e007e0000000010db0000180400021c00001203000000001018001a9b7b0fc4b9c29a8159b97a916e00750000000010db0000180400021c00001203000000001018001d9b7b0f94aec29a3363b97a916400930000000010db0000180400021c00001203000000001018001c9b7b0fcdb3c29a3760b97a916e008a0000000010db0000180400021c0000120300000000101800209b7b0f28a1c29af263b97a918200ba0000000010db0000180400021c00001203000000001018001f9b7b0f61a6c29af263b97a917800b30000000010db0000180400021c00001203000000001018001e9b7b0f58acc29af263b97a916400a50000000010db0000180400021c0000120300000000101800299b7b0ff26fc29ab561b97a916e00b20000000010d90000180400021c00001203000000001018002d9b7b0fd05bc29a3760b97a916e00bd0000000010d80000180400021c0000120300000000101800359b7b0f4f31c29abe5bb97a916400b50000000010d70000180400021c0000120300000000101800379b7b0f5d28c29abe5bb97a916e00b40000000010d60000180400021c0000120300000000101800369b7b0fd62cc29abe5bb97a916400bd0000000010d60000180400021c00001203000000001018003c9b7b0fca09c29a0358b97a918c00bc0000000010d50000180400021c0000120300000000101800419b7b0f38ebc19a0855b97a916e00b40000000010d60000180400021c0000120300000000101800449b7b0f4edcc19a8a53b97a916400c30000000010d60000180400021c0000120300000000101800469b7b0fded1c19acb52b97a916e00b70000000010d60000180400021c0000120300000000101800649b7b0f7a69c19a154cb97a810000bc0000000010d60000180400021c0000120300000000101800709b7b0fcc5cc19a114fb97a915000970000000010d70000180400021c0000120300000000101800729b7b0fda53c19a8a53b97a91a0007d0000000010d70000180400021c0000120300000000101800749b7b0fa24ec19a3c5db97a91a000650000000010d70000180400021c0000120300000000101800789b7b0fe74ac19aca7eb97a910e015c0000000010d80000180400021c00001203000000001018007f9b7b0f6e46c19a78e0b97a9190015c0000000010d80000180400021c0000120300000000101800869b7b0f3641c19a144eba7a9190015c0000000010d60000180400021c00001203000000001018008d9b7b0ffd3bc19a74b9ba7a9190015c0000000010d40000180400021c0000120300000000101800949b7b0fc536c19a1524bb7a9186015b0000000010d20000180400021c00001203000000001018009b9b7b0fce30c19af78dbb7a919a015c0000000010d00000180400021c0000120300000000101800a29b7b0f552cc19a93fbbb7a9172015d0000000010cd0000180400021c0000120300000000101800b09b7b0f2521c19a6ec0bc7a9186015b0000000010c90000180400021c0000120300000000101800a99b7b0f5e26c19a8759bc7a915e015b0000000010cb0000180400021c0000120300000000101800b79b7b0fa81fc19a1328bd7a9172015b0000000010c70000180400021c0000120300000000101800be9b7b0f6b1dc19ac686bd7a914a015c0000000010c60000180400021c0000120300000000101800c19b7b0fed1bc19ad2a9bd7a912201530000000010c60000180400021c0000120300000000101800c39b7b0f6223c19af4bdbd7a910401420000000010c60000180400021c0000120300000000101800c29b7b0fe91ec19a42b4bd7a910e014c0000000010c60000180400021c0000120300000000101800c59b7b0f502fc19a1acfbd7a91fa00300000000010c60000180400021c0000120300000000101800c49b7b0fdb27c19ae6c6bd7a91fa00390000000010c60000180400021c0000120300000000101800c79b7b0fb83fc19a8ad9bd7a91f000180000000010c60000180400021b0000120300000000101800c69b7b0fc536c19a52d4bd7a91f000250000000010c60000180400021b0000120300000000101800ca9b7b0fc85fc19a81dfbd7a910401030000000010c50000180400021b0000120300000000101800c89b7b0fe74ac19a86dcbd7a91fa000e0000000010c50000180400021b0000120300000000101800d29b7b0f06b7c19afbe3bd7a91a000100000000010c50000180400021c0000120300000000101800d59b7b0ff0c5c19a6beebd7a91b400410000000010c40000180400021c0000120300000000101800d49b7b0ff4c2c19af2e9bd7a91a000310000000010c40000180400021c0000120300000000101800d39b7b0f3ebcc19a79e5bd7a9196001e0000000010c40000180400021c0000120300000000101800d69b7b0f6dc7c19ae0f5bd7a91c800570000000010c40000180400021c000012030000000016b7"));
+
+ verifyPositions(decoder, binary(
+ "0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2"),
+ position("2018-03-21 05:38:19.000", true, 51.67569, 55.59189));
+
+ verifyPositions(decoder, binary(
+ "0100020B0079000000011F6A001424951CA5CB0F23B5740F020210180023B5740F0A301994DA9C524C9128000A000000100082000011040018110300120900000003150100E803001B0700010000340900001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B0700460000000000008020"));
+
+ verifyPositions(decoder, binary(
+ "0100020B00F200000001D66A001224951CA5CB0FFCB4740F0202101800FCB4740F502119943D9F524C9119805C000000100084000011040018110300120900000003150100E803001B0700410000000000001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B0700460000000000006A001324951CA5CB0F05B5740F020210180005B5740F222519942D9E524C9100008B000000100083000011040018110300120900000003160100E803001B0700010000310900001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B070046000000000000134E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java
new file mode 100644
index 000000000..1c2f9492a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EnforaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EnforaProtocolDecoder decoder = new EnforaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "000A08002020202020303131303730303030353730323637"));
+
+ verifyNull(decoder, binary(
+ "003B000502000000000820202020202030313130373030303035373032363720383A000000000D00508401358E640032B37700000367B00000A804"));
+
+ verifyPosition(decoder, binary(
+ "007100040200202020202020202020382020202020202031323334353637383930313233343520313320244750524D432C3232333135322E30302C412C333530392E3836303539342C4E2C30333332322E3734333838372C452C302E302C302E302C3032303631322C2C2C412A35320D0A"),
+ position("2012-06-02 22:31:52.000", true, 35.16434, 33.37906));
+
+ verifyPosition(decoder, binary(
+ "007600040200202020202020202020382020202020202030313138393230303036303831383920313320244750524D432C3137313834312E30302C412C333530392E3835323431302C4E2C30333332322E3735393131332C452C302E302C302E302C3137303731322C332E342C572C412A32350D0A00"));
+
+ verifyPosition(decoder, binary(
+ "006a000a081000202020202020202020333320202020202038363130373430323137313936353620204750524d432c3136313234382e30302c412c333433322e36393231312c532c30353833312e30323231372c572c302e3034382c2c3232303831342c2c2c412a3734"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java
new file mode 100644
index 000000000..b615e5062
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EsealProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EsealProtocolDecoder decoder = new EsealProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Normal,34,2017-08-31,08:14:40,15,A,25.708828N 100.372870W,10,0,Close,0.71,0:0:3:0,3.8,-73,E##"));
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup,1,2017-08-31,02:01:19,3,V,0.000000N 0.000000E,0,0,Close,3.25,0:0:5:0,3.8,-93,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup OK,1,180,30,30,16,1,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup OK,1,180,30,30,16,1,E##"));
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000898,256,3.0.6,Normal,6,2017-09-06,23:48:39,3,V,0.000000N 0.000000E,0,0,Close,1.0,0:0:3:0,4.0,-81,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000898,256,3.0.6,RC-NFC DEL ACK,,E##"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
new file mode 100644
index 000000000..16f00d69b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class EsealProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ EsealProtocolEncoder encoder = new EsealProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_DISARM);
+
+ assertEquals("##S,eSeal,123456789012345,256,3.0.8,RC-Unlock,E##", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java
new file mode 100644
index 000000000..ed587e4f3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EskyFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EskyFrameDecoder decoder = new EskyFrameDecoder();
+
+ verifyFrame(
+ binary("454f3b303b3836313331313030363436313930383b523b363b3138303432303130343735313b322e39373839363b3130312e36353039313b302e37353b3332303b333339383b313b7c"),
+ decoder.decode(null, null, binary("454f3b303b3836313331313030363436313930383b523b363b3138303432303130343735313b322e39373839363b3130312e36353039313b302e37353b3332303b333339383b313b7c")));
+
+ verifyFrame(
+ binary("454c3b313b3836343930363032393139363632363b3137303832323134333432363b"),
+ decoder.decode(null, null, binary("454c3b313b3836343930363032393139363632363b3137303832323134333432363b")));
+
+ verifyFrame(
+ binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333"),
+ decoder.decode(null, null, binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333")));
+
+ verifyFrame(
+ binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333"),
+ decoder.decode(null, null, binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
new file mode 100644
index 000000000..da7df0ab2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EskyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EskyProtocolDecoder decoder = new EskyProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "EO;0;861311006461908;R;6;180420104751;2.97896;101.65091;0.75;320;3398;1;|"));
+
+ verifyNull(decoder, text(
+ "EL;1;864906029196626;170822143426;"));
+
+ verifyPosition(decoder, text(
+ "EO;0;864906029196626;R;7+170822143646+-26.10806+27.94600+0.40+0+0x1+0+102540+0+1242"));
+
+ verifyPosition(decoder, text(
+ "EO;0;864906029196626;R;0+170808155352+0.00000+0.00000+0.00+0+0x1+0+0+0+1233"));
+
+ verifyPosition(decoder, text(
+ "ET;1;014682000989425;R;0+171216001250+33.34405+-111.96682+0.00+0+0x1+0+25598+0+1257+0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java
new file mode 100644
index 000000000..c5b3364cf
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ExtremTracProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ExtremTracProtocolDecoder decoder = new ExtremTracProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GPRMC,862106020628733,050859.000,A,1404.8573,N,08710.9967,W,0.00,0,080117,0,,00C8,00218,99,,,,,,0.00"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,10000000001,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,00000000000,092244.000,A,0000.0000,S,00000.0000,E,0.00,0.00,101016,0,,8000,0"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,1111,1111"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,100936.000,A,0000.0000,S,00000.0000,E,0.00,0.00,101016,0,,8000,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
new file mode 100644
index 000000000..1dcfc89c4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FifotrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FifotrackProtocolDecoder decoder = new FifotrackProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$$79,868345037864709,382,D05,190220085833,22.643210,114.018176,1,1,1,13152,23FFD339*25"));
+
+ verifyPosition(decoder, text(
+ "$$105,866104023179743,AB,A00,,161007085534,A,54.738791,25.271918,0,350,151,0,17929,0000,0,,246|1|65|96DB,936|0*0B"));
+
+ verifyPosition(decoder, text(
+ "$$103,866104023179743,5,A00,,161006192841,A,54.738791,25.271918,0,342,200,0,4265,0000,0,,246|1|65|96DB,9C4|0*75"));
+
+ verifyPosition(decoder, text(
+ "$$103,866104023179743,4,A00,,161006192810,V,54.738791,25.271918,0,158,122,0,4235,0000,0,,246|1|65|96DB,9C5|0*69"));
+
+ verifyPosition(decoder, text(
+ "$$135,866104023192332,29,A01,,160606093046,A,22.546430,114.079730,0,186,181,0,415322,0000,02,2,460|0|27B3|EA7,A2F|3B9|3|0,940C7E,31.76|30.98*46"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java
new file mode 100644
index 000000000..9e5a45a83
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FlespiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlespiProtocolDecoder decoder = new FlespiProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"position.speed\":0,\"position.latitude\":53.90573,\"time.valid.status\":true,\"timestamp\":1506956075,\"position.satellites\":10,\"message.buffered.status\":false,\"business.mode.status\":true,\"gps.status\":true,\"position.longitude\":27.455848,\"position.direction\":0,\"ident\":\"605630\"},{\"siren.status\":false,\"business.mode.status\":true,\"position.satellites\":8,\"timestamp\":1506695785,\"led.status\":false,\"position.latitude\":53.905569,\"position.longitude\":27.455986,\"position.speed\":0,\"gradual.stop.status\":false,\"position.direction\":262.643854,\"hardware.version.enum\":223,\"vehicle.mileage\":160,\"message.buffered.status\":false,\"blinkers.status\":false,\"ident\":\"605630\",\"position.altitude\":233.48,\"immobilizer.status\":false}]")));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]")));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]")));
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java
new file mode 100644
index 000000000..28ecaf646
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FlexCommProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlexCommProtocolDecoder decoder = new FlexCommProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "7E00865067022408382201705302358271024932258006712785200700022601010224100040002C5002A2210001000000010012342107"));
+
+ verifyPosition(decoder, text(
+ "7E27865067022408382201705241211301024932197006712794000910022481008234100040002C5002A2200011000000006306941827"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java
new file mode 100644
index 000000000..3f268fde9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class FlextrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlextrackProtocolDecoder decoder = new FlextrackProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "-1,LOGON,7000000123,8945000000"));
+
+ verifyNull(decoder, text(
+ "-1,LOGON,1080424008,8945020110126633198"));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20060101,123442,1080424008,N0.00.0000,E0.00.0000,0,0,0,4129,-61,2,23866,0,999,A214,63,2EE2,3471676"));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20050205,181923,7000004634,N55.46.0812,E009.21.1665,122,198,6,3934,-81,01A8,23802,213,55,37FD,45,0055,12878"),
+ position("2005-02-05 18:19:23.000", true, 55.76802, 9.35278));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20050205,181923,7000004634,N55.46.0812,E009.21.1665,122,198,6,3934,-81,01A8,23802,213,55,37FD,45,0055,12878"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java
new file mode 100644
index 000000000..837b36b64
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FoxProtocolDecoder decoder = new FoxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"10\" data=\"51,A,010416,085317,4444.4158,N,02025.4466,E,1,182,,1110111111110111 141 0 0 0 0 0 10010000 10142,018C81851800009B\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"1092,V,010101,000004,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"31,V,110316,125952,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,65 60 60 60 60 60 60 60 65 65 55 55 60 60 60 60 60 60 60 60 55 55 60 55 65 60 60 60 60 60 60 55\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"0,V,110316,130154,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664 0\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"0,A,110316,131834,4448.8355,N,02028.2021,E,0,217,,1111111111111111 123 0 0 0 0 0 00000000 50020,50020 0\"/></fox>"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java
new file mode 100644
index 000000000..b2fc1fd8d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FreedomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FreedomProtocolDecoder decoder = new FreedomProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "IMEI,353358011714362,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05"),
+ position("2014-05-22 20:49:32.000", true, 47.43271, 19.20914));
+
+ verifyPosition(decoder, text(
+ "IMEI,353358011714362,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java
new file mode 100644
index 000000000..a84c4e357
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FreematicsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FreematicsProtocolDecoder decoder = new FreematicsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "1#EV=2,TS=1871902,ID=ESP32305C06C40A24*AC"));
+
+ verifyNull(decoder, text(
+ "0#EV=1,TS=23930,ID=ID1C6606C40A24,SK=TEST_SERVER_KEY*49"));
+
+ verifyPositions(decoder, text(
+ "1#0:102560,20:0;0;0,24:425,10:4285580,A:-35.803696,B:175.748413,C:0.22,D:0.41,F:5,0:103174,20:0;0;0,24:423,10:4285660,A:-35.803696,B:175.748413,C:0.22,D:0.41,F:5,30:88193792*21"));
+
+ verifyPositions(decoder, text(
+ "1#0:49244,20:0;0;0,24:423,0:50779,20:0;0;0,24:425,30:32924444*38"));
+
+ verifyNotNull(decoder, text(
+ "1#0:47607,20:0;0;0,24:423,0:48732,20:0;0;0,24:428,10:4280140,A:0.000000,B:0.000000,C:0.00,D:18520000.00,F:2,30:32924444*BA"));
+
+ verifyPositions(decoder, text(
+ "1#0:68338,10D:79,30:1010,105:199,10C:4375,104:56,111:62,20:0;-1;95,10:6454200,A:-32.727482,B:150.150301,C:159,D:0,F:5,24:1250*7A"));
+
+ verifyPositions(decoder, text(
+ "1#0=68338,10D=79,30=1010,105=199,10C=4375,104=56,111=62,20=0;-1;95,10=6454200,A=-32.727482,B=150.150301,C=159,D=0,F=5,24=1250*7A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
new file mode 100644
index 000000000..4f4972895
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GalileoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GalileoFrameDecoder decoder = new GalileoFrameDecoder();
+
+ assertEquals(
+ binary("011780011102e603383633353931303238393630323437043200801c"),
+ decoder.decode(null, null, binary("011780011102e603383633353931303238393630323437043200801c")));
+
+ assertEquals(
+ binary("01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5"),
+ decoder.decode(null, null, binary("01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java
new file mode 100644
index 000000000..74612caab
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GalileoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GalileoProtocolDecoder decoder = new GalileoProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "011801018202130338363833343530333230343234323604640010a406207caa9f5b300c830a7901ca0ec802330000000034b802350540003e41703f422b1043234504004600e09000000000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d4d3140000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000f400000000f500000000f600000000f700000000f800000000f9000000008960"));
+
+ verifyPositions(decoder, binary(
+ "017583018202120338363833343530333230363635373304520010384520c850975b300cc03a910107cbf9023365000607341300350640012a41236a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4993b0500d64100d70000d8be02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000018202120338363833343530333230363635373304520010394520c950975b300cab3a91010ecbf902336000be06341300350640012a41266a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49b3b0500d64100d70000d8bc02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103a4520ca50975b300c953a910113cbf9023358008f06341300350640012a41206a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49e3b0500d64100d70000d8ba02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103b45204251975b300c6d3a91011dcbf9023300008a06341300350640013a41726a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103c4520bb51975b300c6d3a91011dcbf9023300008a06341300350640013a41816a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000e007"));
+
+ verifyNull(decoder, binary(
+ "07e10300ffd8ffe000114a464946000101010000000000000affe1011445786966000049492a000800000005000e010200140000004a0000000f0102000a0000005e000000100102000a0000006800000032010200130000007200000025880400010000008600000000000000494d45492033353336313230383730353035393247616c696c656f536b7971717a6d202020202020323031383a30383a30392030363a35393a303100060001000200020000004e0000000200050003000000d40000000300020002000000450000000400050003000000ec000000050001000100000000000000060005000100000004010000000000008e7f930240420f0000000000010000000000000001000000a11aaa0140420f00000000000100000000000000010000003300000001000000ffdb0084000d09090b09080d0b0a0b0e0d0d0f131f1413111113261b1d171f2d28302f2d282c2b3238483d323544362b2c3f553f444a4d505150303c585f584e5e484f504d010d0e0e131013251414254d342c344d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4dffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f41237526768c119a0811803c8e29a7eb40075151ba9e2801436060d0df779ef408685c50092db6801e63c9c83834c772a3140c818163522924fa5310367b9a8f3ce2806382734e2bc7340c68031c52ab1e86811ffd0edcf0335170fd7b5333023151c83d4ba"));
+
+ verifyPositions(decoder, false, binary(
+ "01560003383636303530303338343337353836044701e000000000e13c494e414c4c3a696e303d31313230362c696e313d302c696e323d302c696e333d302c696e343d302c696e353d302c4163633d3536363932343732353bfdef"));
+
+ verifyPositions(decoder, false, binary(
+ "012a0003383633353931303233353137333732046600e000000000e1104f555428332e2e3029203d2031313130bb29"));
+
+ verifyPositions(decoder, binary(
+ "0144030338363832303430303132363939333404320010ee0f20f5a86c57300570172f03bc7dfd023363002604343e00351c40092a414a6842af0e432445000046030050246b51666a524c055300000338363832303430303132363939333404320010ed0f20f4a86c57300570172f03b47dfd023363000d05343e00351140090a41c56742a60e432445000046030050b56a514f6a521b045300000338363832303430303132363939333404320010ec0f20e6a86c57300b34172f03287efd023300000000344900350d40290a41562742030b43234500004603205023455190445295005300000338363832303430303132363939333404320010eb0f20e4a86c57300b34172f03287efd023300000000344900350d40290b41000042bd0b432345000046032050dc31518c315200005300000338363832303430303132363939333404320010ea0f20c7a86c57300b34172f03287efd023300000000344900350d40a90b41000042050d43234500004600205000005100005200005300000338363832303430303132363939333404320010e90f204fa86c57300b34172f03287efd023300000000344900350d40a90b41000042ff0c43244500004600205000005100005200005300000338363832303430303132363939333404320010e80f20d7a76c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43244500004600205000005100005200005300000338363832303430303132363939333404320010e70f205fa76c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43254500004600205000005100005200005300000338363832303430303132363939333404320010e60f20e7a66c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43264500004600205000005100005200005300000338363832303430303132363939333404320010e50f206fa66c57300468172f03907cfd023300007a0a343600352b40a90b41000042030d43274500004600205000005100005200005300000338363832303430303132363939333404320010e40f2051a66c5730048c172f03ac7cfd02335300980a341600352b40a12b41000042040d43274500004600e0500000510000520000530000abde"));
+
+ verifyNull(decoder, binary(
+ "011380033836383230343030313534393038370432008590"));
+
+ verifyPositions(decoder, binary(
+ "01cf030446ba10630320a7054c533008f86c8e0310062c043347049e02344000350940013241506b428f10432244aeea572045f9004604a0500000510000529a6b5300000446ba10712420ce1c4b533009b4f06703043df4033381037b0a343800350a40093241db6b428f10432544c05ef81f45f9004604a050000051000052886b5300000446ba10702420c11c4b53300a54f16703c450f403336e034e0a343900350840093241dd6b428f1043254491eaf71f45f9004604a050000051000052c26b5300000446ba106f2420b31c4b53300cecf267033865f403336a03300a343800350740093241e66b429010432544b446582045f9004604a050000051000052f76b5300000446ba106e2420a61c4b53300c9cf467038878f403337b03370a343800350740093241b56b428f10432544ba46f81f45f9004604a050000051000052c66b5300000446ba106d2420991c4b53300bc8f56703508cf403338d036e0a343700350840093241d66b428f10432544b4ea572045f9004604a050000051000052846b5300000446ba106c24208c1c4b533008c8f5670370a0f403338703920a343a00350e40093241c76b428f10432544c0fef71f45f9004604a0500000510000528d6b5300000446ba106b24207f1c4b533009a4f5670338b4f403337603920a343c00350a40093241d06b428f104325449146a81f45f9004604a0500000510000528a6b5300000446ba106a2420721c4b53300b9cf56703ecc7f403337103810a343a00350840093241ca6b428f10432544d12e582045f9004604a050000051000052996b5300000446ba10692420651c4b53300a64f6670358dbf403337a03490a343900350840093241e56b429010432544aed2f71f45f9004604a050000051000052b26b5300000446ba10682420581c4b5330094cf86703e0eef4033381030c0a343a00350940093241f96b428f10432544cb2e182145f9004604a050000051000052926b5300000446ba106724204b1c4b533009f8fa67032802f503337b03fc09343b00350a40093241d86b428f10432544c0ea772145f9004604a0500000510000529e6b5300000446ba106624203e1c4b533009a0fd67036815f503338403fd09343c00350a40093241a86b428f10432544ae2e582045f9004604a050000051000052a86b5300000446ba10652420311c4b53300944006803b028f503338003ff09343d00350940093241dc6b428e10432544a8fea71f45f9004604a050000051000052e26b5300000446ba10642420241c4b533008f0026803083cf503338b03f909343c00350d40093241d36b428f10432544c0eaa71f45f9004604a050000051000052ab6b530000ff3f"));
+
+ verifyPositions(decoder, binary(
+ "011e8304320010270220dbd2f051300a90cf740328ac59033300000000347600351240012a41e92e42500f431f440006c814450f00460020500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d471020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f30000000004320010260220bdd2f051300590cf740328ac59033300000000347600351440090a41f02e427b0f431f44ff0db814450f00460000500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d471020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000043200102502208ed2f051300ed8d0740304ac5903330000000034a500350a40012a41ec2e422d0f431f440016b814450f00460020500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d44d020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000622e"));
+
+ verifyPositions(decoder, binary(
+ "01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java
new file mode 100644
index 000000000..34423578d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class GalileoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ GalileoProtocolEncoder encoder = new GalileoProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "status");
+
+ verifyCommand(encoder, command, binary("01200003313233343536373839303132333435040000e000000000e1067374617475731f64"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
new file mode 100644
index 000000000..e2be99cb9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
@@ -0,0 +1,50 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeId() {
+
+ assertEquals("3512345006", GatorProtocolDecoder.decodeId(12, 162, 50, 134));
+
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GatorProtocolDecoder decoder = new GatorProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "2424800026364101b31608041108380273453415301532000000008000010000122800000124000000c40d"));
+
+ verifyNull(decoder, binary(
+ "242421000658e3d851150d"));
+
+ verifyAttributes(decoder, binary(
+ "242480002658e3d851a60101c662bc00000000000000000000000000470007a30b0c00b10fc900ff00460d"));
+
+ verifyNull(decoder, binary(
+ "242421000643e30282070d"));
+
+ verifyPosition(decoder, binary(
+ "24248000260009632d141121072702059226180104367500000000c04700079c0c34000ad80b00ff000a0d"),
+ position("2014-11-21 07:27:02.000", true, 59.37697, 10.72792));
+
+ verifyPosition(decoder, binary(
+ "24248100230CA23285100306145907022346901135294700000000C04001012C0E1100000021CB0D"));
+
+ verifyPosition(decoder, binary(
+ "2424800023c2631e00111220104909833268648703804100000000c0470000000b4e00000000550d"),
+ position("2011-12-20 10:49:09.000", true, -33.44773, -70.63402));
+
+ verifyPosition(decoder, binary(
+ "24248000260009632d141121072702059226180104367500000000c04700079c0c34000ad80b00ff000a0d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java
new file mode 100644
index 000000000..373c8c49e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GenxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GenxProtocolDecoder decoder = new GenxProtocolDecoder(null);
+
+ decoder.setReportColumns("28,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182");
+
+ verifyPosition(decoder, text(
+ "000036004133,11/05/2017 00:03:45,45.54767,-73.75547,0,0,63,569.35,118,ON,10687,0,12,O,9,3669.000,95.0,0.0,1,107.9464,0.0065,583.752,43,0.00,28.26,7.60,NA,U,UUU,0,-95.0,U"));
+
+ decoder.setReportColumns("1,2,3,4");
+
+ verifyPosition(decoder, text(
+ "000036004130,08/31/2017 17:24:13,45.47275,-73.65491,5,19,117,1.14,147,ON,1462,0,6,N,0,0.000,-95.0,-1.0,0,0.0000,0.0000,0.000,0,0.00,0.00,0.00,NA,U,UUU,0,-95.0,U"));
+
+ verifyPosition(decoder, text(
+ "000036004130,08/31/2017 17:24:37,45.47257,-73.65506,3,0,117,1.14,124,ON,1489,0,5,N,0,0.000,-95.0,-1.0,0,0.0000,0.0000,0.000,0,0.00,0.00,0.00,NA,U,UUU,0,-95.0,U"));
+
+ decoder.setReportColumns("1,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182");
+
+ verifyPosition(decoder, text(
+ "000036035855,04/16/2017 21:19:07,45.46485,-73.65424,24,32,61:213,342.51,157,ON,20984,0,12,O,18,0.000,95.0,24.0,1990,64.0894,0.0219,316.009,71,0.00,16.78,5.10,NA,U,UUU,0,-95.0,U"));
+
+ verifyPosition(decoder, text(
+ "000036004129,10/20/2017 00:54:27,43.44638,-79.68616,36,310,6,4954.40,321,ON,35377,0,12,O,13,0.000,85.6,36.0,1573,451.2514,0.0012,5260.953,0,0.00,122.48,33.17,NA,U,UUU,0,-95.0,U"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java
new file mode 100644
index 000000000..ffafcd7b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java
@@ -0,0 +1,56 @@
+package org.traccar.protocol;
+
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gl100ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl100ProtocolDecoder decoder = new Gl100ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "+RESP:GTLGL,359464030492644,1,2,1,0,0.4,0,299.7,1,5.455551,51.449776,20160311083229,0204,0016,03EC,BD94,00,0036,0102090501"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359464030439249,1,0,61,1,0.0,346,-2.7,2,-80.392825,26.122424,20151214000354,0310,0260,72BC,35F5,00,04B6,0102070407"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,135790246811220,1,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102070202"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,135790246811220,2,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,1,-3.6,145,30.0,2,121.354442,31.221940,20090101000100,0460,0000,18d8,6141,00,11F0,0102070202"));
+
+ verifyNull(decoder, text(
+ "AT+GTHBD=HeartBeat,359231030000010,20090101000000,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTSOS,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"),
+ position("2009-01-01 00:00:00.000", false, 31.22207, 121.35434));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTRTL,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTEST,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTSZI,359231030000010,0,3,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTLBC,359231030000010,02132523415,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359231030000010,1,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359231030000010,2,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,0,0,1,-3.6,145,30.0,2,121.354442,31.221940,20090101000100,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359464030073766,1,0,0,0,1.7,254,-27.8,3,30.474475,50.488383,20131107155511,0255,0003,6995,4761,00,0071,0103090402"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java
new file mode 100644
index 000000000..e6fb98340
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gl200BinaryProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200BinaryProtocolDecoder decoder = new Gl200BinaryProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "2b4556542d00fc1fbf0063450102020956325403000343056437f8220700000200000000010000160100f2007eff75a1f0025c6b1a07e1080108241a02680003189c1ac500000000000002100800000000000000000007e1080108241a19e24e4e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "2b5253506400fc1fbf058e450102020956325403000343056438ed2205010e61c6f0ff75a1b4025c6af959803d8ba07ffe17dea03f7e1fdda0007df7dfa03e7e3fd0a0befdf7cea001fddfd8a000fdefdca042fd9fe1a0427d6fe9a0017db7dca0407d47e7a0027d67e5bfc0fd77eca03ffd8fe4bfff7dcfddbffd7dbfdebffdfddfe2bfbe7e0fe1bf7f7e67e2bf7bfed7e2bf7c7f5fe2bffbffc7e3a12880a7daa0b9013fe3a0f801b7dfa0bd81efe1a03f8207e0a03e8217e4a07e023fe9a0bd824feca03a02affda07b02d004a07f02e007a00002d808a041830001a003834fefa00402b7eebf8382a7ebbfc28267e9bf81821fe3bf0181d7e3bf01016fe9bf010117edbf4080c7f6bf7f8087fabfbf805ff9a0fa8097fca23401300aa0b4016019a13a817026a13b81883ea0be81a83fa0bd81b03ba00101d83abfc0039874bfc081b835bfbf819834bfc081982fa01004702ea00502500da002827802bfc0825fffbf41821fffbf4081d801bf3f816802bec180fffebec20077fdbf80002801bfc0000800a000000800e0e0a202804ffba14a8127eea0460107e4a0cc809fd9a0c4004fcda0c2004fcaa080007fbfa0410067bebfc100c7b6a03f8037c1bfbf004fc6a03f0057c6a0410027c5a081001fbaa0418017baa001001fb8a0007fe7bca000ffdfb7a0817fc7b7a040ffbfb3a0407fb7b4a0407fb7b1bf807fbfb3a0007fb7b5a0007fb7b1a0007fb7b2a0007fbfb3a0407fafaba000ffb7ada0017f97aba040ff7faca001ff77b6bf3fff67b3bf007f87bea082ff47b4bfc27f17c1bfffff3fc2bebdff9fcabe3effbfe0bf3cff47e9a03c002ff0a1740097e9a1f8813fe1a12f01f7fca0fa028ff8a07f02a7fea041829007a00302bff8bf810287f2a0080257e1a0050207dbbfc481cfd3a044819fcda043015fc3a043810fc2a0c680a7b2a0448027b0a0857fa7aea0c37f67a2a0017ee7a7a0407f0f9fa000ff079fa03ffeffa1a03ffeffa2a07fff17a0a03fff1fa0a03fff2fa2a03fff3fa0a07fff47a2a0007f579fa03fff4f9da03fff679ca0007f679ea000ff4f9ca07fff5f9ca0007f579cbfc07f5f9fbf407f6fa6bf807f6fabbfc07f7fadbf807f87b2a0407f87b0a0407f77aba000ff77afbfc07f77aea03fff7fada07fff7faca000ff7fada0007f77abbfc0ff77b0a000ff7faea042ff2faba0037ee7ada0437e57a1a0037e27abbfc1fdf7bcbf827defc5bf01fe0fcebf017e3fd5bfc17e2fdca0ff7e27d7a13cfe27cba0bd7e0fa7a07d7e6fb9a07d7eb7b3a07efedfaea03ffef7afa0c07eefa7a07f7f07a3a03fff0fa3a03f7f27a2a03f7f37a4bfff7f6fa3bfff7f5fa3a07eff979fa03f7fbfa2a03f7fdfa1bfbf8017a5bfbf0037adbeff004fb8bfbf004fc1beff804fcbbf7f8047d5bf408027debf408007e8a03e804fdebf7e8027f2bf00ffefffa0400017efa0418017f1a041002feca0410017edbf007fbffea0007fdff6a1018027e4bf81ffc7f6a1008017dca0c10087bea0018097baa083ffc7cda0837fafc7a102ff0fc8a0c27effb9a0c2fe87c1a143fbf78ea07f7dcfc0a0bd7dffb3a03d7e47b1a03ffe77afa07ffe77ada0007e77aebfc07e77afbfc07e8faea03ffea7afbfbf7ecfb4bfbcfeffbbbf7bff8fb8bf7d0027bebffb809fc7bffa00ffc9bf78813fd9a03d8197e1a03b81d7e6a07e01efdda00081bfdda00101bfdba03f81a7dfbfc001afdbbfbd81a7f1bfbe0187eebf80814feea0028127e7a081813fe2a0010147e6a03e8147f4a0408167eca040817fe7a0018157e4bfc8011fdaa08b002fd1a009ffa7d5a009fe57f6a04b7df7f4a0097e07eca0027df7edbf807e2feca000fe3feca07ffe37f0a0407e0ff7a040fde7f0a0007de7eea000fdcff4a001fddffaa000fdcff8a0027dcffda07f7dbff3a03f7dc7f402680003189c355300000109000002120700000000000000000007e1080108290019e63b5c0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "2B5253500300FC1FFF0064450102020867623130302D446F642F442105007018217345005F010100000001100045073C4D4101DB86BD07E106130B2B0F0460000018770013000000030000000106020F2300002714301107E106130B2B1003424EFB0D0A"));
+
+ verifyPositions(decoder, binary(
+ "2b5253500700fc1fbf005d4501020209563254030003430564377e42071001000000000000007eff75a151025c6a8107e10801081a2a02680003189c1ac500000000000002100700000000000000000007e1080108241019e17ebe0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "2b494e4601fd7f0076676231303000000045010202090104020500004100054007e107150b061d0000003f010e02580000000000d0312a1013648935103226313921591f1200000000000302680003189c1ac3001b02680003189c1ac4000d02680003189c1ac5001207e107150b0d3704f658060d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a"));
+
+ verifyNull(decoder, binary(
+ "2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
new file mode 100644
index 000000000..e90c6495a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gl200FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200FrameDecoder decoder = new Gl200FrameDecoder();
+
+ assertEquals(
+ binary("2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a"),
+ decoder.decode(null, null, binary("2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a")));
+
+ assertEquals(
+ binary("2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a"),
+ decoder.decode(null, null, binary("2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a")));
+
+ assertEquals(
+ binary("2b524553503a47545354522c3430303330302c3836323336353033303134323238392c474c3530302c302c302c302c33392e342c39332c312c302e332c31372c3130352e382c32352e3934343234302c34342e3430333733362c32303137303532393134303533302c303232362c303030312c353643322c373038342c2c2c2c32303137303532393136303533302c30324441"),
+ decoder.decode(null, null, binary("2b524553503a47545354522c3430303330302c3836323336353033303134323238392c474c3530302c302c302c302c33392e342c39332c312c302e332c31372c3130352e382c32352e3934343234302c34342e3430333733362c32303137303532393134303533302c303232362c303030312c353643322c373038342c2c2c2c32303137303532393136303533302c3032444124")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
new file mode 100644
index 000000000..2fe860573
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -0,0 +1,390 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gl200TextProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(null);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTPFA,F50201,866425030235982,GL300M,20190208124849,0BD4$"),
+ Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTPNA,F50201,866425030235982,GL300M,20190208124909,0BD5$"),
+ Position.KEY_ALARM, Position.ALARM_POWER_ON);
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTSTC,410301,864802030022424,,,0,,,,,,,0228,0002,4EE8,1BFF489,00,20181207134332,EC90$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0900,860599000306845,G3-313,0,0,4,1,2.1,0,426.7,8.611466,47.681639,20181214134603,0228,0001,077F,4812,25.2,1,5.7,34,437.3,8.611600,47.681846,20181214134619,0228,0001,077F,4812,25.2,1,4.4,62,438.2,8.611893,47.681983,20181214134633,0228,0001,077F,4812,25.2,1,4.8,78,436.6,8.612236,47.682040,20181214134648,0228,0001,077F,4812,25.2,83,20181214134702,0654$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,270703,867162025056839,gv300w,0,1,E07FFFFF,,2,H9307659,368713.50,1291,90,91,,P82.40,,61,10.10,6.76,3.34,524.08,,,0000,,00,,,007FFFFF,,,,,,,,,,,,,,,,,,,,,0000,2,0,,,0,88.6,104,117.6,-116.886007,32.543697,20181031202959,0334,0020,5234,7FCC3D0,00,20181031203002,9F50$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,310701,863286023712855,,00000004,28378,10,1,1,0.0,294,358.4,14.271475,50.110771,20181111185001,0230,0003,94D4,3B30,00,14.5,,,,110000,2,0,C03FFFFF,,0,H46400,12310.70,0,0,83,,,,0,,0.53,3.43,,,,40,,0,,,20181111185252,2DFF"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,003FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,20181110103016,2945$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,203FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,007FFFFF,,,,,,0,,,134,37,6,0.19,,0.00,0,,,,,,0,0,0,20181110112126,299F$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,E03FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,007FFFFF,,,,,,0,,,134,37,6,0.19,,0.00,0,,,,,,0,0,0,0,0.0,312,358.4,14.271460,50.110796,20181110103130,0230,0003,94D4,3B30,00,20181110105348,2969$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0301,862193022001432,WF0GXXGBBGBM26503,,14900,41,1,1,11.6,74,356.0,14.120023,50.167894,20181104080703,0230,0003,9B14,5891,00,74.1,,,,83,220000,799,7.3,,20181104080703,099B$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,4B0201,867995030001575,,10,0,C03FFFFF,,0,H0,,,,,,,,,,0.00,0.03,,,,0,,0,,,0,10.0,310,404.3,14.096743,50.143363,20181102110535,0230,0003,9B14,5066,00,20181102112101,03E0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTSTR,440502,866427030112088,GL530,0,0,2,,100,3,0.6,0,127.5,2.413963,48.877096,20180704180102,0208,0001,0310,E625,,,0000,20180704180100,004C$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,300500,860599002636595,,0,0,0,0.0,0,2886.5,-78.467145,-0.165335,20180518221815,,,,,,20180518221817,B6FD$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,300500,860599002636595,,0,1,0,0.0,0,2886.5,-78.467145,-0.165335,20180518221818,,,,,,20180518221819,B6FF$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTSW,1A0100,135790246811220,,1,0,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20100214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,1A0100,135790246811220,,0,1,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20100214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGF,270302,867162025085234,,3519,0,0.0,92,111.2,-116.867638,32.450321,20180327070835,0334,0020,2B24,52CC3DE,00,,243.1,20180327070837,2A98$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTDIS,270302,867162025086950,,,21,1,1,0.0,81,117.8,-116.862025,32.453497,20180309084516,0334,0020,2B24,52CA916,00,1286.2,20180309084517,357E$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGL,270302,867162025085234,,,01,1,1,0.0,92,111.2,-116.867638,32.450321,20180327070838,0334,0020,2B24,52CC3DE,00,243.1,20180327070839,2A9A$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310201,153759012347650,gv65,0,1,C03FFFFF,,2,H89394,63.14,200,0,87,,P43.60,0,0,17.53,11.61,5.92,0.00,0,0,4002,0,1,0.76,35.00,0,,,,0,0,,0000,0000,0000,0000,00,20040101000052,05A6$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,00,1,C03FFFFF,,2,H2843820,373.76,1440,44,77,M23,P35.00,1810,,59.48,42.68,16.80,15.42,,,610,,0,,,0,42.7,263,27.2,-2.156478,51.899989,20171021151805,0234,0010,15D6,9AD2,00,20171021151807,0B28$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,02,1,C03FFFFF,,0,H2843820,373.80,0,4,75,M12,,1800,,59.49,42.69,16.80,15.42,,,0,,0,,,0,0.7,75,24.3,-2.155148,51.899400,20171021151837,0234,0010,15D6,9AD2,00,20171021152355,0B2E$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,380603,869606020025833,gv65,00000002,12003,10,1,1,0.0,172,24.6,-81.931875,26.577439,20171002045352,0310,0260,72BD,8E5B,00,1052.1,01383:52:12,0,100,210700,2,1,28FF4560A3150483,1,05B0,20171002045402,9548$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,04040E,861074023747143,gv200,41,8959301000648637556f,24,0,1,0,1,4.4,0,1,0,0,20170912221854,0,00,01,-0500,1,20170912193448,1D5B$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,354524044950583,,42,89011702272048900184,11,99,0,,,4.08,0,1,1,0,0,20170831170831,87,0.00,,,,20170831171010,0064$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,2D0300,A1000043D20139,1G1JC5444R7252367,,11,,31,0,1,12986,,4.16,0,2,,,20170802145640,,,,,,+0000,0,20170802145643,CD5A$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTMPN,450102,865084030001323,gb100,0,1.6,0,-93.1,121.393023,31.164105,20170619103113,0460,0000,1806,2142,00,20170619103143,0512$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTMP,110100,358688000000158,,2,60,1,1,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,00,80,20110214093254,000F$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,110100,358688000000158,,41,0,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,,20110214093254,0022$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTBPL,110100,358688000000158,,3.53,0,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,,20110214093254,001F$"));
+
+ verifyNotNull(decoder, buffer(
+ "+BUFF:GTIGL,060228,862894020180553,,,00,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,20170407081824,9606$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,210100,,,,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$"));
+
+ verifyPositions(decoder, false, buffer(
+ "+RESP:GTFRI,210102,354524044925825,,1,1,1,,,,,,,,310,410,51bc,ca1dae6,10800,1,20170318214333,0002$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030025161,STR,0234,0015,003a,62a2,16,,0234,0015,003a,56a2,14,,0234,0015,003a,062a,13,,0234,0015,003a,32d9,11,,0234,0015,003a,56a0,11,,,,,,,,0234,0015,003a,7489,17,,20170219200048,0033$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030025161,STR,0234,0015,003a,56a2,18,,0234,0015,003a,77bc,14,,0234,0015,003a,32d9,12,,0234,0015,003a,062a,12,,0234,0015,003a,62a2,11,,0234,0015,003a,56a0,10,,0234,0015,003a,7489,15,,20170219080049,0030$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030034940,STR,0234,0030,0870,2469,19,,0234,0030,0870,35ee,18,,0234,0030,0870,16ac,12,,0234,0030,0870,16b2,11,,0234,0030,0870,360f,6,,0234,0030,0870,165d,6,,0234,0030,0870,35ef,17,,20170215220049,008D$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034940,GL500,0,0,2,21.1,86,0,1.6,0,5.8,0.622831,51.582688,20170215090422,0234,0030,0870,35EF,,,,20170215220049,008C$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,2C0402,867162020000816,,0,0,1,2,0.3,337,245.7,-82.373387,34.634011,20170215003054,,,,,,63,20170215003241,3EAB$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTWIF,210102,354524044608058,,4,c413e200ff14,-39,,,,c413e2010e55,-39,,,,c8d3ff04a837,-43,,,,42490f997c6d,-57,,,,,,,,100,20170201020055,0001$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTWIF,210102,354524044484948,,1,08626693fb98,-36,,,,,,,,97,20170119071300,05E3$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,A100004D9EF2AE,,41,,8,99,0,17.7,21,3.58,0,1,1,0,0,20161216135038,4,,,,,20161216135038,00AB$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034957,GL500,0,0,2,23.1,5,2,0.2,0,36.0,0.623089,51.582744,20161129174625,0234,0015,03C3,3550,,,,20161129174625,0026$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034957,GL500,0,1,2,21.8,100,0,,,,,,,0234,0015,03C3,3550,,,,20161129174009,0023$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,A10000499AEF9B,,41,,0,0,0,15.0,9,3.87,0,1,1,0,0,20161101140211,72,,,,,20161101140211,00A3$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTNMR,210102,A10000499AEF9B,,0,0,1,9,0.0,0,288.0,-76.902364,39.578828,20161101134145,,,,,00,73,20161101134145,009F$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,210102,A10000499AEF9B,,0,1,1,9,0.5,0,288.0,-76.902364,39.578828,20161101134124,,,,,00,73,20161101134123,009D$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTRTL,210102,A10000499AEF9B,,0,0,1,10,0.2,0,305.4,-76.902274,39.578517,20161101155001,,,,,00,73,20161101155001,00A6$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,110100,358688000000158,,41,898600810906F8048812,18,99,0,33.23,1,4.19,1,1,1,0,0,20110714104934,100,,,,,20110714104934,0014$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,080100,135790246811220,,16,898600810906F8048812,16,0,1,11870,,4.1,0,0,0,,20090214013254,,12340,,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,040100,135790246811220,,16,898600810906F8048812,16,0,1,,0,4.4,0,0,0,0,20090214013254,13000,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,060100,135790246811220,,16,898600810906F8048812,16,0,1,12000,,4.4,0,0,0,0,20090214013254,0,1300,2000,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,1A0800,860599000773978,GL300,41,89701016426133851978,17,0,1,26.6,,3.90,1,1,0,0,0,20161003184043,69,1,44,,,20161004040811,022C$"));
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTINF,1A0800,860599000773978,GL300,41,89701016426133851978,23,0,1,204.7,,3.84,1,1,0,0,0,20161006072548,62,1,38,,,20161006082343,0C98$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,360100,864251020141408,3VWGW6AJ0FM237324,gv500,,10,1,1,0.0,0,2258.4,-99.256948,19.555800,20160929214743,0334,0020,0084,65AC,00,0.0,,,,100,410000,0,nan,,20160929214743,13BA$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,360201,864251020186064,4T1BE46KX7U018210,,0,19FFFF,4T1BE46KX7U018210,1,14283,983901C0,799,36,18,,33792,0,0,0,,,38,,6,53557,0,0.0,0,219.5,-76.661456,39.832588,20160507132153,20160507132154,0230$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,360201,864251020186064,1G1JC5444R7252367,,12802,10,1,0,0.0,0,219.5,-76.661456,39.832588,20160507132235,,,,,,20460.9,00080:03:37,,,100,210000,791,,56,20160507132239,0233$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0101,135790246811220,1G1JC5444R7252367,,,00,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,210100,,,50,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0101,135790246811220,1G1JC5444R7252367,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,92,80,210100,,,50,20090214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,060228,862894020178276,,21,0,0.0,0,411.3,-63.169745,-17.776330,20160319132220,0736,0003,6AD4,5BAA,00,20160319092223,1FBD$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTNMR,210102,A10000458356CE,,0,1,1,9,0.0,8,190.7,-85.765865,42.894837,20160316123202,,,,,60,30,20160316123202,0137$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIDA,060228,862894020178276,,,01C68011010000C7,1,1,0,0.0,0,413.0,-63.169675,-17.776349,20160317222129,0736,0003,6AD4,32CF,00,34.9,,,,,20160317182130,1626$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGN,060228,862894020180553,,9860,0,0.2,189,420.0,-63.158195,-17.800608,20160309022951,0736,0003,6AD4,3471,00,,881.2,20160308222956,129A$"));
+
+ verifyPosition(decoder, buffer(
+ "+BUFF:GTIGF,060228,862894020180553,,1958,0,0.0,240,390.3,-63.089213,-17.764712,20160309122854,0736,0003,6AB8,5A23,00,,936.8,20160309082858,1368$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020180553,,,10,1,1,20.0,147,329.7,-62.899703,-17.720434,20160309113548,0736,0003,6AAE,3381,00,913.3,,,,0,220101,,,,20160309073554,132B$"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,2C0204,867162020003125,GL300W,0,0,2,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210433,0740,0000,7596,5891C,0.0,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210503,0740,0000,7596,5891C,0.0,88,20160215210506,1E78$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020178276,,15153,10,1,1,0.0,0,431.7,-63.169571,-17.776235,20160210153458,0736,0003,6AD4,80EF,00,34.9,00117:31:26,13442,15163,0,210101,,,,20160210113503,38EE$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,110100,A5868800000015,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,00,80,20110214013254,000C"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTFRI,210102,A10000458356CE,,0,1,1,15,1.4,0,190.6,-85.765763,42.894896,20160208164505,4126,210,0,18673,00,92,20160208164507,00A6"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIDA,06020A,862170013895931,,,D2C4FBC5,1,1,1,0.8,0,22.2,117.198630,31.845229,20120802121626,0460,0000,5663,2BB9,00,0.0,,,,,20120802121627,008E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,1F0101,135790246811220,1G1JC5444R7252367,,16,898600810906F8048812,16,0,1,12000,,4.2,0,0,,,20090214013254,,,,,,+0800,0,20090214093254,11F0$"));
+
+ verifyPositions(decoder, false, buffer(
+ "+RESP:GTFRI,120113,555564055560555,,1,1,1,,,,,,,,0282,0380,f080,cabf,6900,79,20140824165629,0001$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,0F0106,862193020451183,,,10,1,1,0.0,163,,-57.513617,-25.368191,20150918182145,,,,,,21235.0,,,,0,210100,,,,20150918182149,00B8$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,1F0109,864251020135483,,gv500,0,78FFFF,,1,12613,,,,,,,,,,,,,,1286,0,0.0,0,17.1,3.379630,6.529701,20150813074639,0621,0030,51C0,A2B3,00,0.0,20150813074641,A7E6$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,1F0109,864251020135483,4T1BE46KX7U018210,gv500,0,78FFFF,4T1BE46KX7U018210,1,13411,981B81C0,787,3,43,,921,463,1,10,0300030103030304001200310351035203530354,20,55,,1286,0,6.5,74,21.6,3.379710,6.529714,20150813074824,0621,0030,51C0,A2B3,00,0.0,20150813074828,A7E9$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,1A0401,860599000508846,,41,0,0.0,84,107.5,-76.657998,39.497203,20150623160622,0310,0260,B435,3B81,,20150623160622,0F54$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0401,860599000508846,,0,0,1,1,134.8,154,278.7,-76.671089,39.778885,20150623154301,0310,0260,043F,7761,,99,20150623154314,0F24$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0200,860599000165464,CRI001,0,0,1,2,,41,,-71.153137,42.301634,20150328020301,,,,,280.3,55,20150327220351,320C"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010D,867844001675407,,0,0,1,2,0.0,0,28.9,8.591011,56.476397,20140915213209,0238,0001,03CB,2871,,97,20140915213459,009A"));
+
+ verifyNull(decoder, buffer(
+ "+RESP:GTINF,359464030073766,8938003990320469804f,18,99,100,1,0,+2.00,0,20131018084015,00EE,0103090402"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,04040C,359231038939904,,,10,1,2,0.0,117,346.0,8.924243,50.798077,20130618122040,0262,0002,0299,109C,00,0.0,,,,,,,,,20130618122045,00F6"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,04040C,359231038939904,,42,0,0.0,117,346.0,8.924243,50.798077,20130618125152,0262,0002,0299,109C,00,20130618125154,017A"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,000035988863964,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,135790246811220,,0,0,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTDOG,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLBC,020102,135790246811220,,+8613800000000,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTGCR,020102,135790246811220,,3,50,180,2,0.4,296,-5.4,121.391055,31.164473,20100714104934,0460,0000,1878,0873,00,,20100714104934,000C"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTFRI,07000D,868487001005941,,0,0,1,1,0.0,0,46.3,-77.039627,38.907573,20120731175232,0310,0260,B44B,EBC9,0015e96913a7,-58,,100,20120731175244,0114"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTHBM,0F0100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTHBM,0F0100,135790246811220,,,11,1,1,24.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010C,867844001274144,,0,0,1,1,18.0,233,118.1,7.615551,51.515600,20140106130516,0262,0007,79E6,B956,,72,20140106140524,09CE$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010C,867844001274649,,0,0,1,1,0.0,0,122.5,7.684216,51.524512,20140106233722,0262,0007,79EE,1D22,,93,20140107003805,03C4$"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,210101,863286020016706,,,10,1,1,,,,49.903915,40.391669,20140818105815,,,,,,,,,,,210100,,,,,000C$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,240100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,80,,,,,,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,240100,135790246811220,,,10,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,,,,,20090214093254,11F0$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTSTT,280100,A1000043D20139,,42,0,0.1,321,228.6,-76.660884,39.832552,20150615120628,0310,0484,00600019,0A52,,20150615085741,0320$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTRTL,280100,A1000043D20139,,0,0,1,1,0.1,321,239.1,-76.661047,39.832501,20150615114455,0310,0484,00600019,0A52,,87,20150615074456,031E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTBPL,1A0800,860599000773978,GL300,3.55,0,0.0,0,257.1,60.565437,56.818277,20161006070553,,,,,204.7,20161006071028,0C75$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTTEM,1A0102,860599000000448,,3,33,0,5.8,0,33.4,117.201191,31.832502,20130109061410,0460,0000,5678,2079,,20130109061517,0091$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTJDR,0A0102,135790246811220,,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTJDS,0A0102,135790246811220,,2,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTSOS,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTVER,1A0800,860599000773978,GL300,GL300,0A03,0103,20161007041531,10F8$"));
+
+ verifyNull(decoder, buffer(
+ "+ACK:GTHBD,1A0401,135790246811220,,20100214093254,11F0"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTRTO,1A0800,860599000773978,GL300,VER,FFFF,20161006053520,0C19"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTJDC,0A0102,135790246811220,,0016,20090214093254,11F0"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTGEO,1A0102,135790246811220,,0,0008,20100310172830,11F0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java
new file mode 100644
index 000000000..9746845a0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java
@@ -0,0 +1,56 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GlobalSatProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GlobalSatProtocolDecoder decoder = new GlobalSatProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "GSh,131826789036289,3,M,ea04*3d"));
+
+ decoder.setFormat0("SORPZAB27GHKLMN*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,011412001878820,4,5,00,,1,250114,105316,E00610.2925,N4612.1824,0,0.02,0,1,0.0,64*51!"));
+
+ verifyPosition(decoder, text(
+ "GSr,357938020310710,,4,04,,1,170315,060657,E00000.0000,N0000.0000,148,0.00,0,0,0.0,11991mV*6c!"));
+
+ decoder.setFormat0("TSPRXAB27GHKLMnaicz*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,1,135785412249986,01,I,EA02,3,230410,153318,E12129.2839,N2459.8570,0,1.17,212,8,1.0,12.3V*55"));
+
+ verifyPosition(decoder, text(
+ "GSr,GTR-128,012896009148443,0040,5,0080,3,190813,185812,W11203.3661,N3330.2104,344,0.24,78,9,0.8,60%,0,0,12,\"310,410,0bdd,050d,02,21\",\"310,410,0bdd,0639,24,7\"*79"));
+
+ verifyPosition(decoder, text(
+ "$355632004245866,1,1,040202,093633,E12129.2252,N2459.8891,00161,0.0100,147,07,2.4"));
+
+ verifyPosition(decoder, text(
+ "$355632000959420,9,3,160413,230536,E03738.4906,N5546.3148,00000,0.3870,147,07,2.4"));
+
+ verifyPosition(decoder, text(
+ "$353681041893264,9,3,240913,100833,E08513.0122,N5232.9395,181.3,22.02,251.30,9,1.00"));
+
+ decoder.setFormat0("SPRXYAB27GHKLMmnaefghiotuvwb*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,GTR-128,013227006963064,0080,1,a080,3,190615,163816,W07407.7134,N0440.8601,2579,0.01,130,12,0.7,11540mV,0,77,14,\"732,123,0744,2fc1,41,23\",\"732,123,0744,2dfe,05,28\",\"732,123,0744,272a,15,21\",\"732,123,0744,2f02,27,23\"*3b!"));
+
+ verifyPosition(decoder, text(
+ "$80050377796567,0,13,281015,173437,E08513.28616,N5232.85432,222.3,0.526,,07*37"),
+ position("2015-10-28 17:34:37.000", true, 52.54757, 85.22144));
+
+ verifyPosition(decoder, text(
+ "$80050377796567,0,18,281015,191919,E08513.93290,N5232.42141,193.4,37.647,305.40,07*37"),
+ position("2015-10-28 19:19:19.000", true, 52.54036, 85.23222));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java
new file mode 100644
index 000000000..91aca50c8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GnxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GnxProtocolDecoder decoder = new GnxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GNX_MIF,865733022354161,143,0,172642,180316,172642,180316,1,13.034581,N,080.234521,E,0,05396274,ROUTE_2#########,Deo ############,GNX04008,B0*"));
+
+ verifyPosition(decoder, text(
+ "$GNX_LOC,865733022352132,095,0,102134,280914,102134,280914,1,18.765432,N,073.752811,W,032,165.32,12,25,0,A,E,2,000099.9,000099.5,GNX01001,12*"));
+
+ verifyNull(decoder, text(
+ "$GNX_LOC,865733022354161,139,0,142838,160316,142825,160316,0,000000000,N,0000000000,E,000,0.00,00,48,0,e,C,2,000000.0,000000.0,GNX04008,BB*"));
+
+ verifyPosition(decoder, text(
+ "$GNX_DIO,863071015071563,110,1,155627,121214,151244,121214,1,08.878321,N,076.643154,E,0,0,0,0,0,0,GNX01001,B1*"));
+
+ verifyNull(decoder, text(
+ "$GNX_DIO,865733022354161,112,1,142849,160316,142714,160316,0,000000000,N,0000000000,E,0,0,0,0,0,0,0,GNX04008,1A*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
new file mode 100644
index 000000000..70c86bb23
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
@@ -0,0 +1,87 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GoSafeProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GoSafeProtocolDecoder decoder = new GoSafeProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024226974,101437211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;7;N3.052302;E101.787216;16;137;48;1.58,COT:4261733103,ADC:22.86;0.58;0.01,DTT:4004;E1;0;0;0;3$101439211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052265;E101.787200;12;152;46;1.31,COT:4261733103,ADC:22.98;0.58;0.01,DTT:4004;E1;0;0;0;3$101441211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052247;E101.787232;8;131;46;1.34,COT:4261733103,ADC:23.13;0.58;0.01,DTT:4004;E1;0;0;0;3$101510211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052150;E101.787152;0;131;40;0.97,COT:4261733160,ADC:22.88;0.58;0.01,DTT:4000;E1;0;0;0;1$101540211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;7;N3.052150;E101.787152;0;131;40;0.97,COT:4261733160,ADC:22.91;0.58;0.00,DTT:4000;E1;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359568052570135,090349191018,,SYS:G3C;V1.40;V1.0.4,GPS:A;10;S26.112722;E28.078766;0;23;1581;0.80,COT:,ADC:10.86;3.79,DTT:4000;E7;0;0;0;1$090419191018,,SYS:G3C;V1.40;V1.0.4,GPS:A;10;S26.112722;E28.078766;0;23;1581;0.80,COT:,ADC:10.85;3.79,DTT:4000;E7;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359913060650380,152248050718,,SYS:G3C;V1.38;V05,GPS:A;10;N31.914370;E35.914640;0;0,COT:689,ADC:0.18;3.55,DTT:4025;E6;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359913060650380,101019050718,,SYS:G3C;V1.38;V05,GPS:L;6;N31.916576;E35.908480;0;0,GSM:1;4;416;3;627A;A84B;-66,COT:188,ADC:4.31;3.88,DTT:4005;E6;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024287174,070120310318,,SYS:G3SC;V3.32;V1.1.8,GPS:A;9;N23.169946;E113.450568;0;0;23;0.86,COT:65;20,ADC:4.27;3.73;0.01;0.02,DTT:4004;E0;0;0;0;1,IWD:0;0;000000000000#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024213915,032544190318,,SYS:G3SC;V3.32;V1.1.8,GPS:A;7;N3.052417;E101.787112;0;0;94;1.38,COT:686;0-0-0,ADC:16.25;4.09,DTT:4000;E0;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,351535058659335,062728190318,,SYS:G6S;V3.32;V1.0.5,GPS:A;10;N23.169806;E113.450760;0;0;81;0.77,COT:0,ADC:0.00;0.16,DTT:80;E0;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS26,356449061046586,082522030117,,SYS:G737IC;V1.13;V1.0.5,GPS:V;5;N42.594136;W70.723832;0;0;8;2.06,GSM:;;310;260;C76D;9F1D;-85,ADC:3.86,DTT:3918C;;0;0;0;1,#"));
+
+ verifyPositions(decoder, text(
+ "*GS56,357330051092344,123918301116,10,GPS:L;9;N47.582920;W122.238720;0;0;102;0.99,GSM:0;0;310;410;A7DB;385C;-86,COT:76506,ADC:0.82;3.77,DTT:2184;;0;0;10000;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.75,DTT:0;;0;0;40;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.74,DTT:0;;0;0;40;0$000000000000,93,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.73,DTT:8000;;0;0;80000;0$000000000000,13,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.09;3.79,DTT:2004;;0;0;80000;0$000000000000,90,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.13;3.79,DTT:23004;;0;0;10000;0$000000000000,,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,GSM:5;2;310;410;A7DB;385C;-89,COT:76506,ADC:14.12;3.81,DTT:23184;;0;0;0;6#"));
+
+ verifyPositions(decoder, text(
+ "*GS26,356449061139936,022918011216,,SYS:G737IC;V1.13;V1.0.5,GPS:A;9;N42.651728;W70.623520;0;0;48;1.50,ADC:4.08,DTT:3900C;;0;0;0;1,#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052339180916,,SYS:G7S;V1.08;V1.2,GPS:V;4;N24.730006;E46.637816;14;0;630,GSM:;;420;4;5655;507A;-70,COT:75242;2-8-17,ADC:13.22;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052349180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;507A;-70,COT:75290;2-8-27,ADC:13.24;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052444180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;F319;-102,COT:75290;2-9-27,ADC:13.00;0.08,DTT:23004;;0;0;0;1$052449180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;F319;-102,COT:75290;2-9-27,ADC:13.13;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,356449062643845,141224290316,,SYS:G79;V1.13;V1.0.2,GPS:V;5;N24.694972;E46.680736;46;334;606;1.43,GSM:;;420;4;5655;4EB8;-57,COT:330034,ADC:13.31;3.83,DTT:27004;;0;0;0;1,OBD:064101000400000341057E04410304000341510104411001C203410F4B0341112904411F01AB0641010004000014490201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03410D21,FUL:28260"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,145425130316,,GPS:V;0;N0.000000;E0.000000;0;0;0;0.00;0.00,GSM:1;3;416;3;A8C;2820;-81;416;3;A8C;281F;-83;416;3;A8C;368A;-87;416;3;A8C;368B;-89;416;3;A8C;2C26;-103;416;3;A8C;3689;-107;416;3;A8C;2D83;-107"));
+
+ verifyPosition(decoder, text(
+ "*GS02,358696043774648,GPS:230040;A;S1.166829;E36.934287;0;0;170116,STT:20;0,MGR:32755204,ADC:0;11.2;1;28.3;2;4.1,GFS:0;0"));
+
+ verifyNull(decoder, text(
+ "*GS02,358696043774648"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535058709775,100356130215,,SYS:G79W;V1.06;V1.0.2,GPS:A;6;N24.802700;E46.616828;0;0;684;1.35,COT:60,ADC:4.31;0.10,DTT:20000;;0;0;0;1"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,074558291015,,GPS:A;9;N31.935942;E35.867092;;345;921;1.03;1.59,GSM:1;3;416;3;A8C;368B;-78;416;3;A8C;2820;-73;416;3;BB8;2CBE;-76;416;3;A8C;368A;-76;416;3;A8C;2C26;-79,OBD:04410C122003410D0F03411C0103410547037F011203411100"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,083515281015,,GPS:A;9;N31.959502;E35.908316;;108;890;1.05;1.79,GSM:1;4;416;3;AF0;A3A6;-59;416;3;AF0;A3A3;-50;416;3;AF0;A3A4;-56;416;3;AF0;A3A5;-62;416;3;AF0;B195;-76,OBD:04410C194603410D2303411C0103410583037F011203411115"));
+
+ verifyNull(decoder, text(
+ "*GS16,351535058709775"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,103441131015,,GPS:A;8;N31.960122;E35.921652;27;99;847;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-61;416;3;AF0;9C89;-68,OBD:04410C0DA403410D0B03411C010341057A037F011203411100$103453131015,,GPS:A;8;N31.959976;E35.922144;6;0;835;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D8E03410D0B03411C010341057D037F011203411100$103503131015,,GPS:A;9;N31.959870;E35.922284;11;127;830;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D8E03410D0B03411C010341057D037F011203411100$103513131015,,GPS:A;9;N31.959742;E35.922516;10;106;830;1.37;2.91,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D1003410D0603411C010341057E037F011203411100$103553131015,,GPS:A;8;N31.959564;E35.923308;6;0;836;1.41;2.43,GSM:1;4;416;3;AF0;9C73;-65;416;3;AF0;B389;-71;416;3;AF0;9C89;-74,OBD:04410C0DAE03410D0403411C010341057C037F011203411100#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,155750220815,,SYS:G79;V1.10;V1.0.2,GPS:A;4;N31.944198;E35.846644;0;0;923;9.47;1.00,COT:155133,ADC:12.21;0.10,DTT:20002;;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,070034220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945970;E35.859848;29;65;922;1.14;1.68,COT:147528,ADC:14.07;0.11,DTT:27006;;0;0;0;3,OBD:04410C1ECD03410D2D03411C010341057A037F011203411107$070035220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945934;E35.859908;29;86;922;1.14;1.68,COT:147528,ADC:13.94;0.15,DTT:27006;;0;0;0;3,OBD:04410C1ECD03410D2D03411C010341057A037F011203411107$070037220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945844;E35.859952;29;123;922;1.14;1.68,COT:147625,ADC:13.75;0.11,DTT:27006;;0;0;0;3,OBD:04410C0FE803410D1803411C010341057C037F011203411100$070038220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945808;E35.859940;29;145;923;1.14;1.68,COT:147625,ADC:14.00;0.11,DTT:27006;;0;0;0;3,OBD:04410C0FE803410D1803411C010341057C037F011203411100#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java
new file mode 100644
index 000000000..ca3ddfda8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GotopProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GotopProtocolDecoder decoder = new GotopProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ ""));
+
+ verifyNull(decoder, text(
+ "353327020412763,CMD-X"));
+
+ verifyPosition(decoder, text(
+ "013226009991924,CMD-T,A,DATE:130802,TIME:153721,LAT:25.9757433S,LOT:028.1087816E,Speed:000.0,X-X-X-X-81-26,000,65501-00A0-4B8E"));
+
+ verifyPosition(decoder, text(
+ "353327020115804,CMD-T,A,DATE:090329,TIME:223252,LAT:22.7634066N,LOT:114.3964783E,Speed:000.0,84-20,000"),
+ position("2009-03-29 22:32:52.000", true, 22.76341, 114.39648));
+
+ verifyPosition(decoder, text(
+ "353327020115804,CMD-T,A,DATE:090329,TIME:223252,LAT:22.7634066N,LOT:114.3964783E,Speed:000.0,1-1-0-84-20,000"));
+
+ verifyPosition(decoder, text(
+ "353327020412763,CMD-F,V,DATE:140125,TIME:183636,LAT:51.6384466N,LOT:000.2863866E,Speed:000.0,61-19,"));
+
+ verifyPosition(decoder, text(
+ "013949008891817,CMD-F,A,DATE:150225,TIME:175441,LAT:50.000000N,LOT:008.000000E,Speed:085.9,0-0-0-0-52-31,000,26201-1073-1DF5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
new file mode 100644
index 000000000..ce21f733f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gps056FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps056FrameDecoder decoder = new Gps056FrameDecoder();
+
+ assertEquals(
+ binary("242435314750534c5f30323836323436323033333738323934361905110f160b0b7710584e1cbd1b9b4500005b100300fb0a071700ffff23"),
+ decoder.decode(null, null, binary("242435314750534c5f30323836323436323033333738323934361905110f160b0b7710584e1cbd1b9b4500005b100300fb0a071700ffff230030")));
+
+ assertEquals(
+ binary("242432354c4f474e5f3131383632343632303333373832393436322e3123"),
+ decoder.decode(null, null, binary("242432354c4f474e5f3131383632343632303333373832393436322e3123")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java
new file mode 100644
index 000000000..a6d0c024a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gps056ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps056ProtocolDecoder decoder = new Gps056ProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "$$25LOGN_118624620337829462.1#"));
+
+ verifyPosition(decoder, binary(
+ "242435314750534C5F30323836333037313031353034353834391D0A0E091A0A0B1112C34E1E23230A45FF00000000000000000000000023"));
+
+ verifyAttributes(decoder, binary(
+ "2424323853594E435F313138363330373130313530343538343900000000000023"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
new file mode 100644
index 000000000..da8d8ff5a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
@@ -0,0 +1,261 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gps103ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps103ProtocolDecoder decoder = new Gps103ProtocolDecoder(null);
+
+ verifyAttribute(decoder, text(
+ "imei:868683023212255,tracker,190205084503,,F,064459.000,A,4915.1221,N,01634.5655,E,3.91,83.95;"),
+ "course", 83.95);
+
+ verifyPosition(decoder, text(
+ "imei:864180034124375,vt14,190116192753,,F,172750.000,A,3649.2186,N,00235.8411,W,0.00,0,,0,0,51.93%,,+22;"));
+
+ verifyNull(decoder, text(
+ "imei:864180034124375,vr,0c00fa011ea05a03d726977103ad0034c98ef49e6d303fffd1c8361303f2dbb0fa530d8ca3930be3e94f4110145c7029a507a0a00028f4a70514c05c500503170334b400531971cad002ab7634f001a4027949c8e541ea47f853bca2f961ba427ab1e290089197711c485e4f6e82ad0d1ee8f25573eed4af60284f6935bb7ef2307f1dc3f4355bbf3f90a607ffd2e5fcc6c60310be80f152dbca54ed53147fed3ae7fa1fe54809e45330fdedc993d073b7f2a6340a00d8cabeb9a4980c68900e5cb1f6e29aab9e00fc334012a5a5c32e5227c7b21a9a2d35dc02cdb49edb189fe545ec058fec7565e667cfa08b1fccd34686e4f12003dd7ffaf4b980957440bd595beb53269bb082a517dd570693604cb6299cb1663f5a9d608e3e42807d71cd002d206028011e50aa49381513494011349cd2092803ffd3d031f154ef11bc86d870781f8679a90302540adc545c55a00e28c8a602e47ad2e46680133cd19e6800cd1cf4a0063293ce695188e0d20265e58559305c3ffcb2948eca10d20278adb50518890c40f5ed9a5fece941ccb29cfd19ff00c2a6e860d6471c7da1cffb3b507eb9aaafa3dc4a4909b7fdf6c9fd28b88fffd4ca5d06e7b94152af87a53f7a7403d949a8e60265f0eaff0014ee7e8807f5a99340857abcadf881fd2973013a6936f1f48d4ffbc01fe95652dd63fba00fa521926d1de8e07a51600dc3d01d00;"));
+
+ verifyPosition(decoder, text(
+ "imei:868683026321020,T:+11,181217080050,,F,080047.000,A,3227.3057,N,11649.4754,W,0.00,0,,0,0,0.00%,,+11;"));
+
+ verifyAttribute(decoder, text(
+ "imei:868683026321020,tracker,181217080106,,F,080102.000,A,3227.3057,N,11649.4754,W,0.00,0,,0,0,0.00%,0,+11;"),
+ Position.PREFIX_TEMP + 1, 11);
+
+ verifyPosition(decoder, text(
+ "imei:861359038609986,Equipo 1,---,------,----,214734,241018,26,1,-33.42317,-70.61930,067,229,0674,1.00,08,0,1,---,*"));
+
+ verifyPosition(decoder, text(
+ "imei:861359038609986,Equipo 1,---,------,----,214812,241018,14,0,-33.42317,-70.61930,000,000,0000,99.9,00,0,1,---,*"));
+
+ verifyNull(decoder, text(
+ "imei:123451234512345,L,*"));
+
+ verifyAttributes(decoder, text(
+ "imei:868683027758113,OBD,180905200218,,,,0,0,0.39%,70,9.41%,494,0.00,P0137,P0430,,;"));
+
+ verifyAttribute(decoder, text(
+ "imei:353451044508750,001,0809231929,13554900601,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,30.1,65.43,1,0,10.5%,0.0%,28;"),
+ "fuel1", 10.5);
+
+ verifyPosition(decoder, text(
+ "imei:864180036029895,acc on,180508145653,,F,065645.000,A,4729.1497,N,01904.2342,E,0.00,0,,1,,0.00%,,;"));
+
+ verifyNotNull(decoder, text(
+ "imei:864895030279986,ac alarm,180404174252,,L,,,296a,,51f7,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710048977327,OBD,180301094003,5000000,0.00,0.00,98,18,68.63%,55,25.10%,1368,14.24,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170605095949,195874,,370.8,808,066,30.0%,+87,13.0%,02444,14.3,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:868683027825532,OBD,170613203014,,,,0,0,0.00%,0,0.00%,0,0.00,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170612165656,196043,,145803.9,,000,0.0%,+,0.0%,00000,12.6,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170605095949,195874,,370.8,808,066,30.0%,+87,13.0%,02444,14.3,,,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,DTC,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,30.1,,1,0,10.5%,P0021,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil1,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,,,51.6,41.7,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil2,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,,,51.6,41.7,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil 51.67,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,T:+28.0,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,bonnet alarm,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,footbrake alarm,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:862106021237716,ac alarm,1611291645,,F,204457.000,A,1010.2783,N,06441.0274,W,0.00,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049057798,OBD,161003192752,1785,,,0,54,96.47%,75,20.00%,1892,0.00,P0134,P0571,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049090138,OBD,160629022949,51442,0.00,15.88,5632,122,40.39%,95,0.00%,2101,13.80,,,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:359710049090138,tracker,160629022948,,F,182949.000,A,4043.8839,N,11328.8029,W,65.26,271.82,,1,0,31.37%,51442,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049042014,001,160615040011,,F,040011.000,A,2833.0957,N,07711.9465,E,0.01,215.33,,0,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049028435,OBD,160316053657,70430,,,0,49,60.00%,46,19.22%,859,0.00,U1108,,,;"));
+
+ verifyPosition(decoder, text(
+ "359769031878322imei:359769031878322,tracker,1602160718,2,F,221811.000,A,1655.2193,S,14546.6722,E,0.00,,"));
+
+ verifyNull(decoder, text(
+ "imei:865328021049167,OBD,141118115036,,,0.0,,000,0.0%,+,0.0%,00000,,,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049032874,OBD,160208152900,13555,,,45,0,24.71%,35,13.73%,1230,14.13,U1108,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049064398,OBD,160101035156,17887,0.00,17.06,0,0,0.00%,0,0.00%,16383,10.82,,,,"));
+
+ verifyPosition(decoder, text(
+ "imei:868683020235846,rfid,160202091347,49121185,F,011344.000,A,0447.7273,N,07538.9934,W,0.00,0,,0,0,0.00%,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:359710049075097,help me,,,L,,,113b,,558f,,,,,0,0,,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:359710041100000,tracker,000000000,,L,,,fa8,,c9af,,,,,0,0,0.00%,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:863070016871385,tracker,0000000119,,L,,,0FB6,,CB5D,,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710045559474,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;"),
+ position("2015-10-30 00:01:01.000", true, 54.72306, 25.21512));
+
+ verifyPosition(decoder, text(
+ "imei:359710049092324,tracker,151027025958,,F,235957.000,A,2429.5156,N,04424.5828,E,0.01,27.91,,0,0,,,;"),
+ position("2015-10-26 23:59:57.000", true, 24.49193, 44.40971));
+
+ verifyPosition(decoder, text(
+ "imei:865328021058861,tracker,151027041419,,F,011531.000,A,6020.2979,N,02506.1940,E,0.49,113.30,,0,0,0.0%,,;"),
+ position("2015-10-27 01:15:31.000", true, 60.33830, 25.10323));
+
+ // Log on request
+ verifyNull(decoder, text(
+ "##,imei:359586015829802,A"));
+
+ // Heartbeat package
+ verifyNull(decoder, text(
+ "359586015829802"));
+
+ // No GPS signal
+ verifyNull(decoder, text(
+ "imei:359586015829802,tracker,000000000,13554900601,L,;"));
+
+ verifyPosition(decoder, text(
+ "imei:869039001186913,tracker,1308282156,0,F,215630.000,A,5602.11015,N,9246.30767,E,1.4,,175.9,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710040656622,tracker,13/02/27 23:40,,F,125952.000,A,3450.9430,S,13828.6753,E,0.00,0"));
+
+ verifyPosition(decoder, text(
+ "imei:359710040565419,tracker,13/05/25 14:23,,F,062209.000,A,0626.0411,N,10149.3904,E,0.00,0"));
+
+ verifyPosition(decoder, text(
+ "imei:353451047570260,tracker,1302110948,,F,144807.000,A,0805.6615,S,07859.9763,W,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587016817564,tracker,1301251602,,F,080251.000,A,3223.5832,N,11058.9449,W,0.03,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587016817564,tracker,1301251602,,F,080251.000,A,3223.5832,N,11058.9449,W,,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000208821,tracker,1301080525,,F,212511.000,A,2228.5279,S,06855.6328,W,18.62,268.98,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000208821,tracker,1301072224,,F,142411.077,A,2227.0739,S,06855.2912,,0,0,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000431811,tracker,1210260609,,F,220925.000,A,0845.5500,N,07024.7673,W,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:100000000000000,help me,1004171910,,F,010203.000,A,0102.0003,N,00102.0003,E,1.02,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451040164707,tracker,1105182344,+36304665439,F,214418.000,A,4804.2222,N,01916.7593,E,0.37,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451042861763,tracker,1106132241,,F,144114.000,A,2301.9052,S,04909.3676,W,0.13,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587010124900,tracker,0809231929,13554900601,F,112909.397,A,2234.4669,N,11354.3287,E,0.11,321.53,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451049926460,tracker,1208042043,123456 99008026,F,124336.000,A,3509.8668,N,03322.7636,E,0.00,,"));
+
+ // SOS alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,help me,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Low battery alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,low battery,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Geo-fence alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,stockade,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Move alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,move,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Over speed alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,speed,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211051840,,F,104000.000,A,2220.6483,N,11407.6377,,0,0,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211051951,63360926,F,115123.000,A,2220.6322,N,11407.5313,E,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211060621,,F,062152.000,A,2220.6914,N,11407.5506,E,15.85,347.84,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070012698733,tracker,1303092334,,F,193427.000,A,5139.0369,N,03907.2791,E,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:869039001186913,tracker,130925065533,0,F,065533.000,A,5604.11015,N,9232.12238,E,0.0,,329.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710041641581,acc alarm,1402231159,,F,065907.000,A,2456.2591,N,06708.8335,E,7.53,76.10,,1,0,0.03%,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710041641581,acc alarm,1402231159,,F,065907.000,A,2456.2591,N,06708.8335,E,7.53,76.10,,1,0,0.03%,,"));
+
+ verifyPosition(decoder, text(
+ "imei:313009071131684,tracker,1403211928,,F,112817.000,A,0610.1133,N,00116.5840,E,0.00,,,0,0,0.0,0.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:866989771979791,tracker,140527055653,,F,215653.00,A,5050.33113,N,00336.98783,E,0.066,0"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045375005,tracker,150401165832,61.0,F,31.0,A,1050.73696,N,10636.49489,E,8.0,,22.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045403597,tracker,150420050648,53.0,F,0.0,A,N,5306.64155,E,00700.77848,0.0,,1.0,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045403597,tracker,150420051153,53.0,F,0.0,A,5306.64155,N,00700.77848,E,0.0,,1.0,;"));
+
+ verifyPosition(decoder, text(
+ "imei:359710047424644,tracker,150506224036,,F,154037.000,A,0335.2785,N,09841.1543,E,3.03,337.54,,0,0,45.16%,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:865328023776874,acc off,150619152221,,F,072218.000,A,5439.8489,N,02518.5945,E,0.00,,,1,1,0.0,0.0,23.0,;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
new file mode 100644
index 000000000..f888ee252
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gps103ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() throws Exception {
+
+ Gps103ProtocolEncoder encoder = new Gps103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("**,imei:123456789012345,C,05m", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ Gps103ProtocolEncoder encoder = new Gps103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "H,080");
+
+ assertEquals("**,imei:123456789012345,H,080", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java
new file mode 100644
index 000000000..d35666b56
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java
@@ -0,0 +1,52 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GpsGateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsGateProtocolDecoder decoder = new GpsGateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$FRCMD,0097,_SendMessage,,7618.51990,S,4002.26182,E,350.0,1.08,0.0,250816,183522.000,0*7F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,356406061385182,_SendMessage,,5223.88542,N,11440.45866,W,951.2,0.027,,220716,153507.00,1*5F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,353067011068246,_SendMessage,,1918.1942,N,09906.3696,W,2246.5,000.0,295.9,150416,213147.00,1,Odometer=*70"));
+
+ verifyNull(decoder, text(
+ "$FRCMD,862950025974620,_Ping,voltage=4*4F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,862950025974620,_SendMessage, ,2721.5781,S,15259.145,E,61,0.00,61,080316,092612,1,SosButton=0,voltage=4*60"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,user1,8IVHF*7A"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,354503026292842,VGZTHKT*0C"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,IMEI,1234123412341234,*7B"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,saab93_device,KLRFBGIVDJ*28"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,154403.000,A,6311.64120,N,01438.02740,E,0.000,0.0,270707,,*0A"),
+ position("2007-07-27 15:44:03.000", true, 63.19402, 14.63379));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,074524,A,5553.73701,N,03728.90491,E,10.39,226.5,160614,0.0,E*75"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,154403.000,A,6311.64120,N,01438.02740,E,0.000,0.0,270707,,*0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java
new file mode 100644
index 000000000..d6ba11cb7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class GpsMarkerProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsMarkerProtocolDecoder decoder = new GpsMarkerProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GM23D863071014445404T260816142611N55441051E037325071033063C0530304#"));
+
+ verifyNull(decoder, text(
+ "$GM300350123456789012T100511123300G25000001772F185200000000000000005230298#"));
+
+ verifyPosition(decoder, text(
+ "$GM200350123456789012T100511123300N55516789E03756123400000035230298#"),
+ position("2011-05-10 12:33:00.000", true, 55.86132, 37.93539));
+
+ verifyPosition(decoder, text(
+ "$GM1350123456789012T1005111233N55516789E03756123400000035200298#"));
+
+ verifyPosition(decoder, text(
+ "$GM203863071014445404T150715202258N55481576E03729275300000040530301#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java
new file mode 100644
index 000000000..438d0fb3a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GpsmtaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsmtaProtocolDecoder decoder = new GpsmtaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "3085a94ef459 1446536867 49.81621 24.054207 1 0 22 0 10 12 24 0 0"));
+
+ verifyPosition(decoder, text(
+ "864528021249771 1446116686 49.85073 24.004438 0 217 6 338 00 59 27 0 0"));
+
+ verifyNotNull(decoder, text(
+ "359144048138856 1442932957 -49.85064 -24.003979 1 0 40 0 10 110 26 0 0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
new file mode 100644
index 000000000..7c4bb06c2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GranitFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GranitFrameDecoder decoder = new GranitFrameDecoder();
+
+ assertEquals(
+ binary("2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"),
+ decoder.decode(null, null, binary("2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"),
+ decoder.decode(null, null, binary("2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("4f4b"),
+ decoder.decode(null, null, binary("4f4b0d0a2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a3932"),
+ decoder.decode(null, null, binary("2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a39320d0a")));
+
+ assertEquals(
+ binary("2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a4346"),
+ decoder.decode(null, null, binary("2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a43460d0a")));
+
+ assertEquals(
+ binary("2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a3231"),
+ decoder.decode(null, null, binary("2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a32310d0a")));
+
+ assertEquals(
+ binary("4552524f522057524f4e4720434845434b53554d5f31"),
+ decoder.decode(null, null, binary("4552524f522057524f4e4720434845434b53554d5f310d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java
new file mode 100644
index 000000000..fc75b2a53
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java
@@ -0,0 +1,49 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GranitProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GranitProtocolDecoder decoder = new GranitProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "2b444441547e8400c500040130050c43495808002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000014002a37420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c5008443495808002839aee315020000000064000000000000002a37410d0a"),
+ position("2016-12-08 11:27:00.000", false, 57.00888, 40.97143));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c500ec904858b842283997e30002000000005e000000000d00002a32390d0a"),
+ position("2016-12-07 22:45:00.000", true, 57.00853, 40.97105));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c500009148580800283997e30002000000005f000000000000002a33410d0a"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003b6d0401b10e9217445800b051398f35d34a313b000072000000010b000080b051398f35d34a313b000072000000010b0000f0b051390f33314c303b900371000000010b0000f0b05139cd31e54c2f3cd0016f000000010b0000f0b051396831204d303d950071000000010b0000f0b051397530aa4d323c610171000000010b00000a002a30420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a3932"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a4346"));
+
+ // +IDNT: Navigator.04x Firmware version 0712GLN *21
+ verifyAttributes(decoder, binary(
+ "2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a3231"));
+
+ // ERROR WRONG CHECKSUM_1
+ verifyAttributes(decoder, binary(
+ "4552524f522057524f4e4720434845434b53554d5f31"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java
new file mode 100644
index 000000000..f9b8f6509
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java
@@ -0,0 +1,43 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt02ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt02ProtocolDecoder decoder = new Gt02ProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "6868150000035889905895258400831c07415045584f4b210d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68682d0000035889905895258400951c1f415045584572726f723a20506172616d65746572203120284f4e2f4f4646290d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68680f0504035889905831401700df1a00000d0a"));
+
+ verifyAttributes(decoder, binary(
+ "6868130504035889905831401700001a040423261e290d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68681905040358899058314017000e1a010a2623211b2722252329000d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68681a060303588990500037252de91a010a171a191b171915191e10000d0a"));
+
+ verifyPosition(decoder, binary(
+ "68682500000123456789012345000110010101010101026B3F3E026B3F3E000000000000000000010D0A"),
+ position("2001-01-01 01:01:01.000", true, -22.54610, -22.54610));
+
+ verifyAttributes(decoder, binary(
+ "6868110603035889905101276600001a0402292d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "68682500a403588990510127660001100e09060a1d1b00ade1c90b79ea3000011b000000000000050d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java
new file mode 100644
index 000000000..cf6d1cfd7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt06FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt06FrameDecoder decoder = new Gt06FrameDecoder();
+
+ verifyFrame(
+ binary("787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"),
+ decoder.decode(null, null, binary("787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A")));
+
+ verifyFrame(
+ binary("78780d0103563140414198583c0d0a"),
+ decoder.decode(null, null, binary("78780d0103563140414198583c0d0a")));
+
+ verifyFrame(
+ binary("787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a"),
+ decoder.decode(null, null, binary("787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a")));
+
+ verifyFrame(
+ binary("7878121011091c0b1e2e98058507f80097a6ac03344a0d0a"),
+ decoder.decode(null, null, binary("7878121011091c0b1e2e98058507f80097a6ac03344a0d0a")));
+
+ verifyFrame(
+ binary("787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a"),
+ decoder.decode(null, null, binary("787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a")));
+
+ verifyFrame(
+ binary("787808134606020002044dc5050d0a"),
+ decoder.decode(null, null, binary("787808134606020002044dc5050d0a")));
+
+ verifyFrame(
+ binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a"),
+ decoder.decode(null, null, binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a")));
+
+ verifyFrame(
+ binary("78780d010352887071911998000479d00d0a"),
+ decoder.decode(null, null, binary("78780d010352887071911998000479d00d0a")));
+
+ verifyFrame(
+ binary("78782516000000000000c000000000000000000020000900fa0210ef00fb620006640301000468030d0a"),
+ decoder.decode(null, null, binary("78782516000000000000c000000000000000000020000900fa0210ef00fb620006640301000468030d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
new file mode 100644
index 000000000..a6008e682
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -0,0 +1,294 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gt06ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt06ProtocolDecoder decoder = new Gt06ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "787805120099abec0d0a"));
+
+ verifyNull(decoder, binary(
+ "78780D01086471700328358100093F040D0A"));
+
+ verifyPosition(decoder, binary(
+ "787821121303120b2524c70138e363085b549003d43301940057d200cd52c000006aa1ca0d0a"));
+
+ verifyAttribute(decoder, binary(
+ "7878251613020C12141AC5027951430C2A16F60014000900000000001A00007C550300020002F1E70D0A"),
+ Position.KEY_ALARM, Position.ALARM_REMOVING);
+
+ verifyNotNull(decoder, binary(
+ "7878006919012105090303028f01007549e05a00bc9c5c5a007a8d1a5a0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878001719011910543607028f0100bc2e695a00bcb3635a00bc27c56400bc447b6400bc46c96400bc33ce6400bc64ca640d0a"));
+
+ verifyAttributes(decoder, binary(
+ "797900de8c120b1502121b013137333d302c3232333d312c3238333d30303030303030302c3436333d30303030303035362c3437333d30303030303030662c3438333d30303030303031312c3242333d30303030303030302c3244333d30303030313839632c3335333d30303030313661382c3336333d30303032386163382c3339333d30303030303230612c3330333d30303030303561612c3439333d30303030303030302c3441333d4b4e4146323431434d4b353031343235352c3341333d30303030303338352c3530333d30303030c0041df0940f89c06700000000c800910d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790020940A035985708053908307060104900402788950301217070401538F0003E8210D0A"));
+
+ verifyPosition(decoder, binary(
+ "78783c3439000000120a0902093a07031f9e690529be2e00155500000016214901a30308b70000b3fb004aa82b059401a3422100000001000000007d9370b90d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7878079404eb001c705d0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78781219028f0a29e10036f12003040102000875dc0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790008940000ed0289d6860d0a"));
+
+ verifyNull(decoder, binary(
+ "797900a59404414c4d313d34353b414c4d323d44353b414c4d333d35353b535441313d34303b4459443d30313b534f533d303538353036313536372c2c3b43454e5445523d3b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c303b49434349443d38393937313033313031303038393539303432463b4d4f44453d4d4f44452c312c3138303b0008f65e0d0a"));
+
+ verifyPosition(decoder, binary(
+ "78782222120616083817c5050cc8c801a819d600152400e8011dbf003332000000004862500d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780B23C00122040001000818720D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78782EA4110C0C02281BF6026C18720C38D22800149C1181CC00010000260E000000000615F8012C05041102FF001058FD0D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78787aa2110c0e06372c813601040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c0104ff02001801eb4039d10000000000000004fabeb50d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782727110c0b0e170f850450059107f461ae001c7e0a81360104cb8a00bef32806030c02ff000316b10d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782322110c070f1b0270000000000000000000040081360104cb8a00bef3000000000523030d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787880a2110b161010140136040000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c0102ff033c1e04ddc28aa6001801eb4039c90000000000000014db84730d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782ba701cc000000919100000000090617032b3836313832323038343735363200000000000000000100049fb60d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787819a501cc0000009191000000000906170304050400010005f44d0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782ca3110b10081336f000000000000000000004003901cc0000009191000000000906170304050400010003e0940d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878A3A2110B0603201501CC010000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F80400FF08F483CD4DF4C0D750BD5F8BC5CECFB0D59DAFB459CDA8574E8424C6CC50BD5F6C7E1CC9B0D59D8AA718C90087363040E0C83496727B4DE4C7002919670D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78786CA1110B0413093801CC01000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201FF0002000541D70D0A"));
+
+ verifyPosition(decoder, binary(
+ "787822220F0C1D023305C9027AC8180C46586000140001CC00287D001F71000001000820860D0A"));
+
+ verifyAttributes(decoder, binary(
+ "797900262100000000020043006f006d006d0061006e00640020006500720072006f0072002100236e850d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231108500200cc080c4e2fa5640c4e2fa66e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800171710231108290200cc080c4e2fa5640c4e2fa5640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231109200400cc080c4e2fa55a0c4ec0025a0c4e2fa6640c583918640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231111210700cc080c4e2fa55a0c4ec0025a0c4e39295a0c583918640c4e2fa6640c4e2fa4640c4ec854640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800171710231112510600cc080c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878121011091c0b1b2999058508040097a89e0034520d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78780869170928113413ac9e17b30808514494fcf6e148596cb0ce2c67bd4a6eb0ce2c67bd4b0018e7d4333e55ec086be7f2df5fe48d8c94fc6657e48d8cb8f378510600cc0400d37a3d4600d37a3c5000d37a3b6400d376716400d305ac6400d393506e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78783b281108111002050136041bcf0000bf09000000000000000000000000000000000000000000000000000000000000000000000000ff00020007d3280d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878412c11030b011c1f013604cb8a00b17754cb8a00bef357cb8a00b73f5fcb8900b0e25fcb8900b6655fcb8a00b74960cb8a00b178620701001801eb40393800bbbde10d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878412c11030b012629013604cb8a00b17757cb8a00b73f5bcb8a00b7495ecb8900b0e25fcb8a00b1b9620000000000ff0000000000ffff01001801eb40393e00c0e6340d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822221106160a1016c60278019407c7783800040001940504700046fc01030100065f570d0a"));
+
+ verifyAttributes(decoder, binary(
+ "797900143311070609020b00000000a0030046000109e4610d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979003e32110707083819000901fe0a060f006a1e3f24000000000000000000000000000000000000000000000000000000000000000000000000000000012116ba0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979007632110706090217000901fe0a060f006a1c2024060f0053a429060f006a1d21060f0053a720060f006f151d0000000000000000000000003844d9e7f7e1773d60e327a9e442405cf28628b9c640a42bb0fc0d0244d855a38c220a4c802aa8da7dab50b0e235ef32dd5348ee0ce77a52540000010a205a0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979006f210000000001426174746572793a352e3536562c4e4f524d414c3b20475052532a4c696e6b2055702047534d205369676e616c204c6576656c3a5374726f6e673b204750533a4f46463b2020204c4f434b3a4f46463b204254204d41433a4234413832383034343436323b007260880d0a"));
+
+ verifyPosition(decoder, binary(
+ "7979004a321106170c1b180cc900a875580b7ab4f00010350901fe0a007c0009112424007c000912240081004efe2100c500100f1200000000000000000000000000000000000000000000bc7c900d0a"));
+
+ verifyNotNull(decoder, binary(
+ "79790045321106170c1b13000901fe0a007c0009112424007c000912230081004efe1e00c500100f120000000000000000000000000000000000000710bef565574e37000000b26f140d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010863586038760942a0010000010aa4000d0a"));
+
+ verifyNull(decoder, binary(
+ "78781f3511061a0b24330503107d06084889cb01000100000cfa20e3acd301333fcb0d0a"));
+
+ verifyPosition(decoder, binary(
+ "78783c340000000011061809130c0903107d2408488a5800144c00000000000001940b00b1000047ff000000000500018f42210000000100050003010b69450d0a"));
+
+ verifyPosition(decoder, binary(
+ "78783c34000000001106190336070903107d51084889b900152e0000000043b101940b00b10000480100000000050001a3422100000001000300011bdc7b5f0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780a13c40604000201298f5b0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78781f12110616091835d0024bb93007d3fb783dd4c501940500f2006c8504a6e0370d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822221106160a1016c60278019407c7783800040001940504700046fc01030100065f570d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878661500000000004459443d537563636573732100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010009e82b0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979000894000000000338ba0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790020940a03516080825457290502194200448892980691312079088572f50004d4350d0a"));
+
+ verifyPosition(decoder, binary(
+ "7979007121000000000143757272656e7420706f736974696f6e214c61743a4e35342e3733393333322c4c6f6e3a4532352e3237333237302c436f757273653a3132362e35332c53706565643a302e303030302c4461746554696d653a323031372d30352d3236202031303a32373a3437000bbee30d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979003F940D110315102A202141494F494C2C30322C3030382E3239302C3032392E3630302C3531394A2C303430302C3030382E3433302C302C30302C4142001678EA0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "79790039940d2141494f494c2c30322c3030322e3732302c3032392e3530302c3532344a2c303130302c3030332e3430302c302c30302c393309ad72000d0a"));
+
+ verifyNull(decoder, binary(
+ "79790005840016BB1A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "797900089400000002e852d70d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979000794050000c9b63e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78783b18100c0f1201010195271784005ab63617840002fa47178400ff8f4817840019f3491784005ab54b178400ff8e4c17840019f24cff0002012287c80d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878251610051b0f1c34c5022515d504b5dcd20738080902d4022bdf009cba5006640201006759680d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787866150000000000416c726561647920696e20746865207374617465206f66206675656c20737570706c7920746f20726573756d652c74686520636f6d6d616e64206973206e6f742072756e6e696e672100000000000000000000000000000000000001001981e50d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78782d152500000000437574206f666620746865206675656c20737570706c793a2053756363657373210002013b898a0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "787829152100000000526573746f7265206675656c20737570706c793a2053756363657373210002014077ce0d0a"));
+
+ verifyNull(decoder, binary(
+ "78780D01012345678901234500018CDD0D0A"));
+
+ verifyNull(decoder, binary(
+ "78780d0103534190360660610003c3df0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780a13440604000201baaf540d0a"));
+
+ verifyAttributes(decoder, binary(
+ "787825160F0C1D0A2B21C8027AC8040C46581000146F0901CC00287D001F714804040301001C84CF0D0A"));
+
+ verifyPosition(decoder, binary(
+ "78781f120f0a140e150bc505e51e780293a9e800540000f601006e0055da00035f240d0a"),
+ position("2015-10-20 14:21:11.000", true, 54.94535, 24.01762));
+
+ verifyPosition(decoder, binary(
+ "787823120f081b121d37cb01c8e2cc08afd3c020d50201940701d600a1190041ee100576d1470d0a"));
+
+ verifyPosition(decoder, binary(
+ "78781F120B081D112E10CC027AC7EB0C46584900148F01CC00287D001FB8000380810D0A"));
+
+ verifyPosition(decoder, binary(
+ "787819100B031A0B1B31CC027AC7FD0C4657BF0115210001001CC6070D0A"));
+
+ verifyPosition(decoder, binary(
+ "787821120C010C0F151FCF027AC8840C4657EC00140001CC00287D001F720001000F53A00D0A"));
+
+ verifyPosition(decoder, binary(
+ "787825160B051B093523CF027AC8360C4657B30014000901CC00266A001E1740050400020008D7B10D0A"));
+
+ verifyPosition(decoder, binary(
+ "787819100e010903230ec803ae32a60653cded00180000020072feb70d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878471e0e03110b0511c501c664fd074db73f0218a602e003433a002fed40433a0056e14e433a0056104e433a0056fd53433a002eed55433a007e4b57433a002ee25aff00020120f6720d0a"));
+
+ verifyNull(decoder, binary(
+ "7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234002a3b240d0a7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234002d4f9b0d0a7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234003084ff0d0a"));
+
+ verifyNull(decoder, binary(
+ "78788b818300000000534545464e2626004f04220045042626262b37393035343031353534362626262626260410041b0415041a04210415041926262b373930363433333031313526260410043d044f26262b373936303437383430363426260412043e0432043026262b373932383834373738383126262626262626262626262626262626232300020022155d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822220e0914160f07c9021a362805090a7800d8b802d402c30e00a98a0105010213f4bb0d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010864717003664467100f190a0002c6d20d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010123456789012345100B3201000171930D0A"));
+
+ verifyNull(decoder, binary(
+ "78780d1f000000000000000200b196a20d0a"));
+
+ verifyPosition(decoder, binary(
+ "78781f12110819110216d402f250340828924055d4c801944600d300c09501429c830d0a"));
+
+ verifyPosition(decoder, binary(
+ "78782516110819110208d402f264dc08289a4058d4c70901944600d300c0954606040600014057e90d0a"));
+
+ verifyNull(decoder, binary(
+ "78780d010359339075005244340d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a"));
+
+ verifyNull(decoder, binary(
+ "787801080d0a"));
+
+ verifyNull(decoder, binary(
+ "78782E2A0F0C1D071139CA027AC8000C4658000014D83132353230313335333231373730373900000000000001002A6ECE0D0A"));
+
+ verifyNull(decoder, binary(
+ "7878058A000688290D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java
new file mode 100644
index 000000000..aceaef434
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class Gt06ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ Gt06ProtocolEncoder encoder = new Gt06ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("787812800c0000000052656c61792c312300009dee0d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java
new file mode 100644
index 000000000..cb792ba09
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt30ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt30ProtocolDecoder decoder = new Gt30ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$005D3037811014 9955102834.000,A,3802.8629,N,02349.7163,E,0.00,,060117,,*13|1.3|26225BD"));
+
+ verifyPosition(decoder, text(
+ "$$005E3037811014 9999\u0003121909.000,A,3802.9133,N,02349.9354,E,0.00,,060117,,*18|1.8|264518B"));
+
+ verifyPosition(decoder, text(
+ "$$00633037811014 9999\u0002121901.000,A,3802.9137,N,02349.9334,E,2.86,18.16,060117,,*3E|1.8|262D752"));
+
+ verifyPosition(decoder, text(
+ "$$005E3037811014 9999\u0001121849.000,A,3802.9094,N,02349.9384,E,0.00,,060117,,*1C|1.2|2683812"));
+
+ verifyPosition(decoder, text(
+ "$$005B3037811124 9955161049.000,A,3802.9474,N,02241.1897,E,0.00,,021115,,*15|2.9|5A639"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
new file mode 100644
index 000000000..bdb2ff37b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
@@ -0,0 +1,71 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class H02FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeShort() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("2a48512c3335353438383032303131333931312c56312c3031323934352c412c353233312e37393238332c4e2c30313332342e31303731382c452c302e30352c302c3137303231372c464646464646464623"),
+ decoder.decode(null, null, binary("2a48512c3335353438383032303131333931312c56312c3031323934352c412c353233312e37393238332c4e2c30313332342e31303731382c452c302e30352c302c3137303231372c4646464646464646230d0a")));
+
+ assertEquals(
+ binary("2441060116601245431311165035313006004318210e000000fffffbffff0024"),
+ decoder.decode(null, null, binary("2441060116601245431311165035313006004318210e000000fffffbffff0024")));
+
+ assertEquals(
+ binary("2441060116601245431311165035313006004318210e000000fffffbffff0024"),
+ decoder.decode(null, null, binary("2441060116601245431311165035313006004318210e000000fffffbffff00242a48512c343130363031313636302c56312c3132343535322c412c353033352e333132392c4e2c30303433312e383231312c452c3030302e32302c3030302c3133313131362c464646464642464623")));
+
+ assertEquals(
+ binary("2a48512c3335333538383036303031353536382c56312c3139333530352c412c3830392e303031302c532c333435342e383939372c572c302e30302c302e30302c3239313031332c65666666666266662c3030303264342c3030303030622c3030353338352c3030353261612c323523"),
+ decoder.decode(null, null, binary("2a48512c3335333538383036303031353536382c56312c3139333530352c412c3830392e303031302c532c333435342e383939372c572c302e30302c302e30302c3239313031332c65666666666266662c3030303264342c3030303030622c3030353338352c3030353261612c323523")));
+
+ assertEquals(
+ binary("24430025645511183817091319355128000465632432000100ffe7fbffff0000"),
+ decoder.decode(null, null, binary("24430025645511183817091319355128000465632432000100ffe7fbffff0000")));
+
+ }
+
+ @Test
+ public void testDecodeLong() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f"),
+ decoder.decode(null, null, binary("24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f")));
+
+ }
+
+ @Test
+ public void testDecodeAlternative() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("2a48512c343230363131393133302c4e42522c3130323430332c3233382c312c302c372c313131312c323236342c36332c313131312c323236352c35382c313131312c323236362c35302c313131312c333133352c33372c313131312c3630352c33332c313131312c343932302c33302c313131312c3630372c32382c3131303131372c46464646444646462c3623"),
+ decoder.decode(null, null, binary("2a48512c343230363131393133302c4e42522c3130323430332c3233382c312c302c372c313131312c323236342c36332c313131312c323236352c35382c313131312c323236362c35302c313131312c333133352c33372c313131312c3630352c33332c313131312c343932302c33302c313131312c3630372c32382c3131303131372c46464646444646462c3623")));
+
+ assertEquals(
+ binary("2442061191301024031101175540227006012321670c000095fffffbffff001f00000001f800ee010000000032"),
+ decoder.decode(null, null, binary("2442061191301024031101175540227006012321670c000095fffffbffff001f00000001f800ee010000000032")));
+
+ assertEquals(
+ binary("5800009814991024031101175540227006012321670c000095fffffbffff0033"),
+ decoder.decode(null, null, binary("5800009814991024031101175540227006012321670c000095fffffbffff0033")));
+
+ assertEquals(
+ binary("2a48512c343230363131393133302c4e42522c3130323431362c3233382c312c302c372c313131312c323236342c35332c313131312c323236352c36302c313131312c323236362c34342c313131312c333133352c34332c313131312c3630352c33392c313131312c343932302c32392c313131312c3630372c32342c3131303131372c46464646464246462c3623"),
+ decoder.decode(null, null, binary("2a48512c343230363131393133302c4e42522c3130323431362c3233382c312c302c372c313131312c323236342c35332c313131312c323236352c36302c313131312c323236362c34342c313131312c333133352c34332c313131312c3630352c33392c313131312c343932302c32392c313131312c3630372c32342c3131303131372c46464646464246462c3623")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
new file mode 100644
index 000000000..4a5eadf52
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -0,0 +1,261 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class H02ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
+
+ verifyNotNull(decoder, buffer(
+ "*hq,356327081001239,VP1,V,470,002,92,3565,0Y92,19433,30Y92,1340,29#"));
+
+ verifyPosition(decoder, binary(
+ "2435248308419329301047591808172627335900074412294E024138FEFFFFFFFF01120064BA73005ECC"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,054048,A,2828.2297,N,07733.4332,E,000.5,047,4,080918,EEE7FBDF,4261193,0#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505221264507,V2,100220,0,5238.26259,N,00507.33983,E,0.25,0,280917,FFFFFFFF,cc,28, db,d75b#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,,V1,173212,A,2225.78879,S,02829.19021,E,0.00,0,290418,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505221264507,VI1,075146,0,5238.25900,N,00507.33429,E,0.54,0,250917,FFFFFFFF,cc,28, db,d75b#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,353505510948929,V1,,V,,N,,E,0.00,0,,FFFFF7FF,f0,a,11a0,c0c6#"));
+
+ verifyPosition(decoder, buffer(
+ "*hq,356327080425330,VP1,A,2702.7245,S,15251.9311,E,0.48,0.0000,080917#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4209951296,V19,214452,A,5201.0178,N,01830.5029,E,000.00,000,200417,,195.63.13.195,89480610500392633029,BFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*hq,356327080425330,VP1,A,2702.7215,S,15251.9309,E,0.62,0.0000,050917#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,356327080425330,XT,1,100#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,353111080001055,V3,044855,28403,01,001450,011473,158,-62,0292,0,X,030817,FFFFFBFF#"));
+
+ verifyPosition(decoder, binary(
+ "2442091341332059572807175137358006000183640e000000fffffbfdff001f080000000000ea1e0000000021"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,4109198974,#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1700086468,LINK,180902,15,0,84,0,0,240517,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020882405,V3,095426,74001,01,010278,045142,128,-92,02DE,0,X,090517,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020882405,V3,095426,74001,04,010278,045142,128,-92,010278,026311,125,,010278,026582,125,,010278,028322,119,,02DD,0,X,090517,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4109179024,V19,181519,V,3853.2587,S,06205.9175,W,000.00,000,090217,,5492932630888,8954315265044716555?,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,8161289587,V1,181933,A,5444.3994,N,02516.3844,E,000.05,000,090317,FFFFBBFF,246,03,00002,41565#"));
+
+ verifyPosition(decoder, binary(
+ "2421305109380127171003170520046500100286297e003085ffffdfffff03440069129344006400001151415a20"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,2130510938,V1,012632,A,0520.0663,N,10028.6324,E,0.286,023,100317,FFFFDFFF,69129336,0,100.0,18,5141,5A20#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201812,A,2608.9437,N,08016.2521,W,000.80,000,150317,EFE7F9FF,310,260,0,0,6#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201844,A,2608.9437,N,08016.2521,W,000.80,000,150317,FFFFF9FF,310,260,0,0,6#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4109179024,V19,103732,V,3853.2770,S,06205.8678,W,000.00,000,100217,,5492932630888,8954314165044716555?,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,4109179024,NBR,103732,722,310,0,6,8106,32010,23,8101,22007,25,8106,12010,23,8106,22105,22,8101,22012,16,8106,42010,5,100217,FFFFFBFF,5#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020930796,V3,002339,62160,06,024852,035421,148,0,024852,035425,143,,022251,036482,137,,024852,000335,133,,024852,031751,133,,024852,035423,133,,02A1,0,X,010104,EFE7FBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4106012736,V1,224434,A,1827.3855,N,06705.7577,W,000.00,000,100117,FFFFFBFF,310,260,49101,1753,5#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,4208150188,NBR,210249,260,6,0,7,1014,50675,37,1014,50633,27,1014,17933,18,1014,17231,15,1014,50632,12,1014,13211,11,1014,17031,10,281216,FFFFFBFF,2#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068812,NBR,141335,262,02,255,6,431,17003,26,431,11101,13,431,6353,13,431,22172,13,431,11093,13,431,60861,10,151216,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588020068342,V1,084436,A,3257.01525,N,00655.03865,W,57.78,40,011216,FFFBFFFF,25c,a, 154,b04c#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,356803210091319,BS,,2d4,a,1b63,1969,26,1b63,10b2,31,0,0,25,,ffffffff,60#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1400046168,NBR,160169,460,0,1,4,9338,3692,150,9338,3691,145,9338,3690,140,9338,3692,139,180813,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,NBR,120156,262,03,255,6,802,54702,46,802,5032,37,802,54782,30,802,5052,28,802,54712,12,802,5042,12,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,NBR,110326,262,03,255,6,802,23152,23,812,49449,14,802,35382,13,802,35402,11,812,56622,09,802,23132,04,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,LINK,112137,20,8,67,0,0,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020533263,V3,121536,65501,04,000152,014001,156,-64,000161,010642,138,,000152,014003,129,,000152,013973,126,,02E4,0,X,071116,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4209917484,V19,093043,V,5052.9749,N,00426.4322,E,000.00,000,130916,,0032475874141,8944538530000543700F,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220873067,V1,,V,4605.75732,N,01430.73863,E,0.00,0,,FFFFFFEF,125,194, 64,d3#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210051415,V1,164549,A,0956.3869,N,08406.7068,W,000.00,000,221215,FFFFFBFF,712,01,0,0,6#"),
+ position("2015-12-22 16:45:49.000", true, 9.93978, -84.11178));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1451316451,NBR,112315,724,10,2,2,215,2135,123,215,2131,121,011215,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316485,V1,121557,A,-23-3.3408,S,-48-2.8926,W,0.1,158,241115,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316485,V1,121557,A,-23-35.3408,S,-48-2.8926,W,0.1,158,241115,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,355488020119695,V1,050418,,2827.61232,N,07703.84822,E,0.00,0,031015,FFFEFBFF#"),
+ position("2015-10-03 05:04:18.000", false, 28.46021, 77.06414));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316409,V1,030149,A,-23-29.0095,S,-46-51.5852,W,2.4,065,070315,FFFFFFFF#"),
+ position("2015-03-07 03:01:49.000", true, -23.48349, -46.85975));
+
+ verifyNull(decoder, buffer(
+ "*HQ,353588020068342,V1,000000,V,0.0000,0,0.0000,0,0.00,0.00,000000,ffffffff,000106,000002,000203,004c87,16#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,3800008786,V1,062507,V,3048.2437,N,03058.5617,E,000.00,000,250413,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4300256455,V1,111817,A,1935.5128,N,04656.3243,E,0.00,100,170913,FFE7FBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,123456789012345,V1,155850,A,5214.5346,N,2117.4683,E,0.00,270.90,131012,ffffffff,000000,000000,000000,000000#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588010001689,V1,221116,A,1548.8220,S,4753.1679,W,0.00,0.00,300413,ffffffff,0002d4,000004,0001cd,000047#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,354188045498669,V1,195200,A,701.8915,S,3450.3399,W,0.00,205.70,050213,ffffffff,000243,000000,000000#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V1,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#\r\n"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S17,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S14,100,10,1,3,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S20,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S20,DONE,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,F7FFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,R8,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S23,165.165.33.250:8800,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S24,thit.gd,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S1,OK,pass_word,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFD#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588020068342,V1,062840,A,5241.1249,N,954.9490,E,0.00,0.00,231013,ffffffff,000106,000002,000203,004c87,24#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220903211,V1,075228,A,5227.5039,N,01032.8443,E,0.00,0,231013,FFFBFFFF,106,14, 201,2173#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220903211,V1,140817,A,5239.3538,N,01003.5292,E,21.03,312,221013,FFFBFFFF,106,14, 203,1cd#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,356823035368767,V1,083618,A,0955.6392,N,07809.0796,E,0.00,0,070414,FFFBFFFF,194,3b5, 71,c9a9#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,8401016597,BASE,152609,0,0,0,0,211014,FFFFFFFF#"));
+
+ verifyPosition(decoder, binary(
+ "2441060116601245431311165035313006004318210e000000fffffbffff0024"));
+
+ verifyPosition(decoder, binary(
+ "24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f"),
+ position("2015-08-31 21:53:21.000", true, 4.69898, -74.06971));
+
+ verifyPosition(decoder, binary(
+ "2427051711092133391406135002584900014337822e000000ffffffffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2427051711092134091406135002584900014337822e000000ffffffffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2410307310010503162209022212874500113466574C014028fffffbffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2441090013450831250401145047888000008554650e000000fffff9ffff001006000000000106020299109c01"));
+
+ verifyPosition(decoder, binary(
+ "24270517030820321418041423307879000463213792000056fffff9ffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2441091144271222470112142233983006114026520E000000FFFFFBFFFF0014060000000001CC00262B0F170A"));
+
+ verifyPosition(decoder, binary(
+ "24971305007205201916101533335008000073206976000000effffbffff000252776566060000000000000000000049"));
+
+ }
+
+ @Test
+ public void testDecodeStatus() throws Exception {
+
+ H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
+
+ verifyAttribute(decoder, buffer(
+ "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#"),
+ Position.KEY_STATUS, 0xFFFFFFFFL);
+
+ verifyAttribute(decoder, binary(
+ "2441091144271222470112142233983006114026520E000000FFFFFBFFFF0014060000000001CC00262B0F170A"),
+ Position.KEY_STATUS, 0xFFFFFBFFL);
+
+ verifyAttribute(decoder, buffer(
+ "*HQ,4210051415,V1,164549,A,0956.3869,N,08406.7068,W,000.00,000,221215,FFFFFBFF,712,01,0,0,6#"),
+ Position.KEY_STATUS, 0xFFFFFBFFL);
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
new file mode 100644
index 000000000..a7ce3fc7e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
@@ -0,0 +1,72 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+
+import static org.junit.Assert.assertEquals;
+
+public class H02ProtocolEncoderTest extends ProtocolTest {
+
+ private H02ProtocolEncoder encoder = new H02ProtocolEncoder();
+ private Date time = Date.from(
+ LocalDateTime.of(LocalDate.now(), LocalTime.of(1, 2, 3)).atZone(ZoneOffset.systemDefault()).toInstant());
+
+ @Test
+ public void testAlarmArmEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_ARM);
+
+ assertEquals("*HQ,123456789012345,SCF,010203,0,0#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testAlarmDisarmEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_DISARM);
+
+ assertEquals("*HQ,123456789012345,SCF,010203,1,1#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testEngineStopEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ assertEquals("*HQ,123456789012345,S20,010203,1,1#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testEngineResumeEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_RESUME);
+
+ assertEquals("*HQ,123456789012345,S20,010203,1,0#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testPositionPeriodicEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.set(Command.KEY_FREQUENCY, 10);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+
+ assertEquals("*HQ,123456789012345,S71,010203,22,10#", encoder.encodeCommand(command, time));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java
new file mode 100644
index 000000000..6d879daf2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HaicomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HaicomProtocolDecoder decoder = new HaicomProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GPRS012497007097169,T100001,150618,230031,5402267400332464,0004,2014,000001,,,1,00#V040*"),
+ position("2015-06-18 23:00:31.000", true, 40.37790, -3.54107));
+
+ verifyPosition(decoder, text(
+ "$GPRS123456789012345,602S19A,100915,063515,7240649312041079,0019,3156,111000,10004,0000,11111,00LH#V037"));
+
+ verifyPosition(decoder, text(
+ "$GPRS123456789012345,T100001,141112,090751,7240649312041079,0002,1530,000001,,,1,00#V039*"));
+
+ verifyPosition(decoder, text(
+ "$GPRS012497007101250,T100001,141231,152235,7503733600305643,0000,2285,000001,,,1,00#V041*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java
new file mode 100644
index 000000000..9acf2ce87
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HomtecsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HomtecsProtocolDecoder decoder = new HomtecsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "MDS0001_R6d1821f7,170323,143601.00,04,,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "MDS0001_R6d1821f7,170323,143621.00,06,5105.29914,N,11400.52675,W,0.223,198.41,1,2.12,1042.3"));
+
+ verifyPosition(decoder, text(
+ "strommabus939_R01272028,160217,191003.00,06,5540.12292,N,01237.49814,E,0.391,,1,1.27,1.2"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
new file mode 100644
index 000000000..0f24d4b5c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class HuaShengFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuaShengFrameDecoder decoder = new HuaShengFrameDecoder();
+
+ assertEquals(
+ binary("c0010c00120060000000000004000600010100c0"),
+ decoder.decode(null, null, binary("c0010c00120060000000000004000600010100c0")));
+
+ assertEquals(
+ binary("c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0"),
+ decoder.decode(null, null, binary("c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0")));
+
+ assertEquals(
+ binary("c00000003faa0000000000003ea5a5005a3f00c000000031363037303530373132353700e6d186ffcc7a25002201160010000000010015000000000000000000c0"),
+ decoder.decode(null, null, binary("c00000003faa0000000000003ea5a5005a3f00dbdc00000031363037303530373132353700e6d186ffcc7a25002201160010000000010015000000000000000000c0")));
+
+ assertEquals(
+ binary("C000000041AA00000000000030C000000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0"),
+ decoder.decode(null, null, binary("C000000041AA00000000000030DBDC00000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
new file mode 100644
index 000000000..319d34e4c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HuaShengProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuaShengProtocolDecoder decoder = new HuaShengProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "c0010c003c0002000000000044020010a0014f42445f3347315f56322e320013a0043335353835353035313032303536360006a08700000006a0a105c9c0"));
+
+ verifyNull(decoder, binary(
+ "c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa0000000000000f8000000031363130323030373236333600e6d4f9ffcc78c700000022003600000001001500000000000000000000059bffffffffff0005000a040300000253c0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa000000000000098000000031363130303732323236343700e6d4efffcc789f000000000026000000010015000000000000000000000488ffffffffff0005000a10060000008dc0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa000000000000098000000031363130303732323236343700e6d4efffcc789f000000000026000000010015000000000000000000000488ffffffffff0005000a10060000008dc0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa00000000000005c400000131363037303630323537303800e6c82effcc7cb0003900a30089000000010015000000000000000000f20559ff577ce3980005000a060500000087c0"));
+
+ verifyNull(decoder, binary(
+ "c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0"));
+
+ verifyNull(decoder, binary(
+ "c0010c00120060000000000004000600010100c0"));
+
+ verifyNull(decoder, binary(
+ "C00000007EAA020000000000010001001047315F48312E305F56312E3000030013383632393530303238353334333036000400144C342D56374C673979497A7A2D724A6D0005000501000600084341524400070008434152440008000500000900183839383630303530313931343436313130393134000A0009434D4E4554C0"));
+
+ verifyPosition(decoder, binary(
+ "C000000041AA00000000000030C000000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java
new file mode 100644
index 000000000..2d3937903
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class HuabaoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuabaoFrameDecoder decoder = new HuabaoFrameDecoder();
+
+ assertEquals(
+ binary("7e307e087d557e"),
+ decoder.decode(null, null, binary("7e307d02087d01557e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
new file mode 100644
index 000000000..d4ae3b50c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -0,0 +1,69 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HuabaoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuabaoProtocolDecoder decoder = new HuabaoProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
+
+ verifyPosition(decoder, binary(
+ "7e020000400303000002280042000000000000000301618ab406c31ec800000000000518092116145701040000047830011031010aeb16000c00b28986011780108622216500060089ffffffffc37e"));
+
+ verifyPosition(decoder, binary(
+ "7E0200002A013833501744001900000000000000C201597FA806CC01580080000000081508180721210104000002F502020000030200009F7E"));
+
+ verifyPositions(decoder, binary(
+ "7E0704014301356777777707F5000801002600000000000000030159741206CBFD5001720000000116052709062401040000001D03020000002600000000000000030159746606CBFD50016B0000000116052709065301040000001D0302000000260000000000000003015975E406CBFE58013F0000000116052709072701040000001D0302000000260000000000000003015976DE06CBFF10012E0000000116052709075601040000001D0302000000260000000000000003015976BC06CBFED0012D0000000116052709083001040000001D0302000000260000000000000003015976FE06CBFEC001080000000116052709090001040000001D0302000000260000000000000003015976FE06CBFE7800FC0000000116052709093301040000001D0302000000260000000000000003015977A606CBFF3001080000000116052709100201040000001D030200001F7E"));
+
+ verifyAttributes(decoder, binary(
+ "7E0200002F01357888888800B60000000000000003015972B406CBF6B802230000000116061317571301040000000003020000EB0700050001016708B37E"));
+
+ verifyPositions(decoder, binary(
+ "7E070400F30303000002450064000401003A000000000000000301618AC606C31F20000000000029180514202847010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000000000000000301618AE806C31EB800000000009F180514202917010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000000000000000301618AE806C31EB800000000009F180514202947010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000001000000080301618AE806C31EB800000000009F180514203006010400000000EB16000C00B28986061708003732585700060089FFFFFFFED77E"));
+
+ verifyPosition(decoder, binary(
+ "7e02000054093037612710000700000000000000010223aca000dc9dd800000000000017121417122133362a4d30302c34352c31313336393042383030313233303026303030303030303030303030263132333435363738393031323334353623897e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002c00160128561400020000000000040001005de1f7065c6cef00000000000017011710044201040000a9002a02000030011b3101030c7e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002c00160128561400030000000000040007005de13c065c6cdb00160000000017011710054201040000a9002a02000030011b310104e47e"));
+
+ verifyNull(decoder, binary(
+ "7e0100002d0141305678720024002c012f373031313142534a2d41362d424400000000000000000000003035363738373201d4c14238383838386d7e"));
+
+ verifyNull(decoder, binary(
+ "7E0100002D013511221122000500000000373031303748422D52303347424400000000000000000000003233363631303402CBD5424136383630387E"));
+
+ verifyNull(decoder, binary(
+ "7e0100002d007089994489002800000000000000000048422d523033474244000000000000000000000031393036373531024142433030303030d17e"));
+
+ verifyNull(decoder, binary(
+ "7E0102000E013511221122000661757468656E7469636174696F6E3F7E"));
+
+ verifyPosition(decoder, binary(
+ "7E02000032013511221122000700000000000C000301578CC006CA3A5C00470000000014072317201501040000000030011631010BD07E"));
+
+ verifyNull(decoder, binary(
+ "7E010200100940278494700084323031313131303831313333323139369F7E"));
+
+ verifyNull(decoder, binary(
+ "7e010000190940278494700012000000000000000000000000000000000000094027849470000a7e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002e094027587492000a000000010000000a03083db7001329f3000000140000130412164952010400000012360a0002341502cb0c20085c107e"));
+
+ verifyPosition(decoder, binary(
+ "7e020000220014012499170007000000000000400e012af16f02cbd2ba000000000000150101194257010400000077a97e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
new file mode 100644
index 000000000..771e6d28c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class HuabaoProtocolEncoderTest extends ProtocolTest {
+
+ @Ignore
+ @Test
+ public void testEncode() throws Exception {
+
+ HuabaoProtocolEncoder encoder = new HuabaoProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("7e81050001080201000027001ff0467e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java
new file mode 100644
index 000000000..f0697f423
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HunterProProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HunterProProtocolDecoder decoder = new HunterProProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">0002<$GPRMC,170559.000,A,0328.3045,N,07630.0735,W,0.73,266.16,200816,,,A77, s000078015180\",0MD"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java
new file mode 100644
index 000000000..d211d80ce
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class IdplProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ IdplProtocolDecoder decoder = new IdplProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*ID1,863071011086474,210314,153218,A,1831.4577,N,07351.1433,E,0.79,240.64,9,20,A,1,4.20,0,1,01,1,0,0,A01,R,935D#"),
+ position("2014-03-21 15:32:18.000", true, 18.524295, 73.852388333333));
+
+ verifyPosition(decoder, text(
+ "*ID1,863071011086474,210314,162752,A,1831.4412,N,07351.0983,E,0.04,213.84,9,25,A,1,4.20,0,1,01,1,0,0,A01,L,EA01#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
new file mode 100644
index 000000000..00b7de094
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
@@ -0,0 +1,64 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class IntellitracProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ IntellitracProtocolDecoder decoder = new IntellitracProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$OK:TRACKING"));
+
+ verifyPosition(decoder, text(
+ "101000001,20100304075545,121.64547,25.06200,0,0,61,7,2,1,0,0.046,0.000,20100304075546,0"),
+ position("2010-03-04 07:55:45.000", true, 25.06200, 121.64547));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,133,7,0,11,15,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,-133,7,0,11,15,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1001070919,20130405084206,37.903730,48.011377,0,0,235,10,2,2,0,20.211,0.153"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217144230,121.646102,25.061398,0,0,139,0,0,0,0,0.093,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000004,20050513153524,121.646075,25.063675,0,166,50,6,1,0,0,0.118,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000004,20050513154001,121.646075,25.063675,0,166,55,7,1,0,0,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,0,7,0,11,15"));
+
+ verifyPosition(decoder, text(
+ "12345,1010000002,20030217132813,121.646060,25.061725,20,157,0,7,0,11,15"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217144230,121.646102,25.061398,0,0,0,7,2,0,0"));
+
+ verifyPosition(decoder, text(
+ "$RP:12345,1010000002,20030217144230,121.646102,25.061398,0,0,0,7,2,0,0"));
+
+ verifyPosition(decoder, text(
+ "1010000001,20030105092129,121.651598,25.052325,0,0,33,0,1,0,0"));
+
+ verifyPosition(decoder, text(
+ "1010000001,20030105092129,-121.651598,-25.052325,0,0,33,0,1,0,0"));
+
+ verifyPosition(decoder, text(
+ "1015210962,20131010144712,-77.070037,-12.097935,0,0,77,7,2,2,0,0,139446.8,2095,20131010144712,,0.103,0.000"));
+
+ verifyPosition(decoder, text(
+ "1003269480,20131126100258,10.32989,49.93836,0,304,217,6,2,0,0,0.000,0.000,20131126100258,0,0,0,-40,0,0,-273,0,0,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
new file mode 100644
index 000000000..7523e29a0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ItsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ItsProtocolDecoder decoder = new ItsProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.8AIS,EA,01,L,869867036341099,,1,11032019,231048,19.25166667,N,73.04615167,E,0.0,230.21,17,12.8,0.80,0.80,airtel,0,1,13.5,4.2,0,O,22,404,90,0CC9,EBC8,19,0CC9,8992,16,0CC9,AB49,15,0CC9,AB44,14,0CC9,F03C,0000,00,002080,C9FBBF99"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.7AIS,NR,01,L,869867036345389,,1,25022019,051716,25.12891000,N,75.85587833,E,7.6,350.00,14,284.8,1.00,1.00,AIRTEL,1,1,28.0,4.2,0,C,13,404,70,4E3B,3C84,11,4E3B,39B8,08,4E3B,3965,07,4E3B,48B5,07,4E3B,3C85,0000,00,000551,71978C6B"));
+
+ verifyPosition(decoder, text(
+ "$,03,XYZ123,0.0.1,TA,16,L,869867035297185,MH12AB1234,1,20,02,2019,10,59,13,023.482630,N,086.399673,E,000.1,015.19,21,212.3,01.12,00.58,NOSERV,0,1,00.0,4.6,1,C,11,404,75,082a,db3a,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,01,000013,01dbd51f,"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.7AIS,NR,01,L,869867036345389,,1,25022019,051716,25.12891000,N,75.85587833,E,7.6,350.00,14,284.8,1.00,1.00,AIRTEL,1,1,28.0,4.2,0,C,13,404,70,4E3B,3C84,11,4E3B,39B8,08,4E3B,3965,07,4E3B,48B5,07,4E3B,3C85,0000,00,000551,71978C6B"));
+
+ verifyPosition(decoder, text(
+ "$,1,CHVTS,CHVTS1.0,DT,16,L,861359039868243,861359039868243,1,05022019,071225,19.965062,N,73.736088,E,0,050,03,0632,6.67,6.75,Idea Cel,1,1,23.96,4.0,0,W,28,404,004,4e2b,49e,4e2bea86727ab3d6704e2bea7714e2be9d72,0000,00,001133,232"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,TA,16,L,861359034100626,MH12AB1234,1,12,11,2018,08,53,08,018.489645,N,073.855972,E,000.0,220.04,12,593.0,01.13,00.75,AIRTEL,1,1,00.0,4.1,1,C,18,404,90,0c23,781a,5169,0c23,-093,0000,0000,0000,0000,0000,0000,0000,0000,0000,1000,01,000006,f906c65c,"));
+
+ verifyPosition(decoder, text(
+ "$,A,MFR,7.0,NR,01,L,869026034780985,PJ09BU1234,1,12112018,121953,12.756974,N,077.800025,E,000.0,318.03,15,794.0,001.3,000.7,TAMIL NAD,0,1,13.08,04.13,0,O,22,404,80,0919,71C1,0919,7168,19,0919,71c3,17,0919,71c2,11,0919,7167,09,0011,00,000173,D8,000000.00,,,"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,TA,16,L,861359034100626,MH12AB1234,1,14,10,2018,04,50,52,018.489624,N,073.855980,E,000.0,039.86,13,584.1,01.11,00.75,AIRTEL,1,1,00.0,4.1,1,C,15,404,90,0c23,781a,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,1000,01,000005,13b75499,"));
+
+ verifyNull(decoder, text(
+ "$,01,XYZ123,0.0.1,861359034137271,MH12AB1234,"));
+
+ verifyNull(decoder, text(
+ "$,02,XYZ123,0.0.1,861359034137271,100,30,00.0,00005,00600,1000,01,00.1,00.0,"));
+
+ verifyPosition(decoder, text(
+ "$,EPB,EMR,861359034100626,SP,00,00,0000,00,00,00,V,000.000000,N,000.000000,E,000.0,000.0,000.00,N,MH12AB1234,0000000000000,d34679e1,"));
+
+ verifyPosition(decoder, text(
+ "$,03,XYZ123,0.0.1,TA,16,L,861359034137271,MH12AB1234,0,00,00,0000,00,00,00,000.000000,N,000.000000,E,000.0,000.00,00,000.0,00.00,00.00,IDEAIN,1,1,00.0,4.0,1,O,16,404,22,2797,11b7,11b9,2797,-087,11b8,2797,-093,11b4,2797,-106,0000,0000,0000,1000,01,000032,8173e058,"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,BR,06,L,861359034137271,MH12AB1234,0,00,00,0000,00,00,00,000.000000,N,000.000000,E,000.0,000.00,00,000.0,00.00,00.00,IDEAIN,1,1,00.0,3.8,1,O,17,404,22,2797,11b7,11b9,2797,-093,11b8,2797,-098,0000,0000,0000,0000,0000,0000,1000,00,000006,abd26284,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
new file mode 100644
index 000000000..b0b416891
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
@@ -0,0 +1,60 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Ivt401ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Ivt401ProtocolDecoder decoder = new Ivt401ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(TLA,356917051007891,190118,090211,+16.986606,+82.242416,0,66,4,13,1,5,000,00,0.0,11.59,8.30,37.77,0.0,1,1.02,0,0,208,0,0,0,0,000000000,0,0,0,0,0,0,0,1,8654604,5,9,114)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,862107032006249,230218,180500,+18.479728,+73.896339,30,0,944,13,1,5,111,11,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,865933030026336,250118,063827,+18.598098,+73.806518,0,79,0,1,1,5,1200,0,0.0,11.50,4.00,36,0,0,1.00,0,0,12702,202,0)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,865933030026336,250118,063838,+18.598110,+73.806518,0,334,0,1,1,5,1200,0,0.0,12.20,4.10,36,0,0,1.00,0,0,12704,0,0,0,0,0,0,0,0,0,0,0,0,1,0,3,203,0)"));
+
+ verifyNull(decoder, text(
+ "(TLB,356917050440515,0,0,+0.0,+0.0,0,0,0,0,0,0,000,00,0.00,12.21,7.95,26.57,0.0,1,1.12,0,0,0,1,90)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,094723,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.11,0.0,0,1.13,0,0,0,4,86)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,356917050440515,250118,094733,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.20,0.0,0,1.12,0,0,0,0,0,0,0,000000000,0,1,0,0,0,0,0,1,0,0,5,70)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,094733,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.20,0.0,0,1.13,0,0,0,6,87)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,103541,+17.015546,+54.080841,0,0,31,12,1,5,000,00,0.0,0.00,8.04,28.59,M+0+0+0+0+0,0,1.12,0,0,2,48,30)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220046,+21.134126,+74.798561,51,28,204,14,1,5,100,00,0.0,13.92,7.82,23.74,0.0,1,1.33,-9,0,429,4848,67)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220116,+21.137619,+74.800659,52,28,202,14,1,3,100,00,0.0,13.92,7.82,23.74,0.0,1,1.26,-23,0,445,4849,125)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ verifyPosition(decoder, text(
+ "(TLB,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLL,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java
new file mode 100644
index 000000000..8179a2bae
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class JpKorjarProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ JpKorjarProtocolDecoder decoder = new JpKorjarProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.247254N,021.013375E,0.00,1,F:4.18V,1 260 01 794B 3517,"));
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.895515N,021.949151E,6.30,212,F:3.94V,0 260 01 794B 3519,"));
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.895596N,021.949343E,12.46,087,L:2.18V,1 260 01 794B 3517,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
new file mode 100644
index 000000000..ae0948987
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Jt600FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Jt600FrameDecoder decoder = new Jt600FrameDecoder();
+
+ verifyFrame(
+ binary("24315011626912001b21111718095900000000000000000e0000005c000000000000000000"),
+ decoder.decode(null, null, binary("24315011626912001b21111718095900000000000000000e0000005c00000000000000000024315011626912001b22111708130400000000000000000e0000005a00000000000000000024315011626912001b22111708140400000000000000000e0000005a000000723e18a61b01")));
+
+ verifyFrame(
+ binary("2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf"),
+ decoder.decode(null, null, binary("2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf")));
+
+ verifyFrame(
+ binary("2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607"),
+ decoder.decode(null, null, binary("2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607")));
+
+ verifyFrame(
+ binary("28333132303832303032392C5730312C30323535332E333535352C452C323433382E303939372C532C412C3137313031322C3035333333392C302C382C32302C362C33312C352C32302C323029"),
+ decoder.decode(null, null, binary("28333132303832303032392C5730312C30323535332E333535352C452C323433382E303939372C532C412C3137313031322C3035333333392C302C382C32302C362C33312C352C32302C323029")));
+
+ verifyFrame(
+ binary("24312082002911001B171012053405243809970255335555000406140003EE2B91044D1F02"),
+ decoder.decode(null, null, binary("24312082002911001B171012053405243809970255335555000406140003EE2B91044D1F02")));
+
+ verifyFrame(
+ binary("28373536303430353535332c404a5429"),
+ decoder.decode(null, null, binary("28373536303430353535332c404a5429")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
new file mode 100644
index 000000000..2bb4b0fa3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -0,0 +1,108 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class Jt600ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Jt600ProtocolDecoder decoder = new Jt600ProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "2475801263981711002713061813333723501622090221558f012f0000002a070000000020c055b88552191f000f0f0f07"));
+
+ verifyPositions(decoder, binary(
+ "24408111888821001B09060908045322564025113242329F0598000001003F0000002D00AB"));
+
+ verifyPositions(decoder, binary(
+ "2475609213701711002701010000020200000000000000000e00000000000f000000000020c164cd7b00d516000f0f0f02"));
+
+ verifyPositions(decoder, binary(
+ "24657060730131001b13111710361906538525079524797f000000000000000003f300036c"));
+
+ verifyPositions(decoder, binary(
+ "24624090196121001b19071703493631277203074235752f295800005308010000768b0822"));
+
+ verifyPositions(decoder, binary(
+ "24624090196123019519071703412131285623074214252f10560000130801000076850819071703420631282832074215165f172c0000030801000076850919071703422131282792074216635f222e0000130801000076850919071703423631282808074218261f212a0000130801000076860819071703435131283678074222928f08350000930801000076860919071703440631283003074223174f19500000930801000076870819071703445131279872074224584f07380000930801000076870819071703453631280643074227091f1b220000530801000076880919071703455131281043074228216f0a260000530801000076880819071703460631281229074228988f0c260000530801000076880819071703462131281551074229954f1f220000530801000076880919071703463631281289074230503f114e0000530801000076880819071703465131281186074230884f094f0000530801000076880819071703470631280308074231240f17500000530801000076880619071703472131280358074231636f0b1d0000530801000076890821"));
+
+ verifyPositions(decoder, binary(
+ "2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf"));
+
+ verifyPositions(decoder, binary(
+ "2475201509260111002313101503464722331560113555309F00000000002D0500CB206800F064109326381A03"));
+
+ verifyPositions(decoder, binary(
+ "2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U01,040812,185302,T,22.564025,N,113.242329,E,5.21,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U02,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U03,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyNull(decoder, buffer(
+ "(3301210003,U04)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U06,1,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,0000000000011,10133,5173,22,100,1,300,100,10)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,220916,135251,T,9.552607,N,13.658292,W,0.31,0,9,0%,00001001000000,11012,10,27,0,0,33)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,010100,000024,F,0.000000,N,0.000000,E,0.00,0,0,100%,00000001000000,263,1,18,0,0,33)"));
+
+ verifyNull(decoder, buffer(
+ "(3460311327,@JT)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,11,220916,135643,T,9.552553,N,13.658265,W,0.61,0,9,100%,00000001000000,11012,10,30,0,0,126,0,30)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,10,220916,140619,T,9.552495,N,13.658227,W,0.43,0,7,0%,00101001000000,11012,10,0,0,0,126,0,30)"));
+
+ verifyPositions(decoder, binary(
+ "24311021600111001B16021105591022329862114046227B0598095080012327951435161F"),
+ position("2011-02-16 05:59:10.000", true, 22.54977, -114.07705));
+
+ verifyPositions(decoder, binary(
+ "24312082002911001B171012052831243810120255336425001907190003FD2B91044D1FA0"));
+
+ verifyPositions(decoder, binary(
+ "24312082002911001B1710120533052438099702553358450004061E0003EE000000000C00"));
+
+ verifyPositions(decoder, binary(
+ "24608111888821001B09060908045322564025113242329F0598000001003F0000002D00AB"));
+
+ verifyPosition(decoder, buffer(
+ "(3110312099,W01,11404.6204,E,2232.9961,N,A,040511,063736,4,7,100,4,17,1,1,company)"),
+ position("2011-05-04 06:37:36.000", true, 22.54994, 114.07701));
+
+ verifyPosition(decoder, buffer(
+ "(3120820029,W01,02553.3555,E,2438.0997,S,A,171012,053339,0,8,20,6,31,5,20,20)"));
+
+ verifyPosition(decoder, buffer(
+ "(3330104377,U01,010100,010228,F,00.000000,N,000.000000,E,0,0,0,0%,00001000000000,741,14,22,0,206)"));
+
+ verifyNull(decoder, buffer(
+ "(6221107674,2,U09,129,2,A,280513113036,E,02711.0500,S,1721.0876,A,030613171243,E,02756.7618,S,2300.0325,3491,538200,14400,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U02,040812,185302,T,00.000000,N,000.000000,E,0,0,0,0%,00000000000011,741,51,22,0,1,05)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U06,4,250916,133207,T,7.011013,N,25.060708,W,27.61,102,10,0%,00101011000000,0,1,0,448,0,126,1,30)"));
+
+ verifyPosition(decoder, buffer(
+ "(3551001012,U01,010100,000032,F,0.000000,N,0.000000,E,0.00,0,0,10%,00000000010000,15748,7923,23,0,0,3E)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
new file mode 100644
index 000000000..100d7492a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class Jt600ProtocolEncoderTest extends ProtocolTest {
+ Jt600ProtocolEncoder encoder = new Jt600ProtocolEncoder();
+ Command command = new Command();
+
+ @Test
+ public void testEngineStop() throws Exception {
+ command.setType(Command.TYPE_ENGINE_STOP);
+ assertEquals("(S07,0)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testEngineResume() throws Exception {
+ command.setType(Command.TYPE_ENGINE_RESUME);
+ assertEquals("(S07,1)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testSetTimezone() throws Exception {
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+4");
+ assertEquals("(S09,1,240)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testReboot() throws Exception {
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+ assertEquals("(S17)", encoder.encodeCommand(command));
+ }
+}
diff --git a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java
new file mode 100755
index 000000000..5596913c9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class KenjiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ KenjiProtocolDecoder decoder = new KenjiProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">C800000,M005004,O0000,I0002,D124057,A,S3137.2783,W05830.2978,T000.0,H254.3,Y240116,G06*17"),
+ position("2016-01-24 12:40:57.000", true, -31.62131, -58.50496));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
new file mode 100644
index 000000000..62b6070b6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class KhdProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ KhdProtocolDecoder decoder = new KhdProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "2929b1000605162935b80d"));
+
+ verifyPosition(decoder, binary(
+ "29298e006d1f29402d181117083846801193910365274500000000f80000227ffc3f00001e00500000000000060088000000220019ffc100000000000000000000000000000000007080002000000016ff893839323534303231303734313134323334333639000800233030302e30306e0d"));
+
+ verifyPosition(decoder, binary(
+ "2929a3002e1780c663170216203353003060811013839500000114f8000000ffff5000000a00000000000000060102003db70d"));
+
+ verifyPosition(decoder, binary(
+ "292980002805162935140108074727801129670365336900000103ffff000082fc0000001e78091b000000360d"));
+
+ verifyPosition(decoder, binary(
+ "29298100280A9F9538081228160131022394301140372500000330FF0000007FFC0F00001E000000000034290D"));
+
+ verifyPosition(decoder, binary(
+ "29298000280A81850A120310095750005281370061190800000232F848FFBBFFFF0000001E000000000000ED0D"));
+
+ verifyPosition(decoder, binary(
+ "29298E00280F80815A121218203116022318461140227000720262FB00077C7FBF5600001E3C3200000000850D"));
+
+ verifyPosition(decoder, binary(
+ "29298200230AA2CC391205030505220285947903109550008002078400000002000000000000750D"));
+
+ verifyPosition(decoder, binary(
+ "29298500081DD08C22120312174026026545710312541700000000F819C839FFFF1D00001E00500000003AF90D"));
+
+ verifyPosition(decoder, binary(
+ "292980002822836665140825142037045343770193879200000050ffff000082fc000004b0780b170000002a0d"));
+
+ verifyPosition(decoder, binary(
+ "292980002802425349120811032137022373011140211100000334FFFF000082FC0000001E780913000034DF0D"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java
new file mode 100644
index 000000000..ab858041a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class KhdProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ KhdProtocolEncoder encoder = new KhdProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("29293900065981972d5d0d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java
new file mode 100644
index 000000000..5ffa3d8d1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class L100FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ L100FrameDecoder decoder = new L100FrameDecoder();
+
+ verifyFrame(
+ binary("41544c2c4c2c3836383334353033383137313936332c4e2c3230313231382c3039333031362c412c3032352e3036373134342c4e2c3035352e3134343833332c452c3030302e302c4750532c333933392c3432342c30332c30303430352c303038383334"),
+ decoder.decode(null, null, binary("41544c2c4c2c3836383334353033383137313936332c4e2c3230313231382c3039333031362c412c3032352e3036373134342c4e2c3035352e3134343833332c452c3030302e302c4750532c333933392c3432342c30332c30303430352c30303838333440")));
+
+ verifyFrame(
+ binary("4c2c41544c2c3836363739353033303437373935322c30312c303033352c"),
+ decoder.decode(null, null, binary("4c2c41544c2c3836363739353033303437373935322c30312c303033352c2a28")));
+
+ verifyFrame(
+ binary("41544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735242c2330313130303131313030313031302c4e2e432c4e2e432c4e2e432c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c"),
+ decoder.decode(null, null, binary("200141544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735242c2330313130303131313030313031302c4e2e432c4e2e432c4e2e432c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c027a")));
+
+ verifyFrame(
+ binary("41544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735244c4f432c436f6e6e61756768742043697263757320c2a0436f6e6e617567687420506c61636520c2a04e65772044656c686920c2a044656c6869c2a0496e6469612c2330313130303130313130313031302c322e332c33352e36372c38302c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c"),
+ decoder.decode(null, null, binary("200341544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735244c4f432c436f6e6e61756768742043697263757320c2a0436f6e6e617567687420506c61636520c2a04e65772044656c686920c2a044656c6869c2a0496e6469612c2330313130303130313130313031302c322e332c33352e36372c38302c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c047a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java
new file mode 100644
index 000000000..04f586f7a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class L100ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ L100ProtocolDecoder decoder = new L100ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "ATL,NP,868004029750174,$GPRMC,062943,A,2533.6719,N,09154.3203,E,0,179,311218,,,*39,#01111011000000,0,0,0,934.82,27.13,4.0,25,405,755,15af,974b,0,0,0,ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL,L,868345038171963,N,201218,093016,A,025.067144,N,055.144833,E,000.0,GPS,3939,424,03,00405,008834"));
+
+ verifyPosition(decoder, text(
+ "N,111116,090031,A,028.123456,N,077.123456,E,000.0,GPS,4180,404,11,00159,064753"));
+
+ verifyAttributes(decoder, text(
+ "L,ATLOBD,866795030475584,03,7429,143344,130918,CAN,0101:00076100,0103:0200,0104:3C,0105:84,010A:XX,010B:19,010C:0F98,010D:22,010E:68,010F:5A,0110:XXXX,0111:28,011C:20,011F:XXXX,0121:0000,0122:XXXX,012F:XX,0162:XX,0132:XXXX,0133:61,0143:00A8,0145:0F,0146:XX,0147:30,0148:XX,0149:31,014A:18,014B:XX,014C:92,0151:XX,0131:00BB,0144:8000,015E:XXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0902:XXXXXXXXXXXXXXX"));
+
+ verifyPosition(decoder, text(
+ "H,ATL,866795030478513,02,0981,054448,230318,A,28.633486;N,77.222595;E,0,154,1.14,4.2,18,404,4,88,ad7b,#1031,0,ATL,"));
+
+ verifyNull(decoder, text(
+ "L,ATL,866795030477952,01,0035,"));
+
+ verifyPosition(decoder, text(
+ "ATL861693039769518,$GPRMC,074930.000,A,2838.0112,N,07713.3602,E,0000,223.36,290518,,,A*7E,#01111011000100,0.012689,0,0,2.572415,0,4.015,22,404,4,88,3ad5,0,0.01,1.4_800F_VTS3D3_gen_peri_myn,,internet,00000000,ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL867857039216564,$GPRMC,131101,A,2838.010010,N,7713.354980,E,0,0,240418,,,*09,#00011011000000,0,0,0,10.70,24.31,3.8,0,0,0,0,0ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL867857039216564,$GPRMC,131033,A,2838.010010,N,7713.354980,E,0,51,240418,,,*3D,#00011011000000,0,0,0,10.70,24.31,3.8,20,404,4,88,cfaaATL"));
+
+ verifyPosition(decoder, text(
+ "ATL868004026997257,$GPRMC,095542,A,2838.0107,N,07713.3579,E,0,98,010617,,,*03,#01111011000000,0,0,0,0.01,45.94,4.0,25,404,4,88,3ad5ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL861693035285253,$GPRMC,022040,A,2954.0481,N,07353.1694,E,0,150,280417,,,*36,#01111011000000,0,0,0,82.92,37.92,4.0,23,404,70,163,b178ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL356895037533745,$GPRMC,111719.000,A,2838.0045,N,07713.3707,E,0.00,,120810,,,A*75,#01100111001010,N.C,N.C,N.C,12345.67,31.4,4.2,21,100,000,000001,00000ATL"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
new file mode 100644
index 000000000..31a0434bc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
@@ -0,0 +1,125 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class LaipacProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ LaipacProtocolDecoder decoder = new LaipacProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$AVRMC,80006405,212645,r,3013.9938,N,08133.3998,W,0.00,0.00,010317,a,4076,0,1,0,0,53170583,310260*78"));
+
+ verifyNull(decoder, text(
+ "$AVSYS,99999999,V1.50,SN0000103,32768*15"));
+
+ verifyNull(decoder, text(
+ "$ECHK,99999999,0*35"));
+
+ verifyNull(decoder, text(
+ "$AVSYS,MSG00002,14406,7046811160,64*1A"));
+
+ verifyNull(decoder, text(
+ "$EAVSYS,MSG00002,8931086013104404999,,Owner,0x52014406*76"));
+
+ verifyNull(decoder, text(
+ "$ECHK,MSG00002,0*5E"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*37"),
+ position("2003-07-18 16:43:39.000", true, 43.85090, -79.39241));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,a,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*17"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,v,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*00"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,r,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*04"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,S,3.727,17,1,0,0*54"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,T,3.727,17,1,0,0*53"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,3,3.727,17,1,0,0*34"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,X,3.727,17,1,0,0*5F"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,4,3.727,17,1,0,0*33"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,003016,v,0000.0000,N,00000.0000,E,0.00,0.00,200614,0,3804,167,1,0,0,0D7AB913,020408*23"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,003049,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,3804,167,1,0,0,0D7AB913,020408*71"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,041942,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*0E"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043703,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*07"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043750,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*01"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124022,V,0000.0000,N,00000.0000,E,0.00,0.00,240614,3,4076,167,1,0,0,0D7AB913,020408*0D"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124058,A,5053.0447,N,00557.8549,E,0.45,65.06,240614,0,4037,167,1,0,0,0D7AB913,020408*26"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124144,A,5053.0450,N,00557.8544,E,0.00,65.06,240614,3,4076,167,1,0,0,0D7AB913,020408*26"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,125142,R,5053.0442,N,00557.8694,E,1.21,40.90,240614,0,4037,167,1,0,0,0D7AB913,020408*33"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,125517,R,5053.0442,N,00557.8694,E,0.00,0.00,240614,H,4076,167,1,0,0,0D7AB913,020408*75"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043104,p,5114.4664,N,00534.3308,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*52"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,050601,P,5114.4751,N,00534.3175,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*7D"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,96414215,170046,p,4310.7965,N,07652.0816,E,0.00,0.00,071016,0,4069,98,1,0,0*04"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,999999999999999,111602,r,5050.1262,N,00419.9660,E,0.00,0.00,120318,0,3843,95,1,0,0,3EE4A617,020610*47"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143456,R,5050.1285,N,00420.0620,E,0.00,309.27,190318,0,3455,119,1,0,0,3EE4A617,020610*54"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,999999999999999,084514,r,5050.1314,N,00419.9719,E,0.68,306.39,120318,0,3882,84,1,0,0,3EE4A617,020610*4D"));
+
+ //Alarm button
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,142945,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,3,3455,119,1,0,0,3EE4A617,020610*53"));
+
+ //G-Sensor
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143407,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,8,3455,119,1,0,0,3EE4A617,020610*52"));
+
+ //Powered off
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143648,A,5050.1141,N,00420.0525,E,1.24,174.38,190318,H,3455,119,1,0,0,3EE4A617,020610*3E"));
+
+ //No network
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,R,5050.1124,N,00420.0542,E,1.34,161.96,190318,a,3416,119,1,0,0*7D"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java
new file mode 100644
index 000000000..65c9cc43b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class M2cProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ M2cProtocolDecoder decoder = new M2cProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,1,L,0,20,171221,062016,28.647552,77.192841,0,0,0.0,0,0,64,255,11983,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7524\r\n",
+ "#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,2,L,0,20,171221,062019,28.647552,77.192841,0,0,0.0,0,0,64,255,11983,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7528\r\n",
+ "#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,3,L,0,20,171221,062024,28.647552,77.192841,0,0,0.0,0,0,64,255,16292,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7523\r\n"));
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,2,L,1,100,170704,074933,28.647556,77.192940,900,194,0.0,0,0,0,255,11942,0,0,0,0,0,0,0,0,30068,5051,0,0,1*8159\r\n"));
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,7,L,0,31,170704,075905,28.647615,77.192970,300,260,0.0,6,7,3,255,11967,0,12,0,0,0,0,0,0,19500,5051,0,27,1*8234\r\n",
+ "#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,8,L,0,33,170704,075905,28.647615,77.192970,300,260,0.0,6,7,0,255,11942,0,12,0,0,0,0,0,0,20300,5051,0,27,1*8217\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java
new file mode 100644
index 000000000..1c45c976b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class M2mProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ M2mProtocolDecoder decoder = new M2mProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "235A3C2A2624215C287D70212A21254C7C6421220B0B0B"));
+
+ verifyPosition(decoder, binary(
+ "A6E12C2AAADA4628326B2059576E30202A2FE85D20200B"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java
new file mode 100644
index 000000000..c7a7624c0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MaestroProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MaestroProtocolDecoder decoder = new MaestroProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "@353893040202807,705,UPV-02,1,13.2,17,0,0,16/09/11,11:42:49,0.352705,32.647918,1210.5,0.000000,35.33,11,0.8,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@353893040202807,705,UPV-02,1,13.4,18,0,0,16/09/11,11:43:30,0.352808,32.647990,1211.0,0.000000,80.96,11,0.8,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@353893040202807,601,UPV-02,0,13.4,10,0,0,16/11/04,17:21:14,0.352793,32.647927,0,0,0,0,99,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@123451234512345,531,M2MGTW,1,12.5,30,0,0,11/10/10,09:09:09,22.222222,114.141414,45.6,0.0,160.0,8,1,20!"));
+
+ verifyPosition(decoder, text(
+ "@123451234512345,702,M2MGTW,1,14.7,30,0,1,11/10/10,09:09:09,22.222222,114.141414,45.6,25.12,160.0,8,1,25!"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java
new file mode 100644
index 000000000..1d6f80ae3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ManPowerProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ManPowerProtocolDecoder decoder = new ManPowerProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,tracker,51,24,1.73,130426023608,A,3201.5462,N,03452.2975,E,0.01,28B9,1DED,425,01,1x0x0*0x1*60x+2,en-us,"),
+ position("2013-04-26 02:36:08.000", true, 32.02577, 34.87163));
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,weather,99,20,0.00,130426032310,V,3201.5517,N,03452.3064,E,1.24,28B9,25A1,425,01,1x0x0*0x1*60x+2,en-us,"));
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,SMS,54,19,90.41,130426172308,V,3201.5523,N,03452.2705,E,0.14,28B9,01A5,425,01,1x0x0*0x1*60x+2,en-us,"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
new file mode 100644
index 000000000..68606a98a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MegastekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MegastekFrameDecoder decoder = new MegastekFrameDecoder();
+
+ verifyFrame(
+ binary("30313337244d47563030322c3335343535303035303239323636392c4756543930302c522c3134313231352c3033313830342c412c2c532c2c452c30302c30332c30302c332e36372c302e3030302c302e30302c3131372e312c302e302c3531302c31302c2c2c2c303030302c303030302c32322c31322c302c202c202c2c312d312c39382c5057204f4e3b21"),
+ decoder.decode(null, null, binary("30313337244d47563030322c3335343535303035303239323636392c4756543930302c522c3134313231352c3033313830342c412c2c532c2c452c30302c30332c30302c332e36372c302e3030302c302e30302c3131372e312c302e302c3531302c31302c2c2c2c303030302c303030302c32322c31322c302c202c202c2c312d312c39382c5057204f4e3b21")));
+
+ verifyFrame(
+ binary("244d47563030322c3031333737373030373533363433342c2c522c3031303131342c3030303035372c562c303030302e303030302c4e2c30303030302e303030302c452c30302c30302c30302c39392e392c302e3030302c302e30302c302e302c38302e3236332c3531302c38392c323334322c303330422c2c303030302c303030302c3230302c39362c302c202c202c2c2c2c54696d65723b21"),
+ decoder.decode(null, null, binary("244d47563030322c3031333737373030373533363433342c2c522c3031303131342c3030303035372c562c303030302e303030302c4e2c30303030302e303030302c452c30302c30302c30302c39392e392c302e3030302c302e30302c302e302c38302e3236332c3531302c38392c323334322c303330422c2c303030302c303030302c3230302c39362c302c202c202c2c2c2c54696d65723b210d0a")));
+
+ verifyFrame(
+ binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b3735"),
+ decoder.decode(null, null, binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b37350d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
new file mode 100644
index 000000000..1bf3dbd25
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
@@ -0,0 +1,100 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MegastekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MegastekProtocolDecoder decoder = new MegastekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "0132$MGV002,869152024261561,,S,310818,133945,V,3814.35442,N,02144.50662,E,00,00,00,99.9,,,44.2,,202,10,,,13,0,0,0,0,90,,,,11,100,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "0151$MGV002,869152024261561,ID,S,070918,155544,V,3814.35419,N,02144.52113,E,00,00,00,99.9,0.062,,12.3,,202,01,0898,D1BE,8,34,1055,0,0,600,,,,11,010,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "0174$MGV002,014682001957744,014682001957744,R,260318,042537,A,3853.77301,N,07728.66673,W,00,09,00,1.06,0.147,329.51,123.3,,310,26,B46C,5E69375,5,0000,0000,0,,,,,,10,019,Timer,,;!"));
+
+ verifyNull(decoder, text(
+ "0112$MGV002,,GVT900-3,S,010114,000003,,,,,,00,00,00,,0.000,0.00,,0.0,,,,,,0000,0000,14,10,0, , ,,1-0,0,Low Ext Vol;!"));
+
+ verifyPosition(decoder, text(
+ "0170$MGV002,354550056642321,GVT900-3,S,011017,090208,A,1635.8484,N,10446.6095,E,00,09,00,0.91,16.980,257.73,177.6,0.0,457,01,0741,00C0,21,0000,0000,20,10,0, , ,,1-1,54,Dist;!"));
+
+ verifyNull(decoder, text(
+ "0140$MGV002,354550056642321,GVT900-3,S,300917,071731,V,,,,,00,00,00,99.9,0.000,0.00,,0.0,457,01,0741,00CD,,0000,0000,20,10,0, , ,,1-1,94,PW ON;!"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,869152024446923,,S,290816,200627,V,5056.21059,N,00439.25034,E,00,00,00,99.9,,,-25.1,,206,01,0BBB,4418,28,,,,,,,,,01,093,Timer;"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,869152024446923,869152024446923,S,240816,151631,A,5053.83335,N,00424.05702,E,00,10,00,0.88,2.645,76.09,22.7,,206,01,07D1,6600,28,,,,,,,,,01,100,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "STX,013950007137061,$GPRMC,191959.000,A,5203.09602,N,00830.77057,E,5.73,255.27,240716,,,A*62,L,Belt Up,imei:013950007137061,0/5,,Battery=52%,,1,262,03,0084,B20E;FD"));
+
+ verifyPosition(decoder, text(
+ "STX,865067021328417,$GPRMC,064721.000,A,4241.2793,N,02321.9762,E,6.74,346.90,300316,,,1*CA,F,Nil-Alarms,imei:865067021328417,9,559.8,Battery=82%,0,284,03,047E,2B5F;99"));
+
+ verifyNull(decoder, text(
+ "0147$MGV002,354550050292669,GVT900,S,141215,031804,A,,S,,E,00,04,00,5.17,0.000,193.05,117.1,0.0,510,10,041B,0A5E,,0000,0000,22,12,0, , ,,1-1,98,Timer;!"));
+
+ verifyNull(decoder, text(
+ "0137$MGV002,354550050292669,GVT900,R,141215,031804,A,,S,,E,00,03,00,3.67,0.000,0.00,117.1,0.0,510,10,,,,0000,0000,22,12,0, , ,,1-1,98,PW ON;!"));
+
+ verifyPosition(decoder, text(
+ "0125$MGV002,860719020193193,DeviceName,R,240214,104742,A,2238.20471,N,11401.97967,E,00,03,00,1.20,0.462,356.23,137.9,1.5,460,07,262C,0F54,25,0000,0000,0,0,0,28.5,28.3,,,100,Timer;"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,860719020193193,DeviceName,R,240214,104742,A,2238.20471,N,11401.97967,E,00,03,00,1.20,0.462,356.23,137.9,1.5,460,07,262C,0F54,25,0000,0000,0,0,0,28.5,28.3,,,100,Timer;!"),
+ position("2014-02-24 10:47:42.000", true, 22.63675, 114.03299));
+
+ verifyPosition(decoder, text(
+ "STX2010101801 j$GPRMC,101053.000,A,2232.7607,N,11404.7669,E,0.00,,231110,,,A*7F,460,00,2795,0E6A,14,94,1000,0000,91,Timer;1D"));
+
+ verifyPosition(decoder, text(
+ "STX,861001005215757,$GPRMC,180118.000,A,4241.330116,N,2321.931251,E,0.00,182.19,130915,,E,A,F,Nil-Alarms,imei:861001005215757,8,577.0,Battery=38%,0,284,03,03E8,3139;7A"));
+
+ verifyPosition(decoder, text(
+ "STX,865067020439090,$GPRMC,171013.000,A,5919.1411,N,01804.1681,E,0.000,294.41,140815,,,A"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,013777007536434,,R,010114,000057,V,0000.0000,N,00000.0000,E,00,00,00,99.9,0.000,0.00,0.0,80.263,510,89,2342,030B,,0000,0000,200,96,0, , ,,,,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "STX,GerAL22,$GPRMC,174752.000,A,3637.060059,S,6416.2354,W,0.00,0.00,030812,,,A*55,F,,imei:861785000249353,05,180.6,Battery=100%,,1,722,310,0FA6,39D0;8F"));
+
+ verifyPosition(decoder, text(
+ "STX,GerAL22,$GPRMC,000051.000,A,3637.079590,S,6416.2148,W,1.72,332.98,010109,,,A*52,L,,imei:861785000249353,03,275.3,Battery=68%,,1,722,07,0515,1413;41"));
+
+ verifyPosition(decoder, text(
+ "STX,,$GPRMC,001339.000,A,4710.85395,N,02733.58209,E,1.65,238.00,010109,,,A*67,L,Help,imei:013227009737796,0/8,137.1,Battery=100%,,0,226,01,2B9B,BBBF;8D"));
+
+ verifyPosition(decoder, text(
+ "STX,102110830074542,$GPRMC,114229.000,A,2238.2024,N,11401.9619,E,0.00,0.00,310811,,,A*64,F,LowBattery,imei:012207005553885,03,113.1,Battery=24%,,1,460,01,2531,647E;57"));
+
+ verifyPosition(decoder, text(
+ "STX863070014949464 $GPRMC,215942.290,A,4200.1831,N,02128.5904,E,003.1,079.8,090813,,,A*6E,294,02,0064,0F3D,18,17,0000,000000,0000,0.00,0.02,0.00,Store;D8"));
+
+ verifyPosition(decoder, text(
+ "STX123456 $GPRMC,063709.000,A,2238.1998,N,11401.9670,E,0.00,,250313,,,A*7F,460,01,2531,647E,11,87,1000,001001,0000,0.00,0.02,0.00,Timer;4A"));
+
+ verifyPosition(decoder, text(
+ "STX260475 $GPRMC,104032.001,A,4022.1119,N,01811.4081,E,000.0,000.0,060913,,,A*67,222,01,815A,D455,11,99,0000,0001,0,Timer;"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,225419.000,A,3841.82201,N,09494.73357,W,12.46,135.33,270914,,,A*47,F,,imei:123456789012345,0/6,,Battery=100%,,0,,,5856,78A3;24"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,230551.000,A,3841.81956,N,09494.45403,W,0.00,0.00,270914,,,A*7C,L,,imei:123456789012345,0/7,269.7,Battery=100%,,0,,,5856,78A3;83"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,230739.000,A,3841.81895,N,09494.12409,W,0.00,0.00,270914,,,A*70,L,,imei:123456789012345,0/7,269.7,Battery=100%,,0,,,5856,78A3;78"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
new file mode 100644
index 000000000..2d09c626b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class MeiligaoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeiligaoFrameDecoder decoder = new MeiligaoFrameDecoder();
+
+ assertNull(
+ decoder.decode(null, null, binary("00")));
+
+ assertEquals(
+ binary("2424007b8621700151517899553233323835372e3030302c562c333632372e313835342c4e2c30313034352e323130392c452c302e30302c372c3239303131332c2c2a31347c302e307c347c303030307c303030382c303030357c303235443030303230303541374432327c30367c303030314530353527f40d0a"),
+ decoder.decode(null, null, binary("2424007B8621700151517899553233323835372E3030302C562C333632372E313835342C4E2C30313034352E323130392C452C302E30302C372C3239303131332C2C2A31347C302E307C347C303030307C303030382C303030357C303235443030303230303541374432327C30367C303030314530353527F40D0A")));
+
+ assertEquals(
+ binary("2424007b8621700151517899553233323835372e3030302c562c333632372e313835342c4e2c30313034352e323130392c452c302e30302c372c3239303131332c2c2a31347c302e307c347c303030307c303030382c303030357c303235443030303230303541374432327c30367c303030314530353527f40d0a"),
+ decoder.decode(null, null, binary("002424007B8621700151517899553233323835372E3030302C562C333632372E313835342C4E2C30313034352E323130392C452C302E30302C372C3239303131332C2C2A31347C302E307C347C303030307C303030382C303030357C303235443030303230303541374432327C30367C303030314530353527F40D0A")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
new file mode 100644
index 000000000..da5a81144
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
@@ -0,0 +1,137 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MeiligaoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeiligaoProtocolDecoder decoder = new MeiligaoProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "24240012254748594772ff080002ffff0d0a"));
+
+ verifyNull(decoder, binary(
+ "242403fe254748594772ff99880242681100ffd8ffe000104a46494600010101000000000000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffdb0043010909090c0b0c180d0d1832211c213232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffc000110801e0028003012100021101031101ffda000c03010002110311003f00f0cc679a5a977d84b517d69474a98f615930e3a52edabea50b8a5c54c9e84c9f4178a5c7b50ad617bbd45a5c00287b16d681f852f5eb44aefde32e5bbb0ec0a38eb4d36d9a48753f150f4d5916f74318a70ad5dd8dc75168ed53ccadee8e239452f434afd10921719a7639aad56854ac3c0a5a98cfc8971d6c85a7f6a65ad07e29f8a1b10fc52ff153f405a6e48053f145f4b31abc5928e9cd4cabd3152f4d01ab6a4ca2a50a33509d9d809d56a50b56a7a87c24cbd6a655a2510ea4aa2a555a953b30255a92a80945498a435b8f1520a3d043b14e02974d40752d37a20490b4b40076a2a760129298094949e8030d368527b8c6530d4ddc84861a6353b8119a69e94b402334ca6e5a0861eb4c22b3d876184530d56e85623c506b27cd719e494bdabadab6a2d43e945227588a7d29739e69d92dc7d4703476cd569612d472b6452d4fbb61d828c734d6eac263fb52d539caf7634c55f6a2a1b8885a78e949db94528bd90539714dbea69bea2e7d29d9c51b19db4b053c75abffff0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400716578902405843299553136323533332e3937382c412c343632332e313137392c4e2c30373932342e323437312c572c303030302c3030302c3139313231372c2c2a31437c31312e357c3139347c303030307c313139322c303030307c3835383030307c30303331343809540d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400716578902405843299553136323533332e3937382c412c343632332e313137392c4e2c30373932342e323437312c572c303030302c3030302c3139313231372c2c2a31437c31312e357c3139347c303030307c313139322c303030307c3835383030307c30303331343809540d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424010a142170525979ff9999753137353830322e3030302c412c313330362e303639342c4e2c31303035342e323439302c452c302e30302c3331332c3234313031372c2c2a30457c302e397c377c323530307c303030302c303030302c303130312c303241447c30323038303030353043313330313638353333427c30427c30303032313034357c30417c2520205e59454e53414241494348414924534f4e474b52414e244d522e5e5e3f3b363030373634333130303530303337333835333d3135303531393637303631343d3f2b202020202020202020202020203234202020202020202020202020312020202020202020202020203030303431313120203030313030545c0d0a"));
+
+ verifyPositions(decoder, binary(
+ "2424006661172036237118668801003039333630342e3030302c562c303330332e333231352c4e2c31303134372e313530302c452c302e30302c2c3235313031377c302e307c302e307c303030307c303030302c303030307c30303030303230343259ca0d0a"));
+
+ verifyPositions(decoder, binary(
+ "242401d961172036237118668805003039353830332e3030302c412c303330332e333431392c4e2c31303134372e343130342c452c372e30342c3230362e36312c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313532325c003039353833332e3030302c412c303330332e323630302c4e2c31303134372e333734342c452c31302e33382c3236332e31342c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313734355c003039353930332e3030302c412c303330332e313833382c4e2c31303134372e333735362c452c382e34392c3232332e37372c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313839375c003039353933332e3030302c412c303330332e313033312c4e2c31303134372e333435332c452c382e37312c3139312e35302c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031323130325c003130303030302e3030302c412c303330332e313032332c4e2c31303134372e333338372c452c302e30302c3231332e36392c3235313031377c302e307c302e307c303030307c303030312c303030307c3030303031323131380d110d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f1092ffffffffff9999523232303534392e3030302c412c333533372e313231372c4e2c30313130302e303633332c452c362e34382c3139332c3238303631372c2c2a30357c302e387c32347c323030307c303030432c303030417c303235443030303230303833354437427c31357c3037303636424142f7310d0a"));
+
+ verifyAttributes(decoder, binary(
+ "242400561001ffffffffff99553030303030302e3030302c562c303030302e303030302c532c30303030302e303030302c572c302e30302c302e30302c3232303839392c2c2c412a37457c7c307c3030303059ae0d0a0000"));
+
+ verifyPosition(decoder, binary(
+ "242400706573402852404799553130313932372e3030302c412c313732362e38323739332c4e2c30373832382e31393637312c452c312e382c362e342c3137313131362c2c2a32427c312e36387c3534342e327c313030307c303030302c303030307c3030303032383638373a1a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007d0000000000000099553231303333302e3030302c562c343533342e333832342c532c30373230332e303630302c572c302e30302c302c3231313031362c2c2a31327c302e307c3332397c323030307c303030452c303030437c303244413030303145413634393541417c31307c30303030303030306e540d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040005066104020094432990131302E312C302C3135362C302E30302C31392E36312C2D33342C33342E32362C32312E38332C372E39312C313033332C322E36392C362E35352C302C302C309DBF0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "242400736610402421174399553130353033342e3937382c412c333933352e333638392c4e2c30303233382e313638342c452c303034382c3034322c3038313231362c2c2a31437c31312e357c3139347c313030317c303341362c303030307c30303130343030307c3030303030303cd00d0a2424004e66104024211743990131342e312c323638372c39302c32312e35372c342e37312c38352c372e31302c382e31362c342e32372c3130342c302e33342c392e33342c302c312c30b7160d0a2424003266104024211743990232352c322e34302c302e37392c32322c34332c3131392c333735362c37352c3132e4c90d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400746251103044ffff99553033353033392e3939392c412c323832332e373632312c4e2c31303635322e303730342c572c3030302e302c3030302e302c3136303631362c2c2c412a37357c302e397c323038332e327c303030307c303030302c303030307c31303034333736333265780d0a"));
+
+ verifyPosition(decoder, binary(
+ "24240072190820157fffff99553039343335342e3030302c412c313930372e303631392c4e2c30373235312e333235312c452c3031302e312c3138382e352c3234303231362c2c2c412a36427c302e387c36352e327c303830307c303030302c303030307c303336343838373532c73f0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400680790209818ffff99553038333235382e3030302c412c303131352e393338302c532c30333634382e313430392c452c302e30302c3331352e35352c3132303131367c302e37347c313930322e337c303430307c303030302c303030307c302e30f41b0d0a"));
+
+ verifyNull(decoder, binary(
+ "24240011671440258855405000b24d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400706796502079108999553131333131382e3030302c412c313033372e393637382c4e2c30363132312e353637392c572c302e35342c322e34322c3330303931352c2c2c412a37307c302e37377c392e397c303030307c303030302c303161327c3030313138373132374cae0d0a"),
+ position("2015-09-30 11:31:18.000", true, 10.63280, -61.35947));
+
+ verifyPosition(decoder, binary(
+ "24240074630700194707719966009E1F7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007C3132303334302C3238303630362C323430302E303030302C4E2C31323130302E303030302C458F7E0D0A"),
+ position("2006-06-28 12:03:40.000", true, 24.00000, 121.00000));
+
+ verifyPosition(decoder, binary(
+ "24240076220720151fffff99660012b3ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c3135303634382c3233303731352c313931352e37323835362c4e2c30373235322e35333034342c456dd00d0a"));
+
+ verifyNull(decoder, binary(
+ "24240000123456FFFFFFFF50008B9B0D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400722015032700004299553134313131352e3030302c412c353031312e343335342c4e2c30303834332e373039322c452c3030302e302c3034342e362c3134303431352c2c2c412a36437c322e317c39392e347c303030307c303030302c303030307c3030303032343730350e480d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424006e241120141fffff99553039333931302e30302c412c313931342e37373736352c4e2c30373235302e36383037322c452c302e3035312c2c3230313231342c2c2c442a37357c302e38327c322e387c303030307c303030302c303030307c3031303833373433311d170d0a"));
+
+ verifyPosition(decoder, binary(
+ "24240000123456FFFFFFFF99553033353634342E3030302C412C323233322E363038332C4E2C31313430342E383133372C452C302E30302C2C3031303830392C2C2A31437C31312E357C3139347C303030307C303030302C3030303069620D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400003358019703581F99553133343335312E3030302C412C303932352E353032352C4E2C30363931342E383130372C572C302E30302C32332C3330313031322C2C2A32437C302E387C3138367C323030307C303132362C303046467C303244453030303244384344423431357C30367C303046443642373995820D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400001691000484124F99553134303630332E3030302C412C303933342E323535342C4E2C30363931332E303936362C572C302E30302C2C3330313031322C2C2A30327C302E387C3230377C30303030FA420D0A"));
+
+ verifyPosition(decoder, binary(
+ "2424000045124220306FFF9999143135353432322E3030302C562C323233302E373632332C4E2C31313430332E343231382C452C302E30302C302C3036303231312C2C2A31417C302E307C32367C303030307C303030302C303030307C303030303030303030303030303030307C36337C3030303030303030BAC10D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400008621700142458F9999503139323935382E3030302C412C333632372E313639392C4E2C30313034332E353632372C452C302E30302C3233392C3039313231322C2C2A30467C312E307C377C303030307C303141392C303139377C303235443030303230303541383639467C31327C3030303333424233E2480D0A"));
+
+ verifyPosition(decoder, binary(
+ "24240000123456789FFFFF99553032303630302E3933302C412C323330392E323035312C4E2C31313331382E383434392C452C302E30302C302E30302C3039303731302C2C2C412A36417C322E367C39362E377C303030307C303030302C334646467C303030303030303030C4520D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400005977203744058499553032303131372E3030302C412C343131372E393231322C4E2C30383133302E323039362C572C302E30302C3330332E38352C3236303231337C312E367C30307C303030307C303030302C3030303071CD0D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400003511111111111199553133343734332e3030332c412c303634382e393836362c532c31303730372e353739352c452c3030302e302c3030302e302c323630333133f3150d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400000091800369764199553038353133302e3030302c412c333035332e313634352c4e2c30373535352e373437362c452c302e30302c32372c3136303431332c2c2a33467c302e387c3234357c323030307c303346372c303030302c303030302c303031422c303030302c303030302c303030302c303030307c303139343030303230314343363237437c31417c3031313630383439e6a70d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424000026016100901fff99553136353835332c412c343130392e36303231322c4e2c3833382e35393131392c572c332e3838332c31322e30302c3034303731332c2c2a34467c322e357c3131307c000600007c0000010f7c303242302c3042333697740d0a"));
+
+ verifyNull(decoder, binary(
+ "2424000067622010053562aa0000010001ae4f00000007800000003039353135362e3030302c412c323632332e383936362c4e2c30353030352e303638302c452c32342e352c3234312e302c323231303133599c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400001007ffffffffff99553136323330392e3035342c562c303933312e393136332c4e2c30363931312e383233332c572c2c2c3235313131332c2c2c4e2a36437c7c3135387c303030309cc60d0a"));
+
+ verifyAttributes(decoder, binary(
+ "242400003563070435652099553035323034322e3030302c412c343435382e333536352c4e2c30343130342e343831332c452c302e30302c302e30302c3139303131342c2c2a39437c302e3730303030307c2d3835393131373337367c303130307c307c7c7c4f2a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424005035784251ffffff99553030303033362e3938312c562c303933312e333437312c4e2c30363931312e383431322c572c2c2c3238303131342c2c2c4e2a36357c7c3136387c323030305e420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007811223344ffffff99553031303735372e3030302c412c323935392e313337342c4e2c30393534302e333238342c572c3030302e302c3038382e372c3234303631342c2c2c412a37397c312e347c33352e317c303030307c303030312c303030307c3030303030333732337c3030303030e39f0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f3568960306760199553131303932342e3030302c412c343533342e323538352c4e2c30313933382e363531342c452c302e30302c2c3237303731342c2c2c412a37317c312e377c3130307c383030307c303737422c303030302c303030302c303030302c303030302c303030302c303030302c303030305dfc0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f2015603256ffff99553230303230392e30302c412c313734342e36393434382c4e2c30383331392e30353537302c452c31302e3236322c3234382e35352c3236303631352c2c2c442a35437c302e39397c33312e397c303830317c303030302c303030637c3030303933303131367c3030376165313035d5550d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java
new file mode 100644
index 000000000..ee4a869f9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class MeiligaoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MeiligaoProtocolEncoder encoder = new MeiligaoProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ verifyCommand(encoder, command, binary("404000111234567890123441016cf70d0a"));
+
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 100);
+
+ verifyCommand(encoder, command, binary("40400013123456789012344102000a2f4f0d0a"));
+
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+8");
+
+ verifyCommand(encoder, command, binary("4040001412345678901234413234383030ad0d0a"));
+
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ verifyCommand(encoder, command, binary("40400011123456789012344902d53d0d0a"));
+
+ command.setType(Command.TYPE_ALARM_GEOFENCE);
+ command.set(Command.KEY_RADIUS, 1000);
+
+ verifyCommand(encoder, command, binary("4040001312345678901234410603e87bb00d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
new file mode 100644
index 000000000..53749816e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class MeitrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeitrackFrameDecoder decoder = new MeitrackFrameDecoder();
+
+ assertEquals(
+ binary("24244e3132372c3836333037313031333830333036362c4141412c33352c2d312e3330323638302c33362e3835323133352c3135303430393231313032362c412c392c302c302e312c302c352c313635332c343039362c33323634382c3633397c30327c313030347c3930432c303030302c307c307c307c3346467c3330302c2a37430d0a"),
+ decoder.decode(null, null, binary("24244e3132372c3836333037313031333830333036362c4141412c33352c2d312e3330323638302c33362e3835323133352c3135303430393231313032362c412c392c302c302e312c302c352c313635332c343039362c33323634382c3633397c30327c313030347c3930432c303030302c307c307c307c3346467c3330302c2a37430d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
new file mode 100644
index 000000000..3e05d5243
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -0,0 +1,110 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MeitrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "242441313038362c3836343530373033313231393937342c4430302c3138303232343037323631345f4331453130395f4e31553144312e6a70672c31342c302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00cca69ac8d06e3348569884db4845021b498a60371494008692980119a8ca7a5342101a5cd5221a0ab312ed1ee68b943e80dce2a467ffd0c806a48e592270f13b230e841a0096eeea7bb09e6c85b667033552800069c2980e14f15422418a916ad099228a95455089505584140993a2d5fb598a7cae72bd8fa536ae892e8e69e2b9d971168a459fffd1ece8a0028a006b534f4a68ce5b9130a89ab444919a61a6c634d34d21894952310d25002514084a4a00ffd2d2349564086929082929805250025140094940c4a4a04251400949408292819fffd3cca31591a098a5c62801a45464531098a69a6210d371400629a6980628eb400c64cd3791c1aa16c491479393563343105424fcc4d007ffd4c3463c0a94500381a5e3b8cd000c99e57f2a8f3835402834e0d4d08914d4aa6ac4c954d4aad4c4c955aa647a6496236ab51b552132e412e383d3f955b0722b09ab32a0c5a2a0d0ffd5ece8a0028a0061a6d5193dc8daa36ab422334c34c634"));
+
+ verifyPositions(decoder, binary(
+ "24246b3131342c3836353738393032343134303439352c4343452c0000000001005000130006011f05010607071415001b00060800000949010a0c000b9b0119a1011afe010602e934ce0203fc9aeb0004309f13220cafc503000d97741e001c01000000010e0ce8000300092f2e060000b7ff2a33330d0a"));
+
+ verifyPosition(decoder, buffer(
+ "$$^182,864507031245110,AAA,109,13.844553,100.644360,171227173141,A,11,19,0,359,0.8,8,15075,934591,520|4|0643|07D20555,8400,0000|0000|0000|018D|04CB,,,108,0000,,6,0,,,,,10|171227173100*7C"));
+
+ verifyPosition(decoder, buffer(
+ "$$S214,864507031219974,AAA,109,13.844643,100.644395,171207021520,A,10,28,0,31,0.8,6,390,421327,520|0|0016|000F2DB0,8400,0000|0000|0000|018D|04C6,,,108,0000,,6,0,,,,,11|171207091500|171207091500|78|3500|000000|000003*12"));
+
+ verifyPositions(decoder, binary(
+ "24245f3237382c3836353738393032313434373233322c4343452c5b00000003005000130006012305010608070d15001b0006080000091e010a09000b2e0019a1011af90106025c033300039be60c06044f6678210c6f1806000d48db06001c41000000010e0cf60113002005912b830001ff5000130006012305010608070d15001b0006080000091e010a09000b2e0019a0011af90106025c033300039be60c0604506678210c6f1806000d49db06001c41000000010e0cf60113002005912b830001ff5000130006012305010608070d15001b0006080000091e010a09000b2e0019a1011af90106025c033300039be60c0604516678210c6f1806000d4adb06001c41000000010e0cf60113002005912b830001ff2a37460d0a"));
+
+ verifyPosition(decoder, buffer(
+ "$$V177,863835026871173,AAA,35,34.516428,10.470160,170915154043,A,9,12,68,74,0.9,9,1988259,525882,605|2|008C|0007B5A6,0200,0003|0000|0000|01A6|0571,00000001,,3,0000,06FB2E,360,511*74"));
+
+ verifyPosition(decoder, buffer(
+ "$$V177,863835026871173,AAA,35,34.516428,10.470160,170915154043,A,9,12,68,74,0.9,9,1988259,525882,605|2|008C|0007B5A6,0200,0003|0000|0000|01A6|0571,00000001,,3,0000,010A92,360,511*74"));
+
+ verifyPosition(decoder, buffer(
+ "$$B136,011691002364761,AAA,29,47.055220,28.893193,170914144240,V,0,7,0,0,0,132,129754946,129793197,259|2|02F8|413F,0000,000D|000C||028C|,*9E"));
+
+ verifyNotNull(decoder, buffer(
+ "$$F153,863835026880190,AAA,29,25.313160,55.422473,170628150902,V,0,0,0,0,0.0,0,6553,6697,0|0|0000|00000000,0000,0002|0000|0000|018B|0000,,,3,0000,,110,386*22"));
+
+ verifyPosition(decoder, buffer(
+ "$$T143,869013024733944,AAA,1,18.459575,-69.947161,170220142912,A,5,15,10,300,1.6,115,3989,187884,370|2|5337|2B2C,0100,0000|0000|0000|0964|0B04,,*C2"));
+
+ verifyPosition(decoder, buffer(
+ "$$K157,866771027160687,AAA,3,37.040231,10.042391,160412151656,A,10,11,0,48,0.8,21,1035518,774980,605|2|0010|307B,0400,0000|0000|0000|0A47|03E3,,,1,0000,001206*2C"));
+
+ verifyNull(decoder, buffer(
+ "$$D28,353358017784062,D03,OK*F3"));
+
+ verifyPosition(decoder, buffer(
+ "$$A158,79007001520234,AAA,35,40.996370,-8.575065,150730184834,A,8,24,0,1,1.3,173,32573389,31405012,268|3|2BC0|250B,2000,|||0A2D|0000,00000001,,50,,,,,,,,,,,,,*4A"),
+ position("2015-07-30 18:48:34.000", true, 40.99637, -8.57507));
+
+ verifyPosition(decoder, buffer(
+ "$$G145,862106024274815,AAA,35,-1.287125,36.906061,150530054639,A,10,13,12,67,0.8,1621,38359791,42330881,639|2|FB2|2F3,0000,3|0|0|A58|432,,,1,0009,*26"));
+
+ verifyPosition(decoder, buffer(
+ "$$I152,013949004569813,AAA,37,54.739468,25.273648,150208173414,A,5,24,0,73,1.5,165,74,3381,246|1|0065|118A,0000,0003|0003|0000|08D4|0002,006380DF,,1,0008*7C"));
+
+ verifyPosition(decoder, buffer(
+ "$$E141,863071013799553,AAA,35,-1.264521,36.801128,150307132846,A,11,20,0.2,0,5,1767,84045888,36496633,639|02|100E|844,1234,0018|||025D|00CB,*17"));
+
+ verifyPosition(decoder, buffer(
+ "$$m140,013777008931857,AAA,1,54.739580,25.273263,141120144603,V,0,25,0,6,50.0,159,19825,13940,246|1|0065|118A,0100,0000|0000|0000|092A|0001,,*1C"));
+
+ verifyPosition(decoder, buffer(
+ "$$X138,862170010187175,AAA,35,-29.960365,-51.655455,130507201625,A,8,9,0,107,0.9,7,169322,126582,724|6|0547|132B,0000,0009|000A||0278|0000,*BE"));
+
+ verifyPosition(decoder, buffer(
+ "$$X138,862170010187175,AAA,35,-29.960365,-51.655455,130507201625,A,8,9,0,107,0.9,-7,169322,126582,724|6|0547|132B,0000,0009|000A||0278|0000,*BE"));
+
+ verifyPosition(decoder, buffer(
+ "$$]138,012896000475498,AAA,35,-6.138255,106.910545,121205074600,A,5,18,0,0,0,49,3800,24826,510|10|0081|4F4F,0000,0011|0012|0010|0963|0000,,*94"));
+
+ verifyPosition(decoder, buffer(
+ "$$d138,012896000475498,AAA,35,-6.138255,106.910545,121205074819,A,7,18,0,0,0,49,3800,24965,510|10|0081|4F4F,0000,000D|0010|0012|0963|0000,,*BF"));
+
+ verifyPosition(decoder, buffer(
+ "$$j138,012896000475498,AAA,35,-6.138306,106.910655,121205103708,A,3,11,0,0,1,36,4182,35025,510|10|0081|4F4F,0000,000A|000C|000A|0915|0000,,*BF"));
+
+ verifyPosition(decoder, buffer(
+ "$$m139,012896005334567,AAA,35,-33.866423,151.190060,121208020649,A,7,27,0,32,4,13,6150,49517,505|2|0B67|5A6C,0000,0000|0000|0000|0977|0000,,*F1"));
+
+ verifyPosition(decoder, buffer(
+ "$$A141,012896005334567,AAA,35,-33.866543,151.190148,121209081758,A,6,27,0,16,1,48,65551,152784,505|2|0B5F|D9D3,0000,0000|0000|0000|0A39|0000,,*5B"));
+
+ verifyPosition(decoder, buffer(
+ "$$_128,861074020109479,AAA,34,22.512618,114.057065,090215000318,V,0,31,0,0,0,0,0,733,302|720|3EE4|BBB5,0000,0006|0006||028C|0000,*E3"));
+
+ verifyPosition(decoder, buffer(
+ "$$K146,013227004985762,AAA,35,28.618005,-81.246783,131101213828,A,9,22,0,209,1.1,23,80974,1187923,310|260|2A13|634E,0000,0000|0000|0000|09DA|0B34,,*51"));
+
+ verifyPosition(decoder, buffer(
+ "$$E150,013777001165479,AAA,35,10.296601,123.872115,140501161505,A,4,22,1,170,1.4,77,39097,393563,515|3|A0CC|ED96,0000,0008|0003|0000|09D5|0000,,,1,0009*1E"));
+
+ verifyPosition(decoder, buffer(
+ "$$B140,013777001293701,AAA,35,-7.266760,112.743550,140521095314,A,3,22,0,275,2.7,45,1984,8059,510|1|3504|EBFE,0000,0000|0000|0000|0914|0002,,*F9\r\n"));
+
+ verifyPosition(decoder, buffer(
+ "$$J163,123123123123123,AFF,0004,35,58.588926,16.180473,140928192856,A,10,27,0,161,1.2,19,1648894,435695,240|24|88B9|E435,0000,|||0A22|0000,00000001,,50,,,,,,,,,,,,,*70\r\n"));
+
+ verifyPositions(decoder, binary(
+ "24245838362c3336393830303031343039303032312c4343432c020134000100000023381f91ffe354b806c5e3121b0009130000000000000000d33801007cbf0200fe0101000435feeb02000500a3010000000000002a62650d0a"),
+ position("2014-05-24 04:59:49.000", false, -7.26650, 112.74365));
+
+ verifyPositions(decoder, binary(
+ "2424473937302c3336393830303031333436303637342c4343432c020134005b000000010ce304035db9e000ec6f591a000013000000000c001801edb70200c96d0100e60001004838576501000300a101c20400000000010ce304035db9e000ee6f591a000013000000000c001801edb70200ca6d0100e60001004838576501000300a101c20400000000010ce304035db9e000ef6f591a000013000000000c001801edb70200cc6d0100e60001004838576501000300a101c20400000000020ce304035db9e000f76f591a000016000000000c001801edb70200d36d0100e60001004838576502000300a101bf04000000000a0ce304035db9e000f76f591a000016000000000c001801edb70200d46d0100e60001004838576500000300a101bf0400000000020ce304035db9e000fb6f591a000016000000000c001801edb70200d86d0100e60001004838576502000300a101760400000000180ce304035db9e000fc6f591a0000120000000000008c00edb70200d96d0100e60001004838576502000300a10176040000000019b1e2040323b9e0000b70591a0105150600bb0012002901edb70200e76d0100e60001004838576502000300a2017005000000002023e304031fb9e0001070591a010615070027010d001601fcb70200ec6d0100e60001004838576502000300a201800500000000201fe3040302b9e0001170591a010615090019010d001501feb70200ed6d0100e60001004838576502000300a2018005000000002018e30403dcb8e0001270591a0106150b0011010d00150100b80200ee6d0100e60001004838576502000300a2018005000000002036e3040345b8e0001570591a0107150b002d010b0013010ab80200f16d0100e60001004838576502000300a2018005000000002053e3040326b8e0001670591a0107150d0041010b0013010eb80200f26d0100e60001004838576502000300a2018005000000002070e3040310b8e0001770591a0107150e004f010b00130111b80200f36d0100e60001004838576502000300a2018005000000002095e3040306b8e0001870591a0107150d005a010b00140115b80200f46d0100e60001004838576502000300a20180050000000020b3e3040305b8e0001970591a0107150b0060010b00140118b80200f56d0100e60001004838576502000300a20183050000000020cfe3040308b8e0001a70591a0107150b0066010b0014011bb80200f66d0100e60001004838576502000300a20183050000000020eee304030cb8e0001b70591a0106170b0004000d0014011eb80200f76d0100e60001004838576502000300a2018305000000002a62350d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
new file mode 100644
index 000000000..b63ce5051
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class MeitrackProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MeitrackProtocolEncoder encoder = new MeitrackProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("@@Q25,123456789012345,A10*68\r\n", encoder.encodeCommand(command));
+
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REQUEST_PHOTO);
+
+ assertEquals("@@D46,123456789012345,D03,1,camera_picture.jpg*1F\r\n", encoder.encodeCommand(command));
+
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SEND_SMS);
+ command.set(Command.KEY_PHONE, "15360853789");
+ command.set(Command.KEY_MESSAGE, "Meitrack");
+
+ assertEquals("@@f48,123456789012345,C02,0,15360853789,Meitrack*B0\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java
new file mode 100644
index 000000000..be0209975
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MilesmateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MilesmateProtocolDecoder decoder = new MilesmateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "ApiString={A:861359037373030,B:09.8,C:00.0,D:083506,E:2838.5529N,F:07717.8049E,G:000.00,H:170918,I:G,J:00004100,K:0000000A,L:1234,M:126.86}"));
+
+ verifyPosition(decoder, text(
+ "ApiString={A:861359037496211,B:12.7,C:06.0,D:060218,E:2837.1003N,F:07723.3162E,G:016.80,H:310818,I:G,J:10010100,K:0000000A,L:1234,M:358.33}"),
+ position("2018-08-31 06:02:18.000", true, 28.61834, 77.38860));
+
+ verifyPosition(decoder, text(
+ "ApiString={A:862631032208018,B:12.1,C:24.4,D:055852,E:2838.5310N,F:07717.8126E,G:000.0,H:200117,I:G,J:10100100,K:1000000A,L:1234,M:324.45}"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
new file mode 100644
index 000000000..afa930e5b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
@@ -0,0 +1,73 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MiniFinderProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MiniFinderProtocolDecoder decoder = new MiniFinderProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "!1,867273023933661,V07S.5701.1621,100"));
+
+ verifyAttributes(decoder, text(
+ "!3,ok"));
+
+ verifyNull(decoder, text(
+ "!1,123456789012345"));
+
+ verifyNull(decoder, text(
+ "!5,17,V"));
+
+ verifyNull(decoder, text(
+ "!1,860719027585011"));
+
+ verifyPosition(decoder, text(
+ "!D,02/05/17,19:56:17,47.083542,15.482373,0,0,100001,479.3,100,4,9,0"));
+
+ verifyPosition(decoder, text(
+ "!D,15/04/17,13:58:53,51.483067,-0.452548,60,180,140001,28.7,47,4,13,0"));
+
+ verifyPosition(decoder, text(
+ "!D,07/04/17,05:42:26,-37.588970,145.121231,0,0,0c0001,185.2,92,7,14,1.2"));
+
+ verifyPosition(decoder, text(
+ "!D,28/11/16,00:04:09,42.926067,-85.747589,124,236,140001,179.8,60,11,16,0"));
+
+ verifyPosition(decoder, text(
+ "!C,30/1/16,1:1:6,31.259157,30.020910,0,0,100001,25.32,100,0.03,0.01,0"));
+
+ verifyPosition(decoder, text(
+ "!A,26/10/12,00:28:41,7.770385,-72.215706,0.0,25101,0"));
+
+ verifyPosition(decoder, text(
+ "!A,01/12/10,13:25:35,22.641724,114.023666,000.1,281.6,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,04:01:32,40.428257,-3.704808,0,0,170001,701.7,22,5,14,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,04:55:13,40.428257,-3.704932,0,0,180001,680.0,8,8,13,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,02:01:32,40.428230,-3.704950,4,170,170001,682.7,43,6,13,0"));
+
+ verifyNull(decoder, text(
+ "!1,860719020212696"));
+
+ verifyPosition(decoder, text(
+ "!D,22/2/14,13:40:58,56.899601,14.811541,0,0,1,176.0,98,5,16,0"),
+ position("2014-02-22 13:40:58.000", true, 56.89960, 14.81154));
+
+ verifyPosition(decoder, text(
+ "!D,22/2/14,13:47:51,56.899517,14.811665,0,0,b0001,179.3,97,5,16,0"));
+
+ verifyPosition(decoder, text(
+ "!D,3/7/13,6:35:30,22.645952,114.040436,0.0,225.8,1f0001,12.11,98,0,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
new file mode 100644
index 000000000..e9422da9f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class MiniFinderProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MiniFinderProtocolEncoder encoder = new MiniFinderProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+1");
+
+ assertEquals("123456L+01", encoder.encodeCommand(command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, 2);
+ command.set(Command.KEY_PHONE, "1111111111");
+
+ assertEquals("123456C1,1111111111", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java
new file mode 100644
index 000000000..c4f15d907
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MtxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MtxProtocolDecoder decoder = new MtxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#MTX,353815011138124,20101226,195550,41.6296399,002.3611174,000,035,000000.00,X,X,1111,000,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
new file mode 100644
index 000000000..834a35011
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MxtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MxtProtocolDecoder decoder = new MxtProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "01a631144c7e0008643ad2f456fb2d49747cfe4cbe0ffd002008800000001021000fd43d3f1403000000ff300000f42760001031102445a81fda04"));
+
+ verifyPosition(decoder, binary(
+ "01a631361e7a00082471418b052a2c46b587ffc01ae3fd000008800000000000003345422203000000f000f00000000000ea1e04"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d00086440628d226e2bc26a97feac8a3afd10210010308000000000000018003d2b10240000005e2f0000f427f21031feff0000593804"));
+
+ verifyPosition(decoder, binary(
+ "01a631bd777d0008646e319e17292ce86798fed4cd3afd102110211030800000102403001f15003e2b102400000034300000f4271021007b175535a7be04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cf40a98151c2cc46898fee0ce3afd1021001030c0000006102116072e003829bb00000036102100001024000000062b0000f42730004b06a6384b4304"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d00086468457a466a2bc26a97feac8a3afd10212010308000000000001fe1053d291024000000922f0000f4271021007b17553599bb04"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d0008648645ec486a2bc26a97feac8a3afd1021001030c0000000001419eb05372b1024000000982a0000f4271021007b17000010308c04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cfa0af3151c2c126798febace3afd1021801030c0000006102122082f003e29bb00000037102100001024000000ab2f0000f42730004b060000488c04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cfe0a4b161c2c126798febace3afd1021801030800000071021240731003e2abb00000038102100001024000000c12f0000f42730004b06a638633104"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d0008648645ec486a2bc26a97feac8a3afd1021001030c0000000001419eb05372b1024000000982a0000f4271021007b17000010308c04"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java
new file mode 100644
index 000000000..2db4afbf2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NavigilProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NavigilProtocolDecoder decoder = new NavigilProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01004300040020000000f60203080200e7cd0f510c0000003b00000000000000"));
+
+ verifyPosition(decoder, binary(
+ "0100b3000f0024000000f4a803080200ca0c1151ef8885f0b82e6d130400c00403000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
new file mode 100644
index 000000000..0ebfeacd2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class NavisFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeNtcb() throws Exception {
+
+ NavisFrameDecoder frameDecoder = new NavisFrameDecoder();
+
+ verifyFrame(binary(
+ "404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080"),
+ frameDecoder.decode(null, null, binary("404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080")));
+
+ }
+
+ @Test
+ public void testDecodeFlex10() throws Exception {
+
+ NavisFrameDecoder frameDecoder = new NavisFrameDecoder();
+
+ frameDecoder.setFlexDataSize(73);
+
+ verifyFrame(binary(
+ "7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f"),
+ frameDecoder.decode(null, null, binary("7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f")));
+
+ verifyFrame(binary(
+ "7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2"),
+ frameDecoder.decode(null, null, binary("7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
new file mode 100644
index 000000000..33a6bab24
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
@@ -0,0 +1,79 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class NavisProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeNtcb() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404E5443010000007B000000130044342A3E533A383631373835303035323035303739"));
+
+ verifyNull(decoder, binary(
+ "404E5443010000007B000000130047372A3E533A383631373835303035313236303639"));
+
+ verifyPosition(decoder, binary(
+ "404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080"),
+ position("2016-11-11 21:00:04.000", true, 53.74336, 87.14437));
+
+ verifyPositions(decoder, binary(
+ "404e544300000000040000005a00c6812a3e410125e3a60700011705071503011030210c0000fa200910e6000000000000000000000001082106150010ae97643f88a39f3f0000000090001fcc6c450000000000000000000000000000000000000000000000f6808080"));
+
+ verifyPositions(decoder, binary(
+ "404e544301000000000000005a002e6c2a3e410125d7540100001512233a0b0a0f08026300000a000b000b00020000000000000000000c12233b0b0a0f03fd6d3f0fde603f00000000ba0051e0c845000000000000000000000000000000000000000000000080808080"));
+
+ verifyPositions(decoder, binary(
+ "404E5443010000007B0000005A0050692A3E410125DB0E00000015110707110A0C0880630000AA39A2381600020000000000000000000C110708110A0CB389793F1AEF263F00000000120034F516440000000000000000000000FAFF000000FAFF000000FAFF80808080"));
+
+ verifyPosition(decoder, binary(
+ "404e544301000000cdfbf5027200852e2a3e5406aa170000c11116162410001310a9110e80996b281003000a0008000000000000000000d207d207ffffff00fbff00fbff00fbff00fbff00fbff00fbff00fbff2d808080ffffffffffff2b161624100013509b0302b0f89201830500000000000037002fb8cf43eed5843a35003500"),
+ position("2019-01-16 22:22:36.000", true, 56.31800, 44.01523));
+
+ verifyPositions(decoder, binary(
+ "404e54430100000045635902730081972a3e4101060b7e0e000b171328050d00133029110e00bc6141100200000000000000000000000000d207d307ffffff00fbff00fbff00fbff00fbff00fbff00fbff00fbff02808080ffffffffffff4f1328050d001371cd0302c5109101a60300000000000000003d1b37470000000096009600"));
+ }
+
+ @Test
+ public void testDecodeFlex10() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404e544301000000c9b5f602130046c52a3e533a383639363936303439373232383235"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000aaecf6021300c8712a3e464c4558b00a0a45ffff300a08080f8388"));
+
+ verifyPosition(decoder, binary(
+ "7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f"),
+ position("2019-01-17 10:23:20.000", true, 56.33996, 43.80762));
+
+ verifyPositions(decoder, binary(
+ "7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2"));
+ }
+
+ @Test
+ public void testDecodeFlex20() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404e544301000000a9eef602130043fb2a3e533a383639363936303439373337333835"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000a9eef6021a003f8e2a3e464c4558b014147afffff008080800000e00000000000000"));
+
+ verifyPosition(decoder, binary(
+ "7e5428000000280000002111d16b435c00a900154bd16b435ce19e030259f6920133050000b7623e429300c9e7f03f2ba45a3e1f001f007b6c5910850f0100001629080a000000000000060947"),
+ position("2019-01-19 18:26:25.000", true, 56.31952, 44.01423));
+
+ verifyPositions(decoder, binary(
+ "7e4101270000000b17b16b435c00a9000d4bb26b435caaa2030229f29201620500000000000093004493d53fee892d3e1f001f00ac6c591081f00000001700080a0000000000000609f2"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java
new file mode 100644
index 000000000..a8db30476
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NeosProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NeosProtocolDecoder decoder = new NeosProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">12345678,1,1,070201,144111,W05829.2613,S3435.2313,,00,034,25,00,126-000,0,3,11111111*2d!\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java
new file mode 100644
index 000000000..7c02402b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NoranProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NoranProtocolDecoder decoder = new NoranProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0d0a2a4b57000d000080010d0a"));
+
+ verifyPosition(decoder, binary(
+ "34000800010b0000000000003f43bb8da6c2ebe229424e523039423233343439000031362d30392d31352030373a30303a303700"));
+
+ verifyPosition(decoder, binary(
+ "28003200c380000000469458408c4ad340ad381e3f4e52303947313336303900000001ff00002041"));
+
+ verifyPosition(decoder, binary(
+ "28003200c38000d900fcc97a416b1a7a42b43eef3d4e523039473034383737000000000092fcda4a"));
+
+ verifyPosition(decoder, binary(
+ "3400080001090000000000001D43A29BE842E62520424E523039423036363932000031322D30332D30352031313A34373A343300"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c000000000080a3438e20944149bd07c24e523039423139323832000031352d30342d32362030383a34333a353300"));
+
+ verifyNull(decoder, binary(
+ "0f0000004e52303946303431353500"));
+
+ verifyPosition(decoder, binary(
+ "22000800010c008a007e9daa42317bdd41a7f3e2384e523039463034313535000000"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c0000000000001c4291251143388d17c24e523039423131303930000031342d31322d32352030303a33333a303700"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c00000000000000006520944141bd07c24e523039423139323832000031352d30342d32352030303a30333a323200"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java
new file mode 100644
index 000000000..38599e0ba
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class NoranProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ NoranProtocolEncoder encoder = new NoranProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary(
+ "0d0a2a4b5700440002000000000000002a4b572c3030302c3030372c3030303030302c302300000000000000000000000000000000000000000000000000000000000d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
new file mode 100644
index 000000000..8a00207eb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class NvsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NvsFrameDecoder decoder = new NvsFrameDecoder();
+
+ assertEquals(
+ binary("0012333537303430303630303137383234312e38"),
+ decoder.decode(null, null, binary("0012333537303430303630303137383234312e38")));
+
+ assertEquals(
+ binary("cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a"),
+ decoder.decode(null, null, binary("cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java
new file mode 100644
index 000000000..9a516e733
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NvsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NvsProtocolDecoder decoder = new NvsProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0012333537303430303630303137383234312e38"));
+
+ verifyNull(decoder, binary(
+ "0012313233343536373839303132333435312E31"));
+
+ verifyPositions(decoder, binary(
+ "cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a"));
+
+ verifyPositions(decoder, binary(
+ "CCCCCCCC00FE00007048860DDF79020446a6f1ce010f14f650209cca80006f00d6040004010300030101150316030001460000015d0046a6f1dc0d0f14ffe0209cc580006e00c7050001010300030101150316010001460000015e0046a6f1ea0e0f150f00209cd20000950108040000010300030101150016030001460000015d0046a6f1ff0b0f150a50209cccc000930068040000010300030101150016030001460000015b006123"));
+
+ verifyPositions(decoder, binary(
+ "cccccccc0217000144b9ddf2aca002055683f72b01165d80632139a3c800ab00ce0a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b7c700000000e100000001005683f74901165d80632139a3c800ab012a0a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b8c700000000e100000001005683f76801165d80632139a3c800ab00590a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b9c700000000e100000001005683f78601165d80632139a3c800ab00c80a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000bac700000000e100000001005683f7a401165d80632139a3c800ab01310a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000bbc700000000e100000001001d72"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java
new file mode 100644
index 000000000..4cafd7612
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NyitechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NyitechProtocolDecoder decoder = new NyitechProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "4040690030313436383230303238373201201c0c12031a308080801c0c12031a3007d67e7e08aceb841002000000ae08000000000000000000000000001e002900f0ffdd002700f2ffe0002700f2ffe1002400f0ffdf002400f3ffe3008a00ffff01010000a9c70d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040390030313436383230303238373203200100010c000000001c0c1203192a1b0c12171d3104fed87d089288801000000000000011ec0d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040480030313436383230303238373201101c0c12031a2907fa7e7e08b8eb841002000000bc080101040904040300010100000a818283848586878862611c0c12031a293f9c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "40404b003247512d313630313030313901101e0b100604190c02c83707f887ac0f000000002d130101030304020000010100000d426162636465666768696a6ba51e0b1006041965c30d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040490030313436383230303238373202201c0c120319348080001b0c12171d3104fed87d0892888010000000000000000000000000000000000000008b00ffff010100008a480d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java
new file mode 100644
index 000000000..4c33d1766
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ObdDongleProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ObdDongleProtocolDecoder decoder = new ObdDongleProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "55550003383634383637303232353131303135010009010011023402010201ABAAAA"));
+
+ verifyPosition(decoder, binary(
+ "5555000338363438363730323235313130313503000100010355AABBCC184F1ABC614E21C1FA08712A84ABAAAA"),
+ position("2015-07-18 20:49:16.000", true, 22.12346, -123.45678));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java
new file mode 100644
index 000000000..c79978f88
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OigoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OigoProtocolDecoder decoder = new OigoProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "7e002e000000146310002523830400001bfb000369150f310c0591594d062ac0c0141508011303cd63101604fd00000000"));
+
+ verifyPosition(decoder, binary(
+ "0103537820628365110310410790660962521813380026EE4EFF8593AA0065003E00794C020600100500000000"));
+
+ verifyPosition(decoder, binary(
+ "0E03537820628344660204043255862749531B100E0026EE3AFF8593A3FFFE00BF00044C20090710C300000000"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b190203001ac000ff72eedd00370097238b4c34116a130b000094d9"));
+
+ verifyPosition(decoder, binary(
+ "1d035378206638500203340201271426226b19020c001ab144ff72f74d005f0097298a4c1d066d130b000094de"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b191016001c04e5ff760081013d002900814c1a0f5e130b00009576"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff02d0690e000220690e0002200696dbd204bdfde31a070000b307101135de106e05f500000000010908010402200104ffff8001"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff02d1690e00051f690e00051f0696dbd204bdfde31a070000b307100f35c0106305f500000000010908010402200104ffff8001"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff0d82691300001669130000160696dbd804bdfdbb1a0800000007101035a2106905f500000000010908010402200104ffff8001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
new file mode 100644
index 000000000..e2f72c161
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OkoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OkoProtocolDecoder decoder = new OkoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "{861694033681089,045403.00,A,4924.14181,N,03207.43787,E,0.080,,151117,07,0.00,01,24.8,1,02,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{045411.00,A,4924.14243,N,03207.43754,E,0.172,,151117,07,0.00,F9,28.1,2,C2,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{861001001016415,115031.000,A,4804.101180,N,02255.227002,E,4.121,111.0,211215,6,0.00,F7,13.6,1,00"));
+
+ verifyPosition(decoder, text(
+ "{132810.000,A,4926.4243,N,03203.6831,E,25.0,183,131011,07,5.69,05,14.1,1,82,3N5"));
+
+ verifyPosition(decoder, text(
+ "{115034.000,A,4804.098944,N,02255.233436,E,7.858,120.9,211215,7,0.00,F7,13.7,1,00"));
+
+ verifyPosition(decoder, text(
+ "{115038.000,A,4804.091227,N,02255.250213,E,17.621,128.1,211215,8,0.00,00,13.7,2,00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java
new file mode 100644
index 000000000..a04cf4e72
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OpenGtsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OpenGtsProtocolDecoder decoder = new OpenGtsProtocolDecoder(null);
+
+ verifyPosition(decoder, request(
+ "/?id=999000000000003&gprmc=$GPRMC,082202.0,A,5006.747329,N,01416.512315,E,0.0,,131018,1.2,E,A*2E"));
+
+ verifyPosition(decoder, request(
+ "/?id=gprmc_999000000000003&gprmc=$GPRMC,143013.0,A,5006.728217,N,01416.437869,E,0.0,329.6,281017,1.2,E,A*0E"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456789012345&dev=dev_name&acct=account&batt=0&code=0xF020&alt=160.5&gprmc=$GPRMC,191555,A,5025.46624,N,3030.39937,E,0.000000,0.000000,200218,,*2F"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java
new file mode 100644
index 000000000..bb5f1f135
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OrionProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OrionProtocolDecoder decoder = new OrionProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "5057000137bf6236235a0331b5c6e402a3b5ecff5102980003000e0c1d172936080e0c1d172936b03b01000882050000008e080000000000008c0300940500000084030085030003067600900113150000000000000000000000000000000000000004a4c8"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242B440901ADE97D0163143B07B003000000000D041917382D000B0101000511000000000682050000008E080000000000008C0300840300850300090A0000000048010000008AFC"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242C440901ADE97D0163143B07B003000000000D041917382D000B0101000513000000000682050000008E080000000000008C0300840300850300090A000000003BFEFFFF01FAE5"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242D440901ADE97D0163143B07B003000000000D041917382D000B0101000514000000000682050000008E080000000000008C0300840300850300090A00000000FDFDFFFF023721"));
+
+ verifyPositions(decoder, binary(
+ "505700412ac86236354009114d20e402210f1f00d204000000000e06110d3414000b0101001228000000000682050000008e080000000000008c030084030085030003067b006801000930"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java
new file mode 100644
index 000000000..3c38bd831
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OsmAndProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OsmAndProtocolDecoder decoder = new OsmAndProtocolDecoder(null);
+
+ verifyNotNull(decoder, request(
+ "/?id=123456&timestamp=1377177267&cell=257,02,16,2224&cell=257,02,16,2223,-90&wifi=00-14-22-01-23-45,-80&wifi=00-1C-B3-09-85-15,-70"));
+
+ verifyNull(decoder, request(
+ "/?timestamp=1377177267&lat=60.0&lon=30.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=902064&lat=42.06288&lon=-88.23412&timestamp=2016-01-27T18%3A55%3A47Z&hdop=6.0&altitude=224.0&speed=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=902064&lat=42.06288&lon=-88.23412&timestamp=1442068686579&hdop=6.0&altitude=224.0&speed=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?lat=49.60688&lon=6.15788&timestamp=2014-06-04+09%3A10%3A11&altitude=384.7&speed=0.0&id=353861053849681"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&lat=60.0&lon=30.0&speed=0.0&bearing=0.0&altitude=0&hdop=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&lat=60.0&lon=30.0"));
+
+ verifyPosition(decoder, request(
+ "/?lat=60.0&lon=30.0&speed=0.0&heading=0.0&vacc=0&hacc=0&altitude=0&deviceid=123456"));
+
+ verifyPosition(decoder, request(
+ "/?id=861001000719969&lat=41.666667&lon=-0.883333&altitude=350.059479&speed=0.000000&batt=87"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&location=60.0,30.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456789012345&timestamp=1504763810&lat=40.7232948571&lon=-74.0061408571&bearing=7.19889788244&speed=40&ignition=true&rpm=933&fuel=24"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java
new file mode 100644
index 000000000..248920e21
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OwnTracksProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OwnTracksProtocolDecoder decoder = new OwnTracksProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"_type\":\"location\",\"acc\":15,\"alt\":440,\"batt\":46,\"conn\":\"w\",\"lat\":46.0681247,\"lon\":11.1512805,\"t\":\"u\",\"tid\":\"5t\",\"tst\":1551874878,\"vac\":2,\"vel\":0}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"lon\":2.29513,\"lat\":48.85833,\"tst\":1497349316,\"_type\":\"location\",\"tid\":\"JJ\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"cog\":271,\"lon\":2.29513,\"acc\":5,\"vel\":61,\"vac\":21,\"lat\":48.85833,\"tst\":1497349316,\"alt\":167,\"_type\":\"location\",\"tid\":\"JJ\",\"t\":\"u\",\"batt\":67}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"lat\":48.85,\"lon\":2.295,\"_type\":\"location\",\"tid\":\"JJ\",\"tst\":1497476456}")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java
new file mode 100644
index 000000000..4b9739242
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PathAwayProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PathAwayProtocolDecoder decoder = new PathAwayProtocolDecoder(null);
+
+ verifyPosition(decoder, request(
+ "?UserName=name&Password=pass&LOC=$PWS,1,\"Roger\",,,100107,122846,45.317270,-79.642219,45.00,42,1,\"Comment\",0*58"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java
new file mode 100644
index 000000000..03d0dd7b9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PiligrimProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PiligrimProtocolDecoder decoder = new PiligrimProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST,
+ "/bingps?imei=868204005544720&csq=18&vout=00&vin=4050&dataid=00000000",
+ binary("fff2200d4110061a32354f3422310062000a0005173b0000a101000300005e00fff2200d4110100932354f2b22310042000b000e173b00009f01000700006000")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java
new file mode 100644
index 000000000..61a057dd7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PretraceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PretraceProtocolDecoder decoder = new PretraceProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(867967021915915U1110A1701201500102238.1700N11401.9324E000264000000000009001790000000,&P11A4,F1050^47"));
+
+ verifyPosition(decoder, text(
+ "(864244029498838U1110A1509250653072238.1641N11401.9213E000196000000000406002990000000,&P195%,T1050,F14A5,R104C51E47B^30"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
new file mode 100644
index 000000000..1b2780325
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class PretraceProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() throws Exception {
+
+ PretraceProtocolEncoder encoder = new PretraceProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("(123456789012345D221300,300,,^69)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ PretraceProtocolEncoder encoder = new PretraceProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "D21012");
+
+ assertEquals("(123456789012345D21012^44)", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java
new file mode 100644
index 000000000..dbc1665fb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PricolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PricolProtocolDecoder decoder = new PricolProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "3c5052493030303350020000011402110b222b0455152e4e001de819ca450000000000000003820249000000000000000000000000000000000000000040003e"));
+
+ verifyNotNull(decoder, binary(
+ "3c544553303030324b02000000000000000000000000000000000000000000000000000000037c01f4000000000000000000000000000000000000000000003e"));
+
+ verifyPosition(decoder, binary(
+ "3c4944303030303150FFFFFFFF1C050C121D38045D09FA4e001DE815F4452FFFFFFFFFFF03FF03FF03FF03FF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF113e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java
new file mode 100644
index 000000000..5f6f564b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ProgressProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ProgressProtocolDecoder decoder = new ProgressProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "020037000100000003003131310f003335343836383035313339303036320f00323530303136333832383531353535010000000100000000000000e6bb97b6"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java
new file mode 100644
index 000000000..e7d87d583
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt3000ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt3000ProtocolDecoder decoder = new Pt3000ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%356939010012099,$GPRMC,124945.752,A,4436.6245,N,01054.4634,E,0.11,358.52,060408,,,A,+393334347445,N028d"),
+ position("2008-04-06 12:49:45.000", true, 44.61041, 10.90772));
+
+ verifyPosition(decoder, text(
+ "%356939010014433,$GPRMC,172821.000,A,4019.5147,N,00919.1160,E,0.00,,010613,,,A,+393998525043,N098d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java
new file mode 100644
index 000000000..487a8500c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt502FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt502FrameDecoder decoder = new Pt502FrameDecoder();
+
+ verifyFrame(
+ binary("24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"),
+ decoder.decode(null, null, binary("bffbf6d10324504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a28012800d0a")));
+
+ verifyFrame(
+ binary("244655533836353332383032363234333836342c3531302d56312e31322c4131312d56332e30"),
+ decoder.decode(null, null, binary("bffb192d00244655533836353332383032363234333836342c3531302d56312e31322c4131312d56332e300d0d")));
+
+ verifyFrame(
+ binary("24504f532c313336303030303237372c3138323234312e3030302c412c303834362e303839362c4e2c30373535322e313733382c572c31332e35382c32362e38382c3239313031372c2c2c412f30303030302c30303030302f3134322c302c302c302f36323739323930302f2f6636352f2f23"),
+ decoder.decode(null, null, binary("bffb57d50124504f532c313336303030303237372c3138323234312e3030302c412c303834362e303839362c4e2c30373535322e313733382c572c31332e35382c32362e38382c3239313031372c2c2c412f30303030302c30303030302f3134322c302c302c302f36323739323930302f2f6636352f2f230a24504f532c313336303030303237372c3138323235312e3030302c412c303834362e313234382c4e2c30373535322e313534352c572c31352e35322c33362e39332c3239313031372c2c2c412f30303030302c30303030302f3134312c302c302c302f36323739333030302f2f6636382f2f230a24504f532c313336303030303237372c3138323332342e3030302c412c303834362e323633362c4e2c30373535322e303630352c572c31382e39342c32392e39302c3239313031372c2c2c412f30303030302c30303030302f3133652c302c302c302f36323739333330302f2f6639372f2f230a24504f532c313336303030303237372c3138323332362e3030302c412c303834362e323733302c4e2c30373535322e303535342c572c31392e31322c33302e34322c3239313031372c2c2c412f30303030302c30303030302f3134302c302c302c302f36323739333330302f2f6639382f2f230a")));
+
+ verifyFrame(
+ binary("24504f532c3836353332383032363234333836342c3134343733352e3030302c412c313333322e373038332c4e2c3230342e363833312c452c302e302c3233302e30302c3032303531372c2c2c412f30303030302c31302f312c302f3233342f2f4646392f"),
+ decoder.decode(null, null, binary("24504f532c3836353332383032363234333836342c3134343733352e3030302c412c313333322e373038332c4e2c3230342e363833312c452c302e302c3233302e30302c3032303531372c2c2c412f30303030302c31302f312c302f3233342f2f4646392f0d0a")));
+
+ verifyFrame(
+ binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f"),
+ decoder.decode(null, null, binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f0d0a")));
+
+ verifyFrame(
+ binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f"),
+ decoder.decode(null, null, binary("bffb1b6a0024504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f0d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
new file mode 100644
index 000000000..cb1c1eb0e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
@@ -0,0 +1,86 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Pt502ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt502ProtocolDecoder decoder = new Pt502ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO3821-1,1156802639,022125.000,A,0707.0014,N,07307.3725,W,0.0,0.1,110418,,,A/00000,00000/0,0,0,0/500//fd4//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,1360000277,182241.000,A,0846.0896,N,07552.1738,W,13.58,26.88,291017,,,A/00000,00000/142,0,0,0/62792900//f65//#"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO0-1,1360000260,123012.000,A,0913.9644,N,07548.8345,W,0.0,309.8,111017,,,A/00000,10000/0,0,0,0/64551600//f98//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,865328026243864,151105.000,A,1332.7096,N,204.6787,E,0.0,10.00,050517,,,A/00000,10/1,0/234//FD9/"));
+
+ verifyNull(decoder, buffer(
+ "$FUS865328026243864,510-V1.12,A11-V3.0"));
+
+ verifyPosition(decoder, buffer(
+ "$HDA,20007,134657.000,A,0626.1607,N,00330.2245,E,33.38,81.79,041016,,,A/00010,00000/270,0,0,0/19948900//fa4//"));
+
+ verifyPosition(decoder, buffer(
+ "$HDB,20007,134708.000,A,0626.1759,N,00330.3192,E,26.55,80.37,041016,,,A/00010,00000/23b,0,0,0/19949100//fa4//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,20007,134704.000,A,0626.1698,N,00330.2870,E,31.23,79.58,041016,,,A/00010,00000/26c,0,0,0/19949100//fa4//#"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO6608,115099,133140.000,A,1307.1238,N,05936.4194,W,0.00,21.50,290816,,,A/00010,00000/0,0,0,0/185100//f59/"));
+
+ verifyPosition(decoder, buffer(
+ "$DFR,40456789,083125.000,A,2232.0971,N,11400.9504,E,0.0,5.00,090714,,,A/00000,00/0,0/200076//FE7/"));
+
+ verifyPosition(decoder, buffer(
+ "$FDA,40456789,083125.000,A,2232.0971,N,11400.9504,E,0.0,5.00,090714,,,A/00000,00/0,0/200076//FE7/"));
+
+ verifyAttribute(decoder, buffer(
+ "$CPA,40456789,083125.000,A,2232.0971,N,11400.9504,E,7.62,265.24,291117,,,A/00000,00000/0/1200//#"), Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+
+ verifyPosition(decoder, buffer(
+ "$POS,216769295715,163237.000,A,3258.1738,S,02755.4350,E,0.00,215.88,100915,,,A/0000,0//232300//5b3/"),
+ position("2015-09-10 16:32:37.000", true, -32.96956, 27.92392));
+
+ verifyPosition(decoder, buffer(
+ "$POS,11023456,033731.000,A,0335.2617,N,09841.1587,E,0.00,88.12,210615,,,A/0000,0/1f8/388900//f33//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6094,205523.000,A,1013.6223,N,06728.4248,W,0.0,99.3,011112,,,A/00000,00000/0/23895000//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6120,233326.000,V,0935.1201,N,06914.6933,W,0.00,,151112,,,A/00000,00000/0/0/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6002,233257.000,A,0931.0430,N,06912.8707,W,0.05,146.98,141112,,,A/00010,00000/0/5360872"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6095,233344.000,V,0933.0451,N,06912.3360,W,,,151112,,,N/00000,00000/0/1677600/"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO0,6091,233606.000,A,0902.9855,N,06944.3654,W,0.0,43.8,141112,,,A/00010,00000/0/224000//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,353451000164,082405.000,A,1254.8501,N,10051.6752,E,0.00,237.99,160513,,,A/0000,0/0/55000//a71/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,012896008586486,154215.000,A,0118.0143,S,03646.9144,E,0.00,83.29,180714,,,A/0000,0/0/29200//644/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,1151000,205326.000,A,0901.3037,N,07928.2751,W,48.79,30.55,170814,,,A/00010,10000/0,0,0,0/15986500//fb8/"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
new file mode 100644
index 000000000..a6c8bb50f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
@@ -0,0 +1,69 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Pt502ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "#PTI300");
+
+ assertEquals("#PTI300\r\n", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeOutputControl() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 2);
+ command.set(Command.KEY_DATA, "1");
+
+ assertEquals("#OPC2,1\r\n", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeTimezone() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+8");
+
+ assertEquals("#TMZ8\r\n", encoder.encodeCommand(command));
+
+ }
+
+
+ @Test
+ public void testEncodeAlarmSpeed() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SPEED);
+ command.set(Command.KEY_DATA, 120);
+
+ assertEquals("#SPD120\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java
new file mode 100644
index 000000000..1ba8d25c7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt60ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt60ProtocolDecoder decoder = new Pt60ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "@B#@|01|006|864891030184954|9425010450971470|20181213093127|2|1|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|006|864891030184954|9425010450971470|20181213093235|40412,10461,1,425,4|2|1|"));
+
+ verifyNotNull(decoder, text(
+ "@B#@|01|001|864891030184852|9425010450971470|1|84|20181205161005|40412,10461,1,425,10|2|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|864891030184852|9425010450971470|1|45|20181127122717|32.701093|35.570938|1|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|003|864891030184954|9425010450971470|S6_EN_A_V1.3.7|0|66|20181122113251|40412,10461,1,425,18|49382,10461,1,425,9|40411,10461,1,425,7|2|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|033|864891030184954|9425010450971470|0|4|20181120151744|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|111112222233333|8888888888888888|1|55|20160715150323|37.615124|125.48276|111.059279|49.346383|1|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|111112222233333|8888888888888888|1|55|20160715150323|37.615124|125.48276|1|"));
+
+ verifyAttributes(decoder, text(
+ "@G#@,V01,14,357653051059785,9404223001501310,20180419165604,101,26,"));
+
+ verifyAttributes(decoder, text(
+ "@G#@,V01,13,357653051059785,9404223001501310,20180419112656,1180,"));
+
+ verifyPosition(decoder, text(
+ "@G#@,V01,6,111112222233333,8888888888888888,20150312010203,23.2014050;104.235212,"));
+
+ verifyNull(decoder, text(
+ "@G#@,V01,1,353882080015633,9460025014649193,"));
+
+ verifyNull(decoder, text(
+ "@G#@,V01,43,105681639001701,9460000412618231,20180410092923,5,460;0;9763;3852;-63|460;0;9763;3851;-26|460;0;9763;4080;-22|460;0;9763;3593;-18|460;0;9763;3591;-10,5,14:b8:37:26:64:88;004300680069006e0061004e00650074002d006e0047006e0058;-76|08:9b:4b:93:b5:b1;005400480049004e004b0052004100430045;-77|ec:3d:fd:c9:38:4a;004b00460052006f0075007400650072;-78|b0:d5:9d:c6:f8:82;003300360030514d8d390057006900460069002d00380032;-81|02:fa:84:3b:fa:6a;00470075006500730074005f0032002e003400470048007a;-82,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java
new file mode 100644
index 000000000..165027351
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RaveonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RaveonProtocolDecoder decoder = new RaveonProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$PRAVE,0001,0001,3308.9051,-11713.1164,195348,1,10,168,31,13.3,3,-83,0,0,,1003.4*66"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java
new file mode 100644
index 000000000..668879787
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RecodaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RecodaProtocolDecoder decoder = new RecodaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01100020480000000300000030393535360000000000000001000000303030303000000000000000000000000000000000000000006100004531313037353500ffffffffffff0000"));
+
+ verifyNull(decoder, binary(
+ "01200020100000000300000002000000"));
+
+ verifyNull(decoder, binary(
+ "0110000008000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java
new file mode 100644
index 000000000..779e21823
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RetranslatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RetranslatorProtocolDecoder decoder = new RetranslatorProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "74000000333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E666F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB0000001200047077725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001"));
+
+ verifyPosition(decoder, binary(
+ "1f010000333533353439303930303934373330005b129b5f000000010bbb000000270102706f73696e666f004e3be14e5ec356c0e0e92f6201282c400000000000000000000000130d0bbb0000000a00036d636300000002c00bbb0000000a00036d6e6300000000010bbb0000000a00036c616300000001490bbb0000000e000363656c6c5f696400000056590bbb0000000a000361636300000000000bbb000000100003646174615f6d6f6465000000000e0bbb0000001200036770735f7265616c5f757000000000000bbb0000000d000373657269616c000000089b0bbb0000001000016d73675f747970650030783232000bbb000000110004637573746f6d0000000000000033400bbb0000001200046d696c65616765000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java
new file mode 100644
index 000000000..73d07efd6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RitiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RitiProtocolDecoder decoder = new RitiProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "3b28a2a2056315316d4000008100000000000000005f710000244750524d432c3138303535332e3030302c412c353532342e383437312c4e2c30313133342e313837382c452c302e30302c2c3032313231332c2c2c412a37340d0a00000000000000000000000000000000040404"));
+
+ verifyPosition(decoder, binary(
+ "3b2864a3056300006d40000003000000000000000000000000244750524d432c3231313734332e3030302c412c313335372e333637352c4e2c31303033362e363939322c452c302e30302c2c3031303931342c2c2c412a37380d0a00000000000000000000000000000000040404"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java
new file mode 100644
index 000000000..2e3853f86
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RoboTrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RoboTrackFrameDecoder decoder = new RoboTrackFrameDecoder();
+
+ verifyFrame(
+ binary("00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e"),
+ decoder.decode(null, null, binary("00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..0c969ab68
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RoboTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RoboTrackProtocolDecoder decoder = new RoboTrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e"));
+
+ verifyPosition(decoder, binary(
+ "03e020bb5a034409034862120210a9e105000000000000b9"));
+
+ verifyPosition(decoder, binary(
+ "03f120bb5a30460903426312021798e105000000000000cd"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
new file mode 100644
index 000000000..12f63ef7d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RuptelaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RuptelaProtocolDecoder decoder = new RuptelaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "002e000315bc70d3e2ff0f4f42443130302e30312e30382e30300000c2b30ea77e430000601b000001f40000003c00144aa0"));
+
+ verifyAttributes(decoder, binary(
+ "0011000315A07F440B1D07534554494f20636f6e66696775726174696f6e2064617461206f6b341C"));
+
+ verifyAttributes(decoder, binary(
+ "0044000313612d76c5cb0744494e313d312c44494e323d302c44494e333d302c44494e343d302c444f5554313d312c444f5554323d312c41494e313d31372c41494e323d3236ac80"));
+
+ verifyPositions(decoder, binary(
+ "000B00000B1A29F64B1A0902FF4E9CAF2C07D608F11A1480BA015030303130FF4E9CAF2C07D608F11A1480BA0250303031318C91"));
+
+ verifyPositions(decoder, binary(
+ "01a4000315bc70f9b69244000458068f4a0030000d11398a1c0c19fd056524040b000c0a00090c0005010031f40032fd0033f200ce47002400002500001c010199000195010196010086000900aa0000001e0ff000d3ffff0043ffff01930000019200000194000002220000022300000200300000000200af000e872401008e000000000000000058068f4a0031000d11398a1c0c19fd056524040b000c0a00090400870000880000a90000820010008b0002021e0000021f0000021d0000021c0000022400000225000000890000008505f00220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f4a0032000d11398a1c0c19fd056524040b000c0a000905019f01005800001b1f00ad0000cfb10b02290000022a0000022b0000022c0000022d00000012000000130000001d367400c52f8000740055023e0502060097000000000096000058520041007746cb00d0000003f1005c0007c21b0072001864880058068f4a0033000d11398a1c0c19fd056524040b000c0a000900000001008e0000000000000000e815"));
+
+ verifyPositions(decoder, binary(
+ "033d000315bc70f9b69244000858068f3b0030010d11354e1c0c17a5055d54560c00000900050c0005010031f30032fb0033f300ce00002400002500001c010199000195010196010086000900aa0000001e0ff300d3ffff0043ffff01930000019200000194000002220000022300000200300000000000af000e872401008e000000000000000058068f3b0031010d11354e1c0c17a5055d54560c00000900050400870000880000a90000820010008b0000021e0000021f0000021d0000021c0000022400000225000000890000008500000220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f3b0032010d11354e1c0c17a5055d54560c000009000505019f01005800001b1f00ad0000cfac0b02290000022a0000022b0000022c0000022d00000012000000130000001d31b100c5000000740000023e0502060097000000000096000058520041007746be00d0000003f1005c0007c2150072001864880058068f3b0033010d11354e1c0c17a5055d54560c000009000500000001008e000000000000000058068f3b0130000d11354e1c0c17a5055d54560d00000900070c0005010031f30032fb0033f300ce00002400002500001c010199000195010196010086000900aa0000001e0ff300d3ffff0043ffff01930000019200000194000002220000022300000200300000000000af000e872401008e000000000000000058068f3b0131000d11354e1c0c17a5055d54560d00000900070400870000880000a90000820010008b0000021e0000021f0000021d0000021c0000022400000225000000890000008500000220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f3b0132000d11354e1c0c17a5055d54560d000009000705019f01005800001b1f00ad0000cfac0b02290000022a0000022b0000022c0000022d00000012000000130000001d31ae00c5000000740000023e0502060097000000000096000058520041007746be00d0000003f1005c0007c2150072001864880058068f3b0133000d11354e1c0c17a5055d54560d000009000700000001008e0000000000000000084d"));
+
+ verifyPositions(decoder, binary(
+ "0050000310f5615f419c0100015613d8ed0000fff5b37a035af37801e700000900000d07071b0c020003001c01202cad000500064302a81d33e61e100116317cd3ffff174ad60241000077fa960000f232003c2e"));
+
+ verifyPositions(decoder, binary(
+ "00560003116e7438a7a50100015565cbb9000020fd21300f113f4600005f000600090d090805011b13cf00020003001c012029ad00041d31dd1e0ebd160000c50000047200000000d0000000004100016a2a960000a5a300c9ee"));
+
+ verifyPositions(decoder, binary(
+ "00a10003116e7438a7a5010002553dddbe000020fddaff0f12289b007200000600000c070805011b18cf00020003001c01201dad01041d32d81e0d7d160000c50000047200000000d000000000410000b1ae960000a5a300553dddd4000020fdd96f0f122bfe005c16f80700050b090805011b18cf00020003001c01201ead01041d338a1e0d8d160000c50000047200000000d000000000410000b1bd960000a5a3001681"));
+
+ verifyPositions(decoder, binary(
+ "007900000b1a2a5585c30100024e9c036900000f101733208ff45e07b31b570a001009090605011b1a020003001c01ad01021d338e16000002960000601a41014bc16d004e9c038400000f104fdf20900d20075103b00a001308090605011b1a020003001c01ad01021d33b116000002960000601a41014bc1ea0028f9"));
+
+ verifyPositions(decoder, binary(
+ "009200000c07a6bacd4701000552db5cc20000187b8b251ace478e087c044c0a000009070000000052db5cfe0000187b8ab01ace47190879044c0900000b070000000052db5d3a0000187b8b251ace474b089d044c09000009070000000052db5d760000187b8b9a1ace475c08cd044c08000009070000000052db5db20000187b8b141ace46e708b3044c08000009070000000041cb"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
new file mode 100644
index 000000000..8a00caa09
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class RuptelaProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ RuptelaProtocolEncoder encoder = new RuptelaProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, " Setio 2,1");
+
+ verifyCommand(encoder, command, binary("000b6c20536574696F20322C31eb3e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
new file mode 100644
index 000000000..7a42a71a0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class SabertekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SabertekFrameDecoder decoder = new SabertekFrameDecoder();
+
+ assertEquals(
+ binary("2c3939393939393939392c332c34302c36352c372c302c312c2d32352e3738313636362c32382e3235343730322c34302c3236382c313431342c382c35353632332c"),
+ decoder.decode(null, null, binary("022c3939393939393939392c332c34302c36352c372c302c312c2d32352e3738313636362c32382e3235343730322c34302c3236382c313431342c382c35353632332c030d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java
new file mode 100644
index 000000000..20b02841e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SabertekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SabertekProtocolDecoder decoder = new SabertekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ",999999999,3,40,65,7,0,1,-25.781666,28.254702,40,268,1414,8,55623,"));
+
+ verifyPosition(decoder, text(
+ ",999999999,4,356495040613400,89270200120171498287,+27821234123,20180525145412,60,75,15,1,1,-25.781666,28.254702,40,268,1414,8,24844,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java
new file mode 100644
index 000000000..36fd7ecba
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SanavProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SanavProtocolDecoder decoder = new SanavProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "imei=353197040023431&rmc=$GPRMC,015258.000,A,2457.8101,N,12125.5393,E,0.00,0.00,210111,,*18,AUTO,0300,2.1,10,466,97,34E7,3391,74,466,9 7,3F2D,3391,65,466,97,39C9,3391,79,466,97,3F2C,3391,81,466,97,0000,00 00,83,466,97,0000,0000,85,466,97,0000,0000,85,1,24"));
+
+ verifyPosition(decoder, text(
+ "imei=1234567890&rmc=$GPRMC,091950.00,V,5300.10000,N,00900.14000,E,0.160,,200513,,,A*68,STOP,V3.872;67%,S4,H8.3,D2.38"));
+
+ verifyPosition(decoder, text(
+ "imei=352024028982787&rmc=$GPRMC,103048.000,A,4735.0399,N,01905.2895,E,0.00,0.00,171013,,*05,AUTO-4095mv"),
+ position("2013-10-17 10:30:48.000", true, 47.58400, 19.08816));
+
+ verifyPosition(decoder, text(
+ "imei:352024028980000rmc:$GPRMC,093604.354,A,4735.0862,N,01905.2146,E,0.00,0.00,171013,,*09,AUTO-4103mv"));
+
+ verifyPosition(decoder, text(
+ "imei:352024027800000rmc:$GPRMC,000025.000,A,4735.0349,N,01905.2899,E,0.00,202.97,171013,,*03,3950mV,AUTO"));
+
+ verifyPosition(decoder, text(
+ "imei:352024020976845rmc:$GPRMC,000201.000,A,4655.7043,N,01941.3796,E,0.54,159.14,171013,,,A*65,AUTO"));
+
+ verifyPosition(decoder, text(
+ "imei:352024020976845rmc:$GPRMC,000201.000,A,4655.7043,N,01941.3796,E,0.54,159.14,171013,,,A*65,AUTO"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java
new file mode 100644
index 000000000..45e919bbb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SatsolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SatsolProtocolDecoder decoder = new SatsolProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "f0e1bf4cb2ec1600e1005f8791e901000000c959515c2cc24a03aeadcd010e01a800250001090201878e92e901000000cb59515c2dc24a03b8adcd018801a8001d0001080201325993e901000000cc59515c2fc24a03bfadcd01ab01a800220001080201dd8194e901000000cd59515c32c24a03ceadcd015801a8002500010802015f3795e905000900ce59515c32c24a03d8adcd01f600a700250001091401000000000000000000863496e901000000cf59515c34c24a03ddadcd019b00a700280001090201714197e904000600cf59515c34c24a03ddadcd019b00a7002800010a1401becd07001901"));
+
+ verifyPositions(decoder, binary(
+ "22b5bf4cb2ec1600500122b37ba020001500cb5d2f5c68c24a0310aecd016200b1003d000004000177042700a501000000000000000101020100011e00a1657ca020001500cf5d2f5c68c24a03dbadcd013501b10097000004030177042700a501000000000000000101020101011e00816e7da003004800b95e2f5c89c24a03fbadcd010000aa00000000040601011038363133353930333632333039323600383933373530323730313030323335343836363000323537303237303132333534383636004d5453000000000066640101005700e515cf047ea001000000295f2f5c89c24a03fbadcd0100009a0000000106020106dc30a8030048006bc13e5c19c24a03bfadcd010000ad00000000050600011338363133353930333632333039323600383933373530323730313030323335343836363000323537303237303132333534383636004d5453000000000066640101005700e515"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
new file mode 100644
index 000000000..48adb0ccb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SigfoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SigfoxProtocolDecoder decoder = new SigfoxProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("%7B++%22device%22%3A%222BF839%22%2C++%22time%22%3A1510605882%2C++%22duplicate%22%3Afalse%2C++%22snr%22%3A45.61%2C++%22station%22%3A%2235A9%22%2C++%22data%22%3A%2200bd6475e907398e562d01b9%22%2C++%22avgSnr%22%3A45.16%2C++%22lat%22%3A-38.0%2C++%22lng%22%3A145.0%2C++%22rssi%22%3A-98.00%2C++%22seqNumber%22%3A228+%7D=")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java
new file mode 100644
index 000000000..3a91d8482
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SiwiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SiwiProtocolDecoder decoder = new SiwiProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803932,23992,E,0,,0,1,1,0,5055,0,5,A,22.289887,70.807192,152,168,102922,090317,28,1,12,5,4098,1,0,13,0,0,0,1.0,3.1CHKS_4.82,0!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,2845,1320,Q,10,airtelgprs.com,1,1,0,0,876578,43,9,A,19.0123456,72.65347,45,0,055929,071107,22,5,1,0,3700,1210,0,2500,1230,321,0,1.1,4.0,1!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,953,R,9,,0,1,1,0,0,0,8,A,19.066145,73.002278,213,178,122738,210217,28,5,12,6,4066,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$EIT,9803849,953,R,9,,0,1,1,0,0,0,8,A,19.066145,73.002278,213,178,122738,210217,28,5,12,6,4066,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,954,E,0,,0,1,1,0,0,0,0,V,0.000000,0.000000,0,0,122855,210217,29,5,12,5,4104,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,2845,1320,A,0,,1,1,0,0,876578,43,10,A,19.0123456,72.65347,45,0,055929,071107,22,5,1,0,3700,1210,0,2500,1230,321,0,1.1,4.0,1!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,956,E,0,,0,1,1,0,0,0,3,V,19.066935,73.003383,0,111,123037,210217,28,5,12,5,4071,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java
new file mode 100644
index 000000000..bc2dac7e9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java
@@ -0,0 +1,34 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SkypatrolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SkypatrolProtocolDecoder decoder = new SkypatrolProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "000a02171101303131373232303031333537393833060200000006202020202020202020312020202020202030313137323230303133353739383320"));
+
+ verifyNull(decoder, binary(
+ "000402171101303131373232303031333537393833060200081046202020202020202020392020202020202030313137323230303133353739383320244750524d432c3134303931372e30302c412c333330322e3230313132352c532c30373133352e3837383338332c572c302e302c302e302c3036303731372c322e382c572c412a32370d0a00"));
+
+ verifyPosition(decoder, binary(
+ "0005021004FFFFFFFF0000000D313134373735383300CB000000000E11070C010184D032FB3841370000000016072B000017050032000000000000024E0C071116072C105900050000000000050000000000050000000003100260B7363B6306C11A00B73637F206BF19B73637F106B50EB73638B106BB0BB7363B6106B80AB73637F306B709000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "000500030101383637383434303031373832333336420102000c0000fa07b5e101876c5b0e0a111606131c1b5e"));
+
+ // Enfora TT8750
+ verifyNull(decoder, binary(
+ "000502000000f1143035303031393031d1df002f00000d0187120115e556ff762aa90000000000aae40005d2000ee1bc0e010a042530000000000000070004000002233c096c00ee2a00233c008500f022233c0b0500f21d233c000000fb23000000000000000000000000000000000000000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "00040200202020202020202020382020202020202030313137323230303131383531373820313220244750524d432c3232343833392e30302c412c303332382e3433383830362c4e2c30373633312e3630373731372c572c302e302c302e302c3139303731342c332e382c452c412a32420d0a00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java
new file mode 100644
index 000000000..183af899b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SmartSoleProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SmartSoleProtocolDecoder decoder = new SmartSoleProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#GTXRP=359366080000385,8,180514200051,34.041981,-118.255806,60,1,1,7,1.80,180514200051,4.16,11"));
+
+ verifyPosition(decoder, text(
+ "#GTXRP=359372090000290,11,180919105751,46.477173,6.445475,389,0,0,0,1.08,180919105751,4.10,10"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java
new file mode 100644
index 000000000..c2d74b433
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SmokeyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SmokeyProtocolDecoder decoder = new SmokeyProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "534d0300865101019383025f0403000000000b86250200000c0000028f000102f8cc0900127f08"));
+
+ verifyAttributes(decoder, binary(
+ "534d0300865101019383025f0403000000000bcf260200000c0000028f000102f8cc090012360b"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java
new file mode 100644
index 000000000..7191f31ef
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SpotProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SpotProtocolDecoder decoder = new SpotProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<messageList xmlns=\"http://v2.shared.globalstar.com\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://v2.shared.globalstar.com http://share.findmespot.com/shared/schema/spotXml-v2.xsd\">\n <header>\n <totalCount>1</totalCount>\n <mode>LIVE</mode>\n </header>\n <message>\n <id>891801957</id>\n <esn>0-3112123</esn>\n <esnName>0-3112123a</esnName>\n <messageType>NEWMOVEMENT</messageType>\n <messageDetail> SPOT Trace has detected that the asset has moved.</messageDetail>\n <timestamp>2017-12-27T13:19:38.000Z</timestamp>\n <timeInGMTSecond>1514380778</timeInGMTSecond>\n <latitude>-1.28781</latitude>\n <longitude>-47.93042</longitude>\n <batteryState>GOOD</batteryState>\n </message>\n</messageList>")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
new file mode 100644
index 000000000..70e173284
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class StarLinkProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,55,170518122023,16,,,,,,000000,1,1,0,0,0,0,0,0,10443,32722,12.664,03.910,,0,0,,01000001FDB3A9*BF"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,56,170518122023,20,,,,,,000000,1,1,0,0,0,0,0,0,10443,32722,12.664,03.910,,0,0,2,01000001FDB3A9,00000000000000000000*D9"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,57,170518122038,01,,,,,,000000,1,1,0,0,1,0,0,0,10443,32722,12.669,03.910,,0,0,0,99*6E"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,58,170518122045,19,,,,,,000000,1,1,0,0,1,0,0,0,10443,32722,12.678,03.910,,0,0*7C"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,59,170518122054,16,,,,,,000000,1,1,0,0,0,0,0,0,10443,32723,12.678,03.910,,0,0,01000001FDB3A9,01000001ACE0A6*BF"));
+
+ verifyPosition(decoder, text(
+ "$SLU031B2B,06,622,170329035057,01,170329035057,+3158.0018,+03446.6968,004.9,007,000099,1,1,0,0,0,0,0,0,,,14.176,03.826,,1,1,1,4*B0"));
+
+ verifyPosition(decoder, text(
+ "$SLU031B2B,06,624,170329035143,01,170329035143,+3158.0171,+03446.6742,006.8,326,000099,1,1,0,0,0,0,0,0,10452,8723,14.212,03.827,,1,1,1,4*6D"));
+
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3556,170314063523,19,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.870,03.790,,0,0*FC"));
+
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3555,170314063453,20,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.838,03.790,,0,0,1,,1122*74"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375153,170117051824,01,170117051823,+3203.2073,+03448.1360,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,12.655,04.085,,0,0,0,99*45"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375155,170117052615,24,170117052613,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.290,04.083,,1,1*5B"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375156,170117052616,34,170117052614,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.277,04.084,1,1,1,1*F3"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375154,170117052613,04,170117052612,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.287,04.084,,1,0*5B"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java
new file mode 100644
index 000000000..e2be2028e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Stl060ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Stl060ProtocolDecoder decoder = new Stl060ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$1,357804048043099,D001,AP29AW0963,23/02/14,14:06:54,17248488N,078342226E,0.08,193.12,1,1,1,1,1,A"),
+ position("2014-02-23 14:06:54.000", true, 17.41415, 78.57038));
+
+ verifyPosition(decoder, text(
+ "$1,357804048043099,D001,AP29AW0963,12/05/14,07:39:57,1724.8564N,07834.2199E,0.00,302.84,1,1,1,1,1,A"));
+
+ verifyPosition(decoder, text(
+ "$1,357804047969310,D001,AP29AW0963,01/01/13,13:24:47,1723.9582N,07834.0945E,00100,010,0,0,0,0,0,A,"));
+
+ verifyPosition(decoder, text(
+ "$1,357804047969310,D001,AP29AW0963,01/01/13,13:24:47,1723.9582N,07834.0945E,00100,010,0,0,0,0,0,0008478660,1450,40,34,0,0,0,A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
new file mode 100644
index 000000000..355cda783
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -0,0 +1,167 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class SuntechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeTemperature() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ decoder.setHbm(true);
+ decoder.setIncludeAdc(true);
+ decoder.setIncludeTemp(true);
+
+ verifyPosition(decoder, text(
+ "ST300STT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:"));
+
+ verifyPosition(decoder, text(
+ "ST300EVT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;2;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:"));
+
+ verifyPosition(decoder, text(
+ "ST600STT;008349958;35;523;20181112;00:49:30;0bf10d4e;334;20;2f19;22;+20.552718;-100.824478;050.021;095.41;12;1;1303603;14.30;10000000;4;8911;001987;4.1;0;0.00;;;;00000000000000;0;2826C8A70800000C:+26.6;:;:"));
+
+ }
+
+ @Test
+ public void testDecodeHours() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ decoder.setHbm(true);
+
+ verifyPosition(decoder, text(
+ "ST300STT;007238270;40;313;20190220;12:05:04;c99e48;+04.644623;-074.076922;010.390;202.77;20;1;997100;14.10;100000;2;8384;003634;4.1;1"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;109002029;08;1080;20190220;13:00:55;85405;+04.645710;-074.078525;007.760;005.19;10;1;6520802;13.86;100100;4;1716;0000039863;4.1;1;0.00;0000;0000;0;0"));
+
+ verifyAttribute(decoder, text(
+ "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"),
+ Position.KEY_HOURS, 3188 * 60000L);
+
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "SA200STT;608945;129;20190215;15:04:53;3dce071558;+22.006721;-098.771016;001.198;000.00;11;1;2632589;12.21;010000;1;3211"));
+
+ verifyPosition(decoder, text(
+ "ST410STT;007272376;408;01;10217;732;103;-87;51511;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;3.8;1;2503;6;20181031;20:12:58;+04.741277;-074.048238;052.375;189.87;20;1"));
+
+ verifyPosition(decoder, text(
+ "ST410STT;007272376;408;01;21651;732;123;-65;1824;1;21654;732;123;1824;0;0;22542;732;123;1824;0;0;21656;732;123;1824;0;0;21655;732;123;1824;0;0;22541;732;123;1824;0;0;0;0;0;0;0;0;3.7;1;0156;1;20180816;05:18:52;+04.722322;-074.052776;000.074;000.00;10;1"));
+
+ verifyPosition(decoder, text(
+ "ST600STT;008084783;20;419;20180308;18:00:36;0032cc3e;736;3;445c;41;-16.530023;-068.084267;018.640;267.99;10;1;11655;13.33;100000;2;0336;000061;4.5;0;0.00"));
+
+ verifyPosition(decoder, text(
+ "ST600STT;107850496;20;419;20180227;14:30:45;00462b08;736;3;4524;50;-16.479091;-068.119119;000.346;000.00;4;1;0;13.89;000000;1;0223;000003;0.0;0;0.00"));
+
+ verifyPosition(decoder, text(
+ "ST600STT;100850000;01;010;20081017;07:41:56;0000004f;450;20;0023;24;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;00110000;1;0072;0;4.5;1;12.35"));
+
+ verifyPosition(decoder, text(
+ "STT;100850000;3FFFFF;26;010;1;20161117;08:37:39;0000004F;450;0;0014;20;+37.479323;+126.887827;62.03;65.43;10;1;00000101;00001000;1;2;0492"));
+
+ verifyPosition(decoder, text(
+ "STT;6009999006;3FFFFF;26;398;0;20170827;20:04:37;087d4760;310;410;0ba0;23;+40.123420;-074.995971;000.031;000.00;8;1;00000001;00000000;1;1;0006"));
+
+ verifyPosition(decoder, text(
+ "ST500STT;205450135;07;843;20170816;23:24:45;+19.338432;-099.179817;000.283;000.00;6;1;141121;12.89;0;0;1;4659;002.795;0;001.891;611;4.0"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;205170303;12;561;20170816;09:10:34;173f53;+19.082370;-098.214287;006.776;000.00;0;0;52982186;12.75;100000;2;6328;155747;4.2;1;0.00;0;0.00;0.00;00000000000000;0;28F2B7600600005D:+5.2;:;:"));
+
+ verifyPosition(decoder, text(
+ "ST910;Location;205576803;500;20170319;12:18:17;-22.846014;-046.322176;000.000;000.00;0;3.8;0;1;9159"));
+
+ verifyPosition(decoder, text(
+ "ST910;Emergency;205576803;500;20170319;12:15:22;-22.846014;-046.322176;000.000;000.00;0;2"));
+
+ verifyPosition(decoder, text(
+ "ST910;Location;205576803;500;20170312;12:56:52;-22.846014;-046.322176;000.000;000.00;0;3.8;0;0;0019"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;1;0072;0;4.5;1;1750;012497F1160000;1;004f001454;450;00;-320;20;255;1"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;205589913;05;527;20170304;02:21:33;be139;-25.398868;-049.325636;000.476;000.00;6;1;427;12.57;100000010;1;0172;017.159;0;002.327;12;4.0"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;638947;803;20170117;07:40:44;5d309;-01.287213;-047.917462;000.035;000.00;10;1;2036194;12.57;000000;1;0376;010360;4.2;1"));
+
+ verifyPosition(decoder, text(
+ "ST300ALT;205174410;14;712;20110101;00:00:07;00000;+20.593923;-100.336716;000.000;000.00;0;0;0;16.57;000000;81;000000;4.0;0;0.00;0000;0000;0;0"));
+
+ verifyNull(decoder, text(
+ "SA200ALV;317652"));
+
+ verifyPosition(decoder, text(
+ "ST910;Alert;123456;410;20141018;18:30:12;+37.478774;+126.889690;000.000;000.00;0;4.0;1;6002"),
+ position("2014-10-18 18:30:12.000", false, 37.47877, 126.88969));
+
+ verifyPosition(decoder, text(
+ "ST910;Alert;123456;410;20141018;18:30:12;+37.478774;+126.889690;000.000;000.00;0;4.0;1;6002;02;0;0310000100;450;01;-282;70;255;3;0"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;317652;042;20120718;15:37:12;16d41;-15.618755;-056.083241;000.024;000.00;8;1;41548;12.17;100000;2;1979"),
+ position("2012-07-18 15:37:12.000", true, -15.61876, -56.08324));
+
+ verifyPosition(decoder, text(
+ "SA200STT;317652;042;20120721;19:04:30;16d41;-15.618743;-056.083221;000.001;000.00;12;1;41557;12.21;000000;1;3125"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;317652;042;20120722;00:24:23;4f310;-15.618767;-056.083214;000.011;000.00;11;1;41557;12.21;000000;1;3205"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;315198;042;20120808;20:37:34;3fac25;-15.618731;-056.083216;000.007;000.00;12;1;48;0.00;000000;1;0127"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;315198;042;20120809;13:43:34;4f310;-15.618709;-056.083223;000.025;000.00;8;1;49;12.10;100000;2;0231"));
+
+ verifyPosition(decoder, text(
+ "SA200EMG;317652;042;20120718;15:35:41;16d41;-15.618740;-056.083252;000.034;000.00;8;1;41548;12.17;110000;1"));
+
+ verifyPosition(decoder, text(
+ "SA200ALT;317652;042;20120829;14:25:58;16d41;-15.618770;-056.083242;000.029;000.00;0;0;2404240;0.00;000000;10"));
+
+ verifyPosition(decoder, text(
+ "SA200STT;430070;133;20130615;22:22:32;151347;+02.860514;-060.653351;000.003;000.00;12;1;0;12.39;000000;1;0208"));
+
+ verifyPosition(decoder, text(
+ "ST910;Location;344506;017;20130727;14:10:00;-25.398714;-049.296818;000.187;000.00;1;4.32;1;1;0001"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;205027329;03;374;20150108;17:54:42;177b38;-23.566052;-046.477588;000.000;000.00;0;0;0;12.11;000000;1;0312"));
+
+ verifyPosition(decoder, text(
+ "ST910;Emergency;205283272;500;20150716;19:12:01;-23.659019;-046.695403;000.602;000.00;0;4.2;1;1;02;10820;2fdb090736;724;05;0;2311;255;0;100"));
+
+ decoder.setProtocolType(1);
+
+ verifyPosition(decoder, text(
+ "ST910;Location;907510186;552;20180504;23:15:45;3af54e5331;+19.301833;-099.190657;000.246;000.00;1;28462;80;1;0;0423;02;334;05;-215;20051;1;4;100"));
+
+ verifyPosition(decoder, text(
+ "ST910;Alert;485195;20170409;22:37:41;3be0133057;+24.882410;-107.509152;000.070;000.00;1;286734;72;02;295;05;-415;4912;255;10;10"));
+
+ verifyPosition(decoder, text(
+ "ST910;Location;485195;528;20170410;01:18:57;f1dd134840;+24.787139;-107.434679;000.020;000.00;1;286734;100;1;0;0188;02;295;05;-339;4936;255;4;74"));
+
+ verifyPosition(decoder, text(
+ "ST910;Location;560266;500;20161207;21:33:11;af910be101;-25.504234;-049.278003;000.080;000.00;1;10054889;70;1;1;1311;02;724;06;-317;3041;2;10;92"));
+
+ verifyPosition(decoder, text(
+ "ST910;Emergency;238569;528;20170403;00:02:09;7574160020;+19.661292;-099.144473;000.176;000.00;1;228638;1"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java
new file mode 100755
index 000000000..2a7ab2dee
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SupermateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SupermateProtocolDecoder decoder = new SupermateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:1:*,00000000,XT,A,10031b,140b28,80ad4c72,81ba2d2c,06ab,238c,020204010000,12,0,0000,0003e6"));
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:2:*,00000000,UP,A,10031b,140a1c,80ad4bf6,81ba2dc3,0000,0000,020204000000,14,0,0000,0003e6"));
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:1:*,00000000,BJ,A,10031b,140c2f,80ad5012,81ba1f27,0f4c,2e18,020204014000,14,0,0000,0003ed"));
+
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java
new file mode 100644
index 000000000..f7b15ca2e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SviasProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SviasProtocolDecoder decoder = new SviasProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "[7061,3041,57,20324277,710,40618,141342,-93155840,-371754060,0,20469,0,16,1,0,0,11323,100,9,,32,4695"));
+
+ verifyPosition(decoder, text(
+ "[7041,3041,8629,20856286,710,60618,201027,-92268040,-371346250,7994,31844,38271,16,1,0,0,13416,100,0,0,5089"));
+
+ verifyPosition(decoder, text(
+ "[7051,3041,15270,30179873,710,70618,40335,-94679080,-360604930,0,35454,23148,0,1,0,0,12542,100,13,32,4971"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
new file mode 100644
index 000000000..f21acdee7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
@@ -0,0 +1,126 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T55ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T55ProtocolDecoder decoder = new T55ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$DEVID,0x0103846677F21422*41"));
+
+ verifyPosition(decoder, text(
+ "660420156A0066AA$GPRMC,122806.0,A,0119.212178,N,10355.000942,E,0.0,,230119,0.0,E,A*27"));
+
+ verifyNull(decoder, text(
+ "$IMEI=355797031609284"));
+
+ verifyNull(decoder, text(
+ "086415031C20"));
+
+ verifyNull(decoder, text(
+ "358244017671308"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,082350.000,5355.0314,N,01044.1271,E,1,10,0.7,-46.0,M,0.0,M,0.0,0000"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,082350.000,A,5355.0314,N,01044.1271,E,26.20,184.27,080518,,"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,192350.000,V,0000.0000,N,00000.0000,E,,,110318,,*12"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,073446.000,A,1255.5125,N,07738.2948,E,0.00,0.53,080316,D*71,11,865733027593268,1,090,086,123,456,789,987,12345"));
+
+ verifyNotNull(decoder, text(
+ "$GPRMC,161223.000,A,2517.0545,S,05739.1788,W,0.0,0.0,011196,,,A*61"));
+
+ verifyPosition(decoder, text(
+ "4711/022789000688081/$GPRMC,133343,A,5308.56325,N,1029.12850,E,0.000000,0.000000,290316,,*2A"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,073446.000,A,1255.5125,N,07738.2948,E,0.00,0.53,080316,D*71,11,865733027593268,1,090,086"));
+
+ verifyNull(decoder, text(
+ "$GPFID,ID123456ABC"));
+
+ verifyNull(decoder, text(
+ "$PGID,359853000144328*0F"));
+
+ verifyNull(decoder, text(
+ "$PCPTI,CradlePoint Test,184453,184453.0,6F*57"));
+
+ verifyNull(decoder, text(
+ "IMEI 351467108700000"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,012006,A,4828.10,N,1353.52,E,0.00,0.00,180915,020.3,E*42"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,094907.000,A,6000.5332,N,03020.5192,E,1.17,60.26,091111,,*33"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,115528.000,A,6000.5432,N,03020.4948,E,,,091111,,*06"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,064411.000,A,3717.240078,N,00603.046984,W,0.000,1,010313,,,A*6C"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000.0,4337.200755,N,11611.955704,W,1,05,3.5,825.5,M,-11.0,M,,*6F"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"));
+
+ verifyPosition(decoder, text(
+ "$GPRMA,V,0000.00,S,00000.00,E,,,00.0,000.,11.,E*7"));
+
+ verifyPosition(decoder, text(
+ "$TRCCR,20140101001122.333,V,60.0,-100.0,1.1,2.2,3.3,4.4,*00"));
+
+ verifyPosition(decoder, text(
+ "$TRCCR,20140111000000.000,A,60.000000,60.000000,0.00,0.00,0.00,50,*3a"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,125735.000,A,6010.34349,N,02445.72838,E,1.0,101.7,050509,6.9,W,A*1F"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000.000,6010.34349,N,02445.72838,E,1,05,1.7,0.9,M,35.1,M,,*59"));
+
+ verifyPosition(decoder, text(
+ "123456789$GPGGA,000000.000,4610.1676,N,00606.4586,E,0,00,4.3,0.0,M,50.7,M,,0000*59"));
+
+ verifyPosition(decoder, text(
+ "123456789$GPRMC,155708.252,V,4610.1676,N,00606.4586,E,000.0,000.0,060214,,,N*76"));
+
+ verifyPosition(decoder, text(
+ "990000561287964,$GPRMC,213516.0,A,4337.216791,N,11611.995877,W,0.0,335.4,181214,,,A * 72"));
+
+ verifyPosition(decoder, text(
+ "355096030432529$GPGGA,000000.00,3136.599,S,5213.981,W,1,7,2.13,250.00,M,-16.384,M,3550960304325290.0,1"));
+
+ verifyPosition(decoder, text(
+ "355096030432529$GPGGA,000000.00,3136.628,S,5213.990,W,1,7,2.13,250.00,M,-16.384,M,0.0,1"));
+
+ }
+
+ @Test
+ public void testMaxonDecode() throws Exception {
+
+ // Maxon devices can send NMEA before identification
+
+ T55ProtocolDecoder decoder = new T55ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$GPRMC,012006,A,4828.10,N,1353.52,E,0.00,0.00,180915,020.3,E*42"));
+
+ verifyPosition(decoder, text(
+ "$GPFID,ID123456ABC"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java
new file mode 100644
index 000000000..f74d3c350
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T57FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T57FrameDecoder decoder = new T57FrameDecoder();
+
+ verifyFrame(
+ binary("2a5435372346312354353731313137303031233330313131372330303038343323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e3223"),
+ decoder.decode(null, null, binary("2a5435372346312354353731313137303031233330313131372330303038343323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303038353323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039303423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039313423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039323423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303038333323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e3223")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java
new file mode 100644
index 000000000..c457b4602
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T57ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T57ProtocolDecoder decoder = new T57ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*T57#F1#T571117001#301117#000843#2234.1303#N#08826.1714#E#+0.242,+0.109,-0.789#0.000#6.20000#A2#4.2#"));
+
+ verifyPosition(decoder, text(
+ "*T57#F1#0123456789#041117#152900#1258.9653#N#07738.4169#E#00000000000000000000#0.000#926.300#A2#4.0#"));
+
+ verifyPosition(decoder, text(
+ "*T57#F2#0123456789#041117#152900#1258.9653#N#07738.4169#E#00000000000000000000#0.000#926.300#A2#4.0#"));
+
+ verifyNull(decoder, text(
+ "*T57#F3#0123456789#041117#152900#1258.9653#N#07738.4169#E#I#9674432345#340#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
new file mode 100644
index 000000000..fe77d91b7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T800xProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T800xProtocolDecoder decoder = new T800xProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "252501001504050880061689888888111111250350"));
+
+ verifyPosition(decoder, binary(
+ "2525020044a66d0862522030401350001403841409c40064edc000051100960000071701370000003ea7ee0019032010581300000000aad3e1bda6f24d42000000001281"));
+
+ verifyPosition(decoder, binary(
+ "252502004400010880616898888888000A00FF2001000020409600989910101010055501550000101005050005051010050558866B4276D6E342912AB441111500051010"));
+
+ verifyNull(decoder, binary(
+ "232301001500000880316890202968140197625020"));
+
+ verifyNull(decoder, binary(
+ "232303000f00000880316890202968"));
+
+ verifyAttributes(decoder, binary(
+ "232302004200000880316890202968001e02582d00000000000000050000320000018901920000001dc1e2001601081154255d0202005a0053875a00a57e5a00af80"));
+
+ verifyNull(decoder, binary(
+ "232301001500020357367031063979150208625010"));
+
+ verifyNull(decoder, binary(
+ "232303000f00000357367031063979"));
+
+ verifyPosition(decoder, binary(
+ "232304004200030357367031063979003c03842307d00000c80000050100008000008900890100000017b100151022121648b8ef0c4422969342cec5944100000110"));
+
+ verifyPosition(decoder, binary(
+ "232302004200150357367031063979003c03842307d000004a0000050100004001009500940000000285ab001510281350477f710d4452819342d1ba944101160038"));
+
+ verifyAttributes(decoder, binary(
+ "232302004200000357367031063979003c03842307d000008000000501000000010094009400000002a0b90015102814590694015a00620cf698620cf49e620cf498"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
new file mode 100644
index 000000000..af3700225
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class T800xProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ T800xProtocolEncoder encoder = new T800xProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "RELAY,0000,On#");
+
+ verifyCommand(encoder, command, binary("232381001e000101234567890123450152454c41592c303030302c4f6e23"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java
new file mode 100644
index 000000000..bdbaec8aa
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java
@@ -0,0 +1,86 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TaipProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TaipProtocolDecoder decoder = new TaipProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">RGP211217112154-2748332-058946350000000FF7F2100;ID=AA01;#0002;*2D<"));
+
+ verifyPosition(decoder, text(
+ ">RCV12270218010247-3471349-058400030002057F001200020A1D013010600001509+0000FF+0000FF;#1DE2;ID=7196;*03<"));
+
+ verifyPosition(decoder, text(
+ ">RPV03874+3477708-0923453100029212;ID=0017;*71<"));
+
+ verifyNull(decoder, text(
+ ">RAL03874+00185+00012;ID=0017;*4A<"));
+
+ verifyNull(decoder, text(
+ ">RCP03874+347771-092345312;ID=0017;*65<"));
+
+ verifyNull(decoder, text(
+ ">RLN03874000+347770828-0923453071+000608270000+0000292309000000000000000000000000000000000000000000000012;ID=0017;*49<"));
+
+ verifyPosition(decoder, text(
+ ">RPV46640+4197412-0752857900015802;ID=5102;*71<"));
+
+ verifyNull(decoder, text(
+ ">RCP46640+419741-075285802;ID=5102;*6C<"));
+
+ verifyPosition(decoder, text(
+ ">REV001958003965+0307178+1016144900031532;IO=300;SV=8;BL=4159;CF=8161,C,13;AD=14145;IX=10233040;FF=0,0,0,0;VO=338578;ID=357042063052352<"));
+
+ verifyPosition(decoder, text(
+ ">REV011958000369+0307185+1016144400000032;IO=200;SV=9;BL=4158;CF=0,0,0;AD=12347;IX=10213040;FF=0,0,0,0;VO=338572;ID=357042063052352<"));
+
+ verifyPosition(decoder, text(
+ ">REV421942237017+1170957-0701880200000032;ID=356612022463055<"));
+
+ verifyPosition(decoder, text(
+ ">RGP200317010815-3852.9306-06204.88560000003000101;&01;ID=5555;#7AD7*51<"));
+
+ verifyPosition(decoder, text(
+ ">RCQ09000000000000-3460365-058381460000007F0000000000000115000FFFF1099;#0000;ID=555224;*05<"));
+
+ verifyPosition(decoder, text(
+ ">RBR00130217040848-3462200-05846708000175FF0022900003B3C13010800001118410+24061A;ID=555224;*07<"));
+
+ verifyPosition(decoder, text(
+ ">REV451891352379+0307152+1016143700000012;SV=8;BL=4416;VO=8055;ID=356612026322000<"));
+
+ verifyPosition(decoder, text(
+ ">RGP230615010248-2682523-065236820000003007F4101;ID=0005;#0002;*2A<"),
+ position("2015-06-23 01:02:48.000", true, -26.82523, -65.23682));
+
+ verifyPosition(decoder, text(
+ ">RGP190805211932-3457215-058493640000000FFBF0300;ID=8251;#2122;*54<"));
+
+ verifyPosition(decoder, text(
+ ">RPV00000+3739438-1220384601512612;ID=1234;*7F"));
+
+ verifyPosition(decoder, text(
+ "\r\n>REV691615354941+3570173+1397742703203212;ID=Test"));
+
+ verifyPosition(decoder, text(
+ ">REV481599462982+2578391-0802945201228512;ID=Test"),
+ position("2010-09-02 17:29:42.000", true, 25.78391, -80.29452));
+
+ verifyPosition(decoder, text(
+ ">REV131756153215+3359479-0075299001031332;VO=10568798;IO=310;SV=10;BL=4190;CV09=0;AD=0;AL=+47;ID=356612021059680"));
+
+ verifyPosition(decoder, text(
+ ">RPV02138+4555512-0735478000000032;ID=1005;*76<"));
+
+ verifyPosition(decoder, text(
+ ">RPV19105+4538405-0739518900000012;ID=9999;*7A<\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java
new file mode 100644
index 000000000..0446670d8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TekFrameDecoder decoder = new TekFrameDecoder();
+
+ verifyFrame(
+ binary("020315048715E70861074028023219026200400A0340002C007F0009000000000000000000402842064028420641284206402844064128440640284406402844064028440641284406402844060010010C04052B000253000000000001060A0000000000000228330000FF0000FF360014B394"),
+ decoder.decode(null, null, binary("020315048715E70861074028023219026200400A0340002C007F0009000000000000000000402842064028420641284206402844064128440640284406402844064028440641284406402844060010010C04052B000253000000000001060A0000000000000228330000FF0000FF360014B394")));
+
+ verifyFrame(
+ binary("0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA"),
+ decoder.decode(null, null, binary("0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java
new file mode 100644
index 000000000..f67ae9c3f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TekProtocolDecoder decoder = new TekProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "0501E304E00E76086107502100455111492C33332C3137303935342E302C353235352E393933344E2C30303833322E34333935572C322E312C3133342E382C322C302E30302C302E302C302E302C3234303931352C30362C3C45"));
+
+ verifyAttributes(decoder, binary(
+ "0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA"));
+
+ verifyAttributes(decoder, binary(
+ "0509220886157E0863835020373564087B00018C0000018003160A6E28790A6E28790A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BD35"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java
new file mode 100644
index 000000000..e330600e5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelemaxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelemaxProtocolDecoder decoder = new TelemaxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "%067374070128"));
+
+ verifyPositions(decoder, text(
+ "Y000007C6999999067374074649003C00A7018074666F60D66818051304321900000000C5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java
new file mode 100644
index 000000000..5fcbcbb57
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelicFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicFrameDecoder decoder = new TelicFrameDecoder();
+
+ verifyFrame(
+ binary("303032363230333339337c3232367c31307c303032303034303130"),
+ decoder.decode(null, null, binary("303032363230333339337c3232367c31307c30303230303430313000")));
+
+ verifyFrame(
+ binary("3030333032303333393332352c3139303331373038333035322c302c3138303331373130333132372c3235393932342c3434353133332c332c302c302c392c2c2c39332c31323231303134312c2c303031302c30302c34302c3234302c302c30343036"),
+ decoder.decode(null, null, binary("630000003030333032303333393332352c3139303331373038333035322c302c3138303331373130333132372c3235393932342c3434353133332c332c302c302c392c2c2c39332c31323231303134312c2c303031302c30302c34302c3234302c302c3034303600")));
+
+ verifyFrame(
+ binary("303032363239363231385343434530315f534343457c3232367c31307c30323637"),
+ decoder.decode(null, null, binary("303032363239363231385343434530315f534343457c3232367c31307c3032363700")));
+
+ verifyFrame(
+ binary("30303434323936323138544c4f43303236372c30302c3031313030393030303239363231382c3139303331373038333033362c3235353137382c3434353037322c332c302c38322c2c2c2c3136382c31343734313239362c2c30302c30302c302c323137"),
+ decoder.decode(null, null, binary("6400000030303434323936323138544c4f43303236372c30302c3031313030393030303239363231382c3139303331373038333033362c3235353137382c3434353037322c332c302c38322c2c2c2c3136382c31343734313239362c2c30302c30302c302c32313700")));
+
+ verifyNull(
+ decoder.decode(null, null, binary("00303032363937393238317c3233327c30337c30303230303430313000")));
+
+ verifyFrame(
+ binary("303032363937393238317c3233327c30337c303032303034303130"),
+ decoder.decode(null, null, binary("303032363937393238317c3233327c30337c30303230303430313000")));
+
+ verifyFrame(
+ binary("3030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c30343037"),
+ decoder.decode(null, null, binary("650000003030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c3034303700")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
new file mode 100644
index 000000000..b743cef96
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
@@ -0,0 +1,94 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelicProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicProtocolDecoder decoder = new TelicProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "0026355565071347499|206|01|001002008"));
+
+ verifyPosition(decoder, text(
+ "052028495198,160917073641,0,160917073642,43879,511958,3,24,223,17,,,-3,142379,,0010,00,64,205,0,0499"));
+
+ verifyPosition(decoder, text(
+ "01302849516,160917073503,0,160917073504,43907,512006,3,11,160,14,,,-7,141811,,0010,00,64,206,0,0499"));
+
+ verifyPosition(decoder, text(
+ "002135556507134749999,010817171138,0,010817171138,004560973,50667173,3,0,0,11,1,1,100,958071,20601,000000,00,4142,0000,0000,0208,10395,0"));
+
+ verifyPosition(decoder, text(
+ "442045993198,290317131935,0,290317131935,269158,465748,3,26,183,,,,184,85316567,226,01,00,68,218"));
+
+ verifyPosition(decoder, text(
+ "673091036017,290317131801,0,290317131801,262214,450536,3,40,199,8,,,154,19969553,,0011,00,59,240,0,0406"));
+
+ verifyPosition(decoder, text(
+ "092020621198,280317084155,0,280317084156,259762,444356,3,42,278,9,,,89,56793311,,0110,00,67,0,,0400"));
+
+ verifyPosition(decoder, text(
+ "502091227598,280317084149,0,280317084149,261756,444358,3,33,286,9,,,77,3143031,,0010,00,171,240,0,0406"));
+
+ verifyPosition(decoder, text(
+ "232027997498,230317083900,0,230317083900,260105,444112,3,22,259,,,,111,61110817,226,01,00,255,218,00000000000000"));
+
+ verifyPosition(decoder, text(
+ "072027997498,230317082635,0,230317082635,260332,444265,3,28,165,,,,124,61107582,226,01,00,255,219,00000000000000"));
+
+ verifyNull(decoder, text(
+ "0026203393|226|10|002004010"));
+
+ verifyPosition(decoder, text(
+ "003020339325,190317083052,0,180317103127,259924,445133,3,0,0,9,,,93,12210141,,0010,00,40,240,0,0406"));
+
+ verifyNull(decoder, text(
+ "0026296218SCCE01_SCCE|226|10|0267"));
+
+ verifyNull(decoder, text(
+ "1242022592TTUV0100,0201,351266000022592,170403114305,0115859,480323,3,30,5,9,3,4,650,250000000,26202,1001,0001,211,233,111,0"));
+
+ verifyPosition(decoder, text(
+ "123002259213,170403114305,1234,170403114305,0115859,480323,3,30,5,9,3,4,650,250000000,26202,1001,0001,211,233,111,0,600"));
+
+ verifyNull(decoder, text(
+ "0044296218TLOC0267,00,011009000296218,190317083036,255178,445072,3,0,82,,,,168,14741296,,00,00,0,217"));
+
+ verifyPosition(decoder, text(
+ "003097061325,220616044200,0,220616044200,247169,593911,3,48,248,8,,,50,1024846,,1111,00,48,0,51,0406"));
+
+ verifyPosition(decoder, text(
+ "003097061325,210216112630,0,210216001405,246985,594078,3,0,283,12,,,23,4418669,,0010,00,117,0,0,0108"));
+
+ verifyPosition(decoder, text(
+ "592078222222,010100030200,0,240516133500,222222,222222,3,0,0,5,,,37,324,,1010,00,48,0,0,0406"));
+
+ verifyPosition(decoder, text(
+ "002017297899,220216111100,0,220216111059,014306446,46626713,3,7,137,7,,,448,266643,,0000,00,0,206,0,0407"));
+
+ verifyPosition(decoder, text(
+ "003097061325,210216112630,0,210216001405,246985,594078,3,0,283,12,,,23,4418669,,0010,00,117,0,0,0108"));
+
+ verifyNull(decoder, text(
+ "0026970613|248|01|004006011"));
+
+ verifyPosition(decoder, text(
+ "032097061399,210216112800,0,210216112759,246912,594076,3,47,291,10,,,46,4419290,,0010,00,100,0,0,0108"));
+
+ verifyPosition(decoder, text(
+ "002017297899,190216202500,0,190216202459,014221890,46492170,3,0,0,6,,,1034,43841,,0000,00,0,209,0,0407"));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"),
+ position("2013-06-27 04:16:52.000", true, 47.53410, 16.66530));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
new file mode 100644
index 000000000..f94cd9460
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -0,0 +1,134 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class TeltonikaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, false);
+
+ verifyNull(decoder, binary(
+ "000F313233343536373839303132333435"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004c08010000016818d500580009c28d9f1cb3757a00be00c60f0053000f06f0011503c80001011d00fc0007423799180053cdf80dce426f430f88190bb8560bb802f100005aa110002887e000010000ee8d"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000818e0100000166e368a510000f0d8b5b20961c35008d010308000000000014000900ef0000f00100500100150400c800004501001e00002500002900000a00b5000800b60007004230dc0018000000430fcb0044005f001103de001200e50013001200240000000000000001010000113141314a433534343452373235323336370100005e99"));
+
+ verifyPositions(decoder, binary(
+ "000000000000009D10020000013feb55ff74000f0ea850209a690000AE00B90B00000000070A050001000002000003000004000120000200180000004601290200C700000000004C0000000001003E00000000000000000000015B198C7498000F0DBC502095872F00AE00B90B00000000070A050001000002000003000004000120000200180000004601290200C700000000004C0000000001003E000000000000000002000009A5"));
+
+ verifyPositions(decoder, binary(
+ "000000000000009F100100000164D855401800D5E3B744EC11C762023B011A060000000007200A010000010500010600010D00010E00010F00011600011700011800011F001301010000010700000108000001090000010A0000010B0000010C000001100000011100000112000001130000011400000115000001190000011A0000011B0000011C0000011D0000011E000003010200000000010300000000010400000000000100000D3B"));
+
+ verifyPositions(decoder, binary(
+ "000000000000008c08010000013feb55ff74000f0ea850209a690000940000120000001e09FD01FE210300040016014703f0001504c8000c0900730a00460b00501300464306d7440000b5000bb60007422e9f180000cd0386ce000107c700000000f10000601a46000001344800000bb84900000bb84a00000bb84c00000000024e0000000000000000cf00000000000000000100003fca"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000A708010000016269E7D9A8000A5A0F0A1CBF8F3300880046120000001C0801014F005100550F740073007801790103430000440000426F980B540000000056000045275700000047580000022659000000005D0000000068000003D07100007355870000000288000000008A000045270669584C5241534834336A30304731363538326B3600FFFFFF0000008155412055414430308230303039383236368330303000000000000100008396"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000035080100000161f37c50500020de5ba60ef11450000000000000000006040100b300b400ef000109002000014e0000000000000000010000be52"));
+
+ verifyPositions(decoder, binary(
+ "000000000000008c08010000013feb55ff74000f0ea850209a690000940000120000001e09010002000300040016014703f0001504c8000c0900730a00460b00501300464306d7440000b5000bb60007422e9f180000cd0386ce000107c700000000f10000601a46000001344800000bb84900000bb84a00000bb84c00000000024e0000000000000000cf00000000000000000100003fca"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000401080e0000015d12cc211000fadaf627186742f5000d0048080006000a040100f001500515000342318a430fe344000003c700000000f1000068b61000001b05000000015d12c6683800fadaf72118673f82000000000000000007030100f00050040342318a430fe344000001f1000068b6000000015d12bd407800fadaf72118673f82000000000000000007030100f000500403423179430fe144000001f1000068b6000000015d12b414d000fadaf72118673f82000400900c0000fa120a0100f00050051502080007010552090e6f4bfa000542316a430fe14400000600006202b203c700002328f1000068b61000001b05000000015d12b3074800fadaf2821867436a000400890d00170011090100f00150011502081007010553090e6f4d054231fb430fe14400000603ae6202a003c700002328f1000068b61000001b05000000015d12b2ff7800fadaee89186747c60005009a0d001d0011090100f00150011502081b07010553090e6f4d05423125430fe144000006050862029e03c700002328f1000068b61000001b05000000015d12b2e42000fadae8cf18675e0a000300a60d00210011090101f00150011502082407030554090e6f4d0542310a430fe14400000606cf62029703c700002328f1000068b61000001b05000000015d12b2d48000fadae05818676a16000400930c00240011090100f00150011502082207010554090e6f4e05423738430fe144000006066a62029303c700002328f1000068b61000001b05000000015d12b2a1b800fadac33e18678e48000600940d00150011090101f00150051500081907030553090e6f4e054239cc430fe14400000607c662028603c700002328f1000000001000001b05000000015d12b29dd000fadac19d18678fc8000700820d00110011090100f00150051500081607030553090e6f4e054238c8430fe14400000606d962028503c700002328f1000000001000001b05000000015d12b299e800fadabfa9186790e3000700670d00110011090101f00150051500081407030553090e6f4e054231e5430fe144000006060a62028403c700002328f1000000001000001b05000000015d12b2960000fadabd4018679104000600510d00120011090101f00150051500081207030553090e6f4e054231ce430fe144000006057062028303c700002328f1000000001000001b05000000015d12b27aa800fadaa96518678b7c000600470d00120011090101f00150051500081807030551090e6f4e05423a70430fe144000006074462027c03c700002328f1000000001000001b05000000015d12b276c000fadaa73f18678ae60006003b0d000e0011090101f00150051500081607030551090e6f4e05423a5a430fe14400000606b762027b03c700002328f1000000001000001b05000e000007a4"));
+
+ verifyPositions(decoder, binary(
+ "00000000000002cb08080000015a71ccbec00002fc9bfc1e53a1e00016004cf80005001914150216056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d20003480100c6000ac500ce02c80000654ec700004ee8000000015a725aaac80002fc933c1e539d4000150049f80000001914150116056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f4c6000ac500ce02c80000654ec700004ee8000000015a75a42c900002fc97d01e539640001d0008020000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c6000ac500ce02c80006ba5ac700004ee8000000015a75a440180002fc931c1e539b60001d00b9020001001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800fac6000ac500ce02c80006ba5ac700004ee8000000015a75a453a00002fc93601e539cc0001d015d0c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f9c6000ac500ce02c80006ba5ac700004ee8000000015a75a467280002fc93801e539cc0001d013c0c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f9c6000ac500ce02c80006ba5ac700004ee8000000015a75a47ab00002fc92cc1e539c80001d00b00c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c60004c5000a02c800003085c70006ba5a000000015a75a48e380002fc92ec1e539c40001d00410c0000001914150116056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c6000ac500ce02c80000c83dc700004ee800080000e0b2"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000000080100000113fc208dff00209cca800f14f650006f00d60400040004030101150316030001460000015d000100000000")); // invalid length and checksum
+
+ verifyPositions(decoder, binary(
+ "000000000000009f080400000159738f76b8012e13b796110ab27600d700000b00004e01000000014e000000000000000000000159738f6ee8012e13b796110ab27600d700000a00004e01000000014e01000b00791c179300000159738f6b00012e13b796110ab27600d700000a00004e01000000014e000000000000000000000159738f5f48012e13b796110ab27600d700000b00004e01000000014e01000b00791c17930400009671"));
+
+ verifyPositions(decoder, false, binary(
+ "00000000000000710c0106000000694154244d5347534e443d342c225354474234302c50522c3335363630313036303236353035302c313630343232313531372c313630343232313531382c432c2b3032332e332c302c2b3032332e312c302c4445414354492c302c4445414354492c302c312c30220d0a010000d8db"));
+
+ verifyPositions(decoder, false, binary(
+ "0000000000000055070450aa14320201f00150aa17f3031f42332a4c4193d68c008d00020901f00150aa1b6a031f423383f54193624f009d00000a01f00150aa1c230fc01a0000552b040164f400dd00f0010143100c0105000000050400006846"));
+
+ verifyPositions(decoder, binary(
+ "000000000000003508010000014f8e016420002141bbaf0f4e96a7fffa0000120000000602010047030242669c92000002c7000000009100000000000100002df3"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000A7080400000113fc208dff000f14f650209cca80006f00d60400040004030101150316030001460000015d0000000113fc17610b000f14ffe0209cc580006e00c00500010004030101150316010001460000015e0000000113fc284945000f150f00209cd200009501080400000004030101150016030001460000015d0000000113fc267c5b000f150a50209cccc0009300680400000004030101150016030001460000015b00040000"));
+
+ verifyPositions(decoder, binary(
+ "000000000000014708060000013e5a60a4cb003fa7b780fc424518004200000a000000090501010200b300b400f000034268a746011818000001c700000000000000013e5dc8ba28003fa7c080fc4246040001000005000000090501010200b300b400f001034268b44600ef18000001c700000000000000013e5dc90455003fa7b640fc424388003a0000070000f0090501010200b300b400f000034268dc4600f718000001c70000001d000000013e5dc9d368003fa7b800fc4244300049000004000000090501010200b300b400f001034267de46010718000001c700000000000000013e5dca311d003fa7b680fc4243cc00420000070000f0090501010200b300b400f0000342685346010b18000001c700000000000000013e5dcfafe9003fa7b600fc4242f0003d000008000000090501010200b300b400f0000342685246011918000001c700000000000600000275"));
+
+ verifyPositions(decoder, binary(
+ "000000000000002c08010000013eff8d6f9800173295002111f400008100ae0b0000000401010003090016432980422f7200000100007a5d"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000c7070441bf9db00fff425adbd741ca6e1e009e1205070001030b160000601a02015e02000314006615000a160067010500000ce441bf9d920fff425adbb141ca6fc900a2b218070001030b160000601a02015e02000314006615000a160067010500000cc641bf9d740fff425adbee41ca739200b6c91e070001030b1f0000601a02015f02000314006615000a160066010500000ca841bf9cfc0fff425adba041ca70c100b93813070001030b1f0000601a02015f02000314002315000a160025010500000c3004000000"));
+
+ verifyPositions(decoder, binary(
+ "000000000000003107024c61410b013f4231c2c141d0beb9003d000005006483ff4c6140eb013f4231c2c141d0beb9003d000005006483ff02000041df"));
+
+ verifyPositions(decoder, binary(
+ "000000000000002b080100000140d4e3ec6e000cc661d01674a5e0fffc00000900000004020100f0000242322318000000000100007a04"));
+
+ verifyPositions(decoder, false, binary(
+ "000000000000002d0c01060000002523464d323d3236323033323736313732313339362c32363230332c30372e30322e30350d0a0100009a2e"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000a608010000013f14a1d1ce000f0eb790209a778000ab010c0500000000000000000100003390"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004508010000015eb70a86d00024089d4d21dee3860137005f12005f000e06ef01f00150011503c800450108b5000bb6000642382718005fcd057ace19d3430f57440000000001000002bf"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004a08010000015ebc1da508002411926621f15246010b00b913005e000f06ef01f00150011505c800450108b5000bb6000642381b18005ecd0318ce19cd430f5844000001f1000061a900010000c8e1"));
+
+ decoder.setExtended(true);
+
+ verifyPositions(decoder, false, binary(
+ "0000000000000158080b0000015d4b4dc07a00d5dbd13eec04324e020e0000120000000000000000000000015d4b4cd5e800d5dbd13eec04324e020e0000120000000000000000000000015d4b4beb8800d5dbd13eec04324e020e0000130000000000000000000000015d4b4b012800d5dbd13eec04324e020e0000120000000000000000000000015d4b4a16c800d5dbd13eec04324e020f0000110000000000000000000000015d4b492c6800d5dbd13eec04324e020f0000110000000000000000000000015d4b48420800d5dbd13eec04324e020f0000120000000000000000000000015d4b4757a800d5dbd13eec04324e020f00000f0000000000000000000000015d4b466d4800d5dbd13eec04324e020f0000100000000000000000000000015d4b4582e800d5dbd13eec04324e020f0000110000000000000000000000015d4b44988800d5dbd13eec04324e020f0000110000000000000000000b0000ec10"));
+
+ verifyPositions(decoder, false, binary(
+ "00000000000003b5080b0000015ab5642a8800d5db1769ec01d70a020a00e3040004000e0501010200030004006000060900100a00010b0000130000422f1318000302c700000000f7000000000001cb000000000000000000000000000000000000015ab5642e7000d5db178aec01d6d6020a0070040003000e0501010200030004006000060900100a00000b0000130000422f1318000302c700000000f7000000000001cb000000000000000000000000000000000000015ab567050000d5db1805ec01da3c02060101040006000e05010102000300040060000609000f0a00010b0000130000422f0c18000302c700000046f7000000000001cb000000000000000000000000000000000000015ab56708e800d5db1723ec01d9ec020600e5040006000e05010102000300040060000609000f0a00010b0000130000422f1018000502c700000003f7000000000001cb000000000000000000000000000000000000015ab5685cc000d5db20f7ec01d8fa02080033050007000e05010102000300040060000609000f0a00010b0000130000422f0a18000502c700000030f7000000000001cb000000000000000000000000000000000000015ab5693b6800d5db2367ec01d9430211011b040006000e05010102000300040060000609000f0a00010b0000130000422f0c18000302c700000027f7000000000001cb000000000000000000000000000000000000015ab569433800d5db1fb2ec01d9310211008b040006000e0501010200030004006000060900110a00000b0000130000422f1318000402c700000009f7000000000001cb000000000000000000000000000000000000015ab56a0e5800d5db22a2ec01da5502100041050007000e05010102000300040060000609000f0a00000b0000130000422f1118000602c70000000ef7000000000001cb000000000000000000000000000000000000015ab56a700000d5db2afcec01ddb2020a0012050008000e05010102000300040060000609000e0a00000b0000130000422f0918000502c700000026f7000000000001cb000000000000000000000000000000000000015ab56a73e800d5db2ad8ec01de65020a014e050008000e05010102000300040060000609000f0a00010b0000130000422f0818000702c700000002f7000000000001cb000000000000000000000000000000000000015ab56a7bb800d5db2971ec01e00e020a013f040008000e0501010200030004006000060900100a00020b0000130000422f0b18000802c700000004f7000000000001cb000000000000000000000000000000000b00007c5f"));
+
+ }
+
+
+ @Test
+ public void testDecodePhoto() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, false);
+
+ verifyNull(decoder, binary(
+ "000F313233343536373839303132333435"));
+
+ verifyNull(decoder, binary(
+ "00000000000000090c010D00000001000100000CD8"));
+
+ verifyNull(decoder, binary(
+ "000000000000000D0c010D0000000501598493ED01000018B2"));
+
+ verifyNull(decoder, binary(
+ "00000000000008130c010d0000080b02598493ED000000000800ffd8ffe000104a46494600010201006000600000ffdb0084000202020202020202020202020202020403020202020504040304060506060605060606070908060709070606080b08090a0a0a0a0a06080b0c0b0a0c090a0a0a01020202020202050303050a0706070a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0affc401a20000010501010101010100000000000000000102030405060708090a0b0100030101010101010101010000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffc000110801e002d003012200021101031101ffdd00040054ffda000c03010002110311003f00fc918d555b0c0007a8cd4e3054a8c807a64540a32d900f07e626ac2fce0f4014718ae652ba68e87ef222238da4f20f415226d00823a838045210149cf24fbd30305c31391cf18a52574269ad2c49b9be518c60d4dbb0194003232323b542092509e840c0a9304120e4827068924e5a8db761a0971b402011c1229ea02608f5ebeb4a09fba00001eb520552a001c83d09a1493d1149a48455009dc4020f029f210a98079a52a32a091b8f24d23042c41c93fc38a517a89b6f61b19dc371c923a1a9631f2b8639c1e326a280124ae0804fe55232153819c93924d17698452488c079380792dc1c5491a80e10825327f3a7b60052a36e4f069e9d77b1c053d4d24d3634aeee3769f31890060f03da9f364c6029cee5e09a7125db790013d69a49200000eb4ecbd0a5749d86a10ab1a827716c64d3d57639181ce4e29c880216246e43c12296360e0c8c4865e9c76a7169bb762534da18b1b3fdfc003a0a951004236e5b77ca49a8959c48a013f33723daa74604c84f0a0e011d8d36d27a83936c67987718c819cf20fa54ae018f6648048c0c74a810b1901009603a9ed53005958900943920f7a9d131deeb41854064519c01c934e000914124865e49a7901ca903af63da918a96418c91c311495a49836e438f240ebb4fcbf4a7a16219828000232691f3953c023a01446e49008da83ef0f5a6acb71249a41b4642331e0f5a7b8023193f311c81e94e5da496750016ea476a64b2050495279f9463b51aa2ac86a200c1412081c9a9810d2384206178c1a8d010a1ca92d8fc853e37551900167079a4db6ef70bc799362f963682c3073dfbd2e49508848e7e6a79264894e3033900543138569093b8b82001d8d5dd2d985d262801880bcaaf1c1ef52b92c91c600c27041f4a6292acaa9c0c724fad293f3161938eb93509b6f713695d844586e00e060e38a72260b9e8aab9247a546a30c40242b8e07a54b9fbca092ae3814ee9ec4ab45a60c140c81b8b918149b0b2ba3120eee707a5354b052554e54f04f6a7a9561b413bcb0249345d25a17a5ee3c81e5a804860723029502bec2fdb8201ea69db39c90403f74fb5359580057ef6780294527aafe98ad1487ae017278dab900d47390c8072188f978ef52e0b48c41fbc3048a6381bd41390a791449a6c4af263f0ab124472485cb0f53532c8137a6727193c77a81582caace7200e38a7be7cc9b6a819192cc78a77567b84795a240ac0463000232e734b127254fde4078a7021d22232d82371f4a69251caa900b3e4367b526928bbf512516eec91103204271d720fd6a605427cbc04e066aaa615d482483d81ef53306024503049c939e94dd96a55d5c6c6518b316e431c71d6a78c99033138c1f947ad5451f3aae7193f301eb5ad02a44c430071cb023a524d3d413e87b0e829245f0f3c73242e229e4f095e209ca062a1e278fa3023a39edeb5f13ce144ae01dc3770718afb5bc3e88fe0bf1c1b8252de4f09de9322a92630b113b801d4fe07815f155cb0695982ed19e00aceed55934fa47ff6ef989abc48c0cf23800f5cd3d4f24e38238a8949c00304e79a901e7a678a1b5ad898b492255c024824e48e2a55272307041a8548c01d077352a9c923ae2a559bd4a6d264c0e060f04f7a9800001ebd6a01b7007383d2a75008039a4d3be80f995c82f0916f290707674f5aa5e1c19b9949e3e4e3f3ab97b9fb34a31c05e9557c3a019a50724003b576615e8ccab5940ee62c123af5e302aeaf0463a8355225276e3a0ee2ad82474e83b115d8d7bda9cea4921aec76b678c83d6a9d9f319c1c658f22adc9cab6464e0e00aab60018723b93835c59838ac3dcebc124f106cdaa8dcbcf39afa13e0fc65b5e848c83b3e519ef91fe35f3f5a801973d8f15f48fc148fcdf10da2f003ca8a491dcbafaf7e2be2f3549e1a4bb9f474928c6ecfdd5d1a309a7d928fe1b68c0c7fbb5d1c28db01c77ac7d3d156de054185f2d76fd315d25a85d801e7938cd7d2609bf6d7f53e66ac7f77a010000b7cd"));
+
+ }
+
+ @Ignore
+ @Test
+ public void testDecodeConnectionless() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, true);
+
+ verifyPositions(decoder, false, binary(
+ "0049cafe0122000f33353734353430373237313339373508010000015d3766f6a800003eef961ec6215e0063006d09003100070401000200f001c8000242381c18003201c7000000e10001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java
new file mode 100644
index 000000000..83ea961b4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class TeltonikaProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ TeltonikaProtocolEncoder encoder = new TeltonikaProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "setdigout 11");
+
+ verifyCommand(encoder, command, binary("00000000000000160C01050000000E7365746469676F75742031310D0A010000E258"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java
new file mode 100644
index 000000000..a30d17502
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ThinkRaceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ThinkRaceProtocolDecoder decoder = new ThinkRaceProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "48415349483031343730303134382C8000100134363030303134363139363239343806FF"));
+
+ verifyPosition(decoder, binary(
+ "48415349483031343730303134382C90001755701674D70155466406CBB813003D24A410F5000000770B4C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java
new file mode 100644
index 000000000..f1ba7c593
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tk102ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk102ProtocolDecoder decoder = new Tk102ProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "[\u00800000000000\u000821315452]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00f00000000000\u000821315452]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00900100100001\u0036(ONE025857A2232.0729N11356.0030E000.02109110100000000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00900100100001\u0036(ITV025857A2232.0729N11356.0030E000.02109110100000000)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00210000000081\u0072(353327023367238,TK102-W998_01_V1.1.001_130219,255,001,255,001,0,100,100,0,internet,0000,0000,0,0,255,0,4,1,11,00)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u004c0000001323\u004e(GSM,0,0,07410001,20120101162600,404,010,9261,130,0,2353,130,35,9263,130,33,1)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00250000000082\u001d(100100000000000600-30-65535)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00230000000004\u0018(062100000000000600-0-0)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003d0000000083\u0036(ITV013939A4913.8317N02824.9241E000.90018031310010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003d0000000036\u0036(ITV012209A4913.8281N02824.9258E000.32018031310010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003b0000000010\u0036(ONE200834A5952.8114N01046.0832E003.93212071305010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00930000000000\u0046(ITV153047A1534.0805N03233.0888E000.00029041500000400&Wsz-wl001&B0000)]"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java
new file mode 100644
index 000000000..1f42e588f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java
@@ -0,0 +1,44 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tk103FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk103FrameDecoder decoder = new Tk103FrameDecoder();
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429"),
+ decoder.decode(null, null, binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ ByteBuf buf = binary("283836343535353535353535353535352C445735422C3231302C362C353939352C34373730312C352C33303A45453A43433A45373A38363A44442A2D35392A31312C34433A36303A43433A45413A42423A45452A2D36382A312C34323A41413A44453A45413A42423A30302A2D36392A312C33323A43443A42423A43333A34463A43432A2D38362A332C31303A30303A34333A42413A32323A31352A2D38382A312C3135313131372C31363337323229283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529");
+
+ verifyFrame(
+ binary("283836343535353535353535353535352C445735422C3231302C362C353939352C34373730312C352C33303A45453A43433A45373A38363A44442A2D35392A31312C34433A36303A43433A45413A42423A45452A2D36382A312C34323A41413A44453A45413A42423A30302A2D36392A312C33323A43443A42423A43333A34463A43432A2D38362A332C31303A30303A34333A42413A32323A31352A2D38382A312C3135313131372C31363337323229"),
+ decoder.decode(null, null, buf));
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529"),
+ decoder.decode(null, null, buf));
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429"),
+ decoder.decode(null, null, binary("676172626167652540232A5E242D2B3C3E3F2429292924242D2D283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ verifyNull(decoder.decode(null, null, binary("67")));
+
+ verifyNull(decoder.decode(null, null, binary("676172626167652540232a5e242d2b3c3e3f24")));
+
+ verifyFrame(
+ binary("2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A3137292429"),
+ decoder.decode(null, null, binary("610D0A676172626167652540232A5E242D2B3C3E3F2429292924242D2D2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A3137292429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ verifyNull(decoder.decode(null, null, binary("610D0A676172626167652540232A5E242D2B3C3E3F2429292924242D2D2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A31372929283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java
new file mode 100644
index 000000000..db636893b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java
@@ -0,0 +1,202 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Tk103ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk103ProtocolDecoder decoder = new Tk103ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(094625928000BR00190213A1156.0431S07705.6145W000.000023521.40000000007L00000314T113)"));
+
+ verifyPosition(decoder, text(
+ "(019358704260BR00180725A2300.0957N07235.2748E032.412092187.58001100166L000D9779)"));
+
+ verifyPosition(decoder, text(
+ "(358511020000026,DW5B,310,6,29876,30393,0,041217,102211)"));
+
+ verifyPosition(decoder, text(
+ "(007611121184BR00170816A2401.5217N07447.0788E000.0221352232.340000004FL0030F14F)"));
+
+ verifyNull(decoder, text(
+ "(027044702512BP00027044702512HSO01A4)"));
+
+ verifyPosition(decoder, text(
+ "(864768011069660,ZC11,250517,V,0000.0000N,00000.0000E,000.0,114725,000.0,0.00,11)"));
+
+ verifyPosition(decoder, text(
+ "(864768011069660,ZC17,250517,A,3211.7118N,03452.8086E,0.68,115525,208.19,64.50,9)"));
+
+ verifyNull(decoder, text(
+ "(357593060760397BP02,G,2,170304A6015.7466N01101.8460E001.609445591.048,7)"));
+
+ verifyPosition(decoder, text(
+ "(325031693849BR00170228A5750.8012N02700.7476E000.2154529000.0000000200L00000000,170228,194530)"));
+
+ verifyAttribute(decoder, text(
+ "(087073803649BR00170221A6142.0334N02712.2197E000.3203149000.00,00000000L00000000)"),
+ Position.KEY_FUEL_LEVEL, 0);
+
+ verifyPosition(decoder, text(
+ "(864768010869060,DW30,050117,A,5135.82713N,00001.17918E,0.089,154745,000.0,43.40,12)"));
+
+ verifyNotNull(decoder, text(
+ "(087073104337BZ00,740,000,3bf7,0425,3bf7,0bf5,3bf7,09e7,3bf7,cbad,3bf7,0dcf,3bf7,c7b2,01000000)"));
+
+ verifyNull(decoder, text(
+ "(087073005534BP00HSO)"));
+
+ verifyNull(decoder, text(
+ "(027028258309BQ86,0,05550c21b10d1d0f431008bd114c0ea5078400010007a100423932,161117005322,01000001)"));
+
+ verifyNull(decoder, text(
+ "(027028258309BQ86,0,05470c0eb20d040f4410022911360e92077e00010007a1004237c7,161117005232,01000001)"));
+
+ verifyPosition(decoder, text(
+ "(01602009983BR00160830V1855.7022S4817.8731W000.0002729000.0010000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(088046338039BR00160727A3354.7768N03540.7258E000.0140832068.4700000000L00BEB0D4+017.7)"));
+
+ verifyPosition(decoder, text(
+ "(088046338039BP05000088046338039160727A3354.7768N03540.7258E000.0140309065.1000000000L00BEB0D4+017.3)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,180716,144222,6,392,65535,255)"));
+
+ verifyAttributes(decoder, text(
+ "(087072009461BR00000007V0000.0000N00000.0000E000.00014039900000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(013612345678BO012061830A2934.0133N10627.2544E040.0080331309.6200000000L000770AD)"));
+
+ verifyNotNull(decoder, text(
+ "(088047194605BZ00,510,010,36e6,932c,43,36e6,766b,36,36e6,7668,32)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,040613,040137,6,421,112,0)"));
+
+ verifyAttributes(decoder, text(
+ "(864768010159785,ZC20,291015,030413,3,362,65535,255)"));
+
+ verifyPosition(decoder, text(
+ "(088047365460BR00151024A2555.3531S02855.3329E004.7055148276.1701000000L00009AA3)"),
+ position("2015-10-24 05:51:48.000", true, -25.92255, 28.92222));
+
+ verifyPosition(decoder, text(
+ "(088047365460BP05354188047365460150929A3258.1754S02755.4323E009.4193927301.9000000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BP05354188048003342150917A1352.9801N10030.9050E000.0103115265.5600010000L000003F9)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BR00150917A1352.9801N10030.9050E000.0103224000.0000010000L000003F9)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BR00150807A1352.9871N10030.9084E000.0110718000.0001010000L00000000)"));
+
+ verifyNull(decoder, text(
+ "(090411121854BP0000001234567890HSO)"));
+
+ verifyPosition(decoder, text(
+ "(01029131573BR00150428A3801.6382N02351.0159E000.0080729278.7800000000LEF9ECB9C)"));
+
+ verifyPosition(decoder, text(
+ "(035988863964BP05000035988863964110524A4241.7977N02318.7561E000.0123536356.5100000000L000946BB)"));
+
+ verifyPosition(decoder, text(
+ "(013632782450BP05000013632782450120803V0000.0000N00000.0000E000.0174654000.0000000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(013666666666BP05000013666666666110925A1234.5678N01234.5678W000.002033490.00000000000L000024DE)"));
+
+ verifyPosition(decoder, text(
+ "(013666666666BO012110925A1234.5678N01234.5678W000.0025948118.7200000000L000024DE)"));
+
+ verifyPosition(decoder, text(
+ "(088045133878BR00130228A5124.5526N00117.7152W000.0233614352.2200000000L01B0CF1C)"));
+
+ verifyPosition(decoder, text(
+ "(008600410203BP05000008600410203130721A4152.5790N01239.2770E000.0145238173.870100000AL0000000)"));
+
+ verifyPosition(decoder, text(
+ "(013012345678BR00130515A4843.9703N01907.6211E000.019232800000000000000L00009239)"));
+
+ verifyPosition(decoder, text(
+ "(012345678901BP05000012345678901130520A3439.9629S05826.3504W000.1175622323.8700000000L000450AC)"));
+
+ verifyPosition(decoder, text(
+ "(012345678901BR00130520A3439.9629S05826.3504W000.1175622323.8700000000L000450AC)"));
+
+ verifyPosition(decoder, text(
+ "(352606090042050,BP05,240414,V,0000.0000N,00000.0000E,000.0,193133,000.0)"));
+
+ verifyPosition(decoder, text(
+ "(352606090042050,BP05,240414,A,4527.3513N,00909.9758E,4.80,112825,155.49)"),
+ position("2014-04-24 11:28:25.000", true, 45.45586, 9.16626));
+
+ verifyPosition(decoder, text(
+ "(013632782450,BP05,101201,A,2234.0297N,11405.9101E,000.0,040137,178.48,00000000,L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(864768010009188,BP05,271114,V,4012.19376N,00824.05638E,000.0,154436,000.0)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,BP05,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC07,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC11,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC12,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC13,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC17,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(094050000111BP05000094050000111150808A3804.2418N04616.7468E000.0201447133.3501000011L0028019DT000)"));
+
+ verifyPosition(decoder, text(
+ "(864555555555555,DW3B,131117,A,5544.02870N,01315.08194E,1.597,223707,291.65,-0.10,4)"));
+
+ verifyPosition(decoder, text(
+ "(864555555555555,DW3B,131117,A,5544.02870N,01315.08194E,1.597,223707,291.65,0.10,8)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC07,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,040613,040137,6,42,112,0)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,DW51,200,1,3215,43370,2,58:F3:BB:3B:AA:82*-65*1,1C:6A:BB:AA:81:95*-78*1,151117,154419)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,DW5B,210,6,5995,47701,5,30:EE:CC:E7:86:DD*-59*11,4C:60:CC:EA:BB:EE*-68*1,42:AA:DE:EA:BB:00*-69*1,32:CD:BB:C3:4F:CC*-86*3,10:00:43:BA:22:15*-88*1,151117,163722)"));
+
+ verifyNotNull(decoder, text(
+ "(013632651491,DW50,460,0,0,6,2,aa:bb:cc:dd:ee:ff*-8*0,aa:bb:cc:dd:ee:ff*-8*0,040613,040137)"));
+
+ verifyNotNull(decoder, text(
+ "(013632651491,DW50,460,0,0,6,0,040613,040137)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,ZC03,191117,234207,$Notice: Device version: 1.0$)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,ZC03,191117,234207,$1 .Sensor sensitivity: 1\r\n2 .Alert status: Off\r\n3 .Check interval is set to 240 minute(s).\r\n4 .Checkgsm interval is set to 60 minute(s).\r\n5 .SOS SMS Alert: On\r\n6 .SOS Call Alert: On\r\n7 . Power: 95%$)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
new file mode 100644
index 000000000..34b2acf86
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
@@ -0,0 +1,282 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Tk103ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeOutputControl() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_DATA, "1");
+
+ assertEquals("(123456789012345AV001)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeEngineStop() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ assertEquals("(123456789012345AV010)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionSingle() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("(123456789012345AP00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionPeriodic() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 60);
+
+ assertEquals("(123456789012345AR00003C0000)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionStop() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_STOP);
+
+ assertEquals("(123456789012345AR0000000000)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeGetVersion() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_VERSION);
+
+ assertEquals("(123456789012345AP07)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeRebootDevice() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ assertEquals("(123456789012345AT00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSetOdometer() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_ODOMETER);
+
+ assertEquals("(123456789012345AX01)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionSingleAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("[begin]sms2,*getposl*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionPeriodicAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+
+ assertEquals("[begin]sms2,*routetrack*99*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionStopAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_STOP);
+
+ assertEquals("[begin]sms2,*routetrackoff*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeGetVersionAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_VERSION);
+
+ assertEquals("[begin]sms2,*about*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeRebootDeviceAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ assertEquals("[begin]sms2,88888888,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeIdentificationAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_IDENTIFICATION);
+
+ assertEquals("[begin]sms2,999999,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosOnAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SOS);
+ command.set(Command.KEY_ENABLE, true);
+
+ assertEquals("[begin]sms2,*soson*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosOffAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SOS);
+ command.set(Command.KEY_ENABLE, false);
+
+ assertEquals("[begin]sms2,*sosoff*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "AA00");
+
+ assertEquals("(123456789012345AA00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustomAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "any text is ok");
+
+ assertEquals("[begin]sms2,any text is ok,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSetConnectionAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_CONNECTION);
+ command.set(Command.KEY_SERVER, "1.2.3.4");
+ command.set(Command.KEY_PORT, "5555");
+
+ assertEquals("[begin]sms2,*setip*1*2*3*4*5555*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosNumberAlternative() throws Exception {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, "0");
+ command.set(Command.KEY_PHONE, "+55555555555");
+ command.set(Command.KEY_DEVICE_PASSWORD, "232323");
+
+ assertEquals("[begin]sms2,*master*232323*+55555555555*,[end]", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
new file mode 100644
index 000000000..83caae208
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
@@ -0,0 +1,64 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tlt2hProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tlt2hProtocolDecoder decoder = new Tlt2hProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "#867962040161955#MT600#0000#0#0#137#41#0#AUTO#1\r\n" +
+ "#00019023402$GPRMC,084702.00,A,3228.6772,S,11545.9684,E,,159.80,251018,,,A*56\r\n"));
+
+ verifyPositions(decoder, text(
+ "#868323028789359#MT600#0000#AUTOLOW#1\r\n",
+ "#07d8cd5198$GPRMC,164934.00,A,1814.4854,N,09926.0566,E,0.03,,240417,,,A*4A\r\n"));
+
+ verifyNull(decoder, text(
+ "#861075026000000#\r\n",
+ "#0000#AUTO#1\r\n",
+ "#002c4968045$GPRMC,001556.00,A,3542.1569,N,13938.9814,E,7.38,185.71,160417,,,A*55\r\n"));
+
+ verifyPositions(decoder, text(
+ "#863835026938048#MT500#0000#AUTO#1\r\n",
+ "#67904917c0e$GPRMC,173926.00,A,4247.8476,N,08342.6996,W,0.03,,160417,,,A*59\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030108689##0000#AUTO#1\r\n",
+ "#13AE2F8F$GPRMC,211452.000,A,0017.378794,S,03603.441981,E,0.000,0,060216,,,A*68\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030946351#V500#0000#AUTO#1\r\n",
+ "#$GPRMC,223835.000,A,0615.3545,S,10708.5779,E,14.62,97.41,070313,,,D*70\r\n"),
+ position("2013-03-07 22:38:35.000", true, -6.25591, 107.14297));
+
+ verifyPositions(decoder, text(
+ "\r\n#357671030946351#V500#0000#AUTO#1\r\n",
+ "#$GPRMC,223835.000,A,0615.3545,S,10708.5779,E,14.62,97.41,070313,,,D*70\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030938911#V500#0000#AUTOSTOP#1\r\n",
+ "#00b34d3c$GPRMC,140026.000,A,2623.6452,S,02828.8990,E,0.00,65.44,130213,,,A*4B\r\n"));
+
+ verifyPositions(decoder, text(
+ "#123456789000001#V3338#0000#SMS#3\r\n",
+ "#25ee0dff$GPRMC,083945.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E\r\n",
+ "#25ee0dff$GPRMC,083950.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E\r\n",
+ "#25ee0dff$GPRMC,083955.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E"));
+
+ verifyPositions(decoder, text(
+ "#353686009063310#353686009063310#0000#AUTO#2\r\n",
+ "#239757a9$GPRMC,150252.001,A,2326.6856,S,4631.8154,W,,,260513,,,A*52\r\n",
+ "#239757a9$GPRMC,150322.001,A,2326.6854,S,4631.8157,W,,,260513,,,A*55"));
+
+ verifyPositions(decoder, text(
+ "#357671031289215#V600#0000#AUTOLOW#1\r\n",
+ "#00735e1c$GPRMC,115647.000,A,5553.6524,N,02632.3128,E,0.00,0.0,130614,0.0,W,A*28"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java
new file mode 100644
index 000000000..0aaf567e8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TlvProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TlvProtocolDecoder decoder = new TlvProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "30430f383630323437303330303934333931ff10393233323132323030303834353433340f533636385f415f56312e30315f454eff1130303a30433a45373a30303a30303a30300132"));
+
+ verifyNull(decoder, binary(
+ "30410f383630323437303330303934333931"));
+
+ verifyNull(decoder, binary(
+ "30420f3836303234373033303039343339310131"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java
new file mode 100644
index 000000000..4f7be1b28
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TmgFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TmgFrameDecoder decoder = new TmgFrameDecoder();
+
+ verifyFrame(
+ binary("24696f662c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3332302c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e3161"),
+ decoder.decode(null, null, binary("111538360b383634353032303337393939363034eb0b1c8d00ffff23000000000000000000000000000000001c9401008c320c00188901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000001d940100c8950100bd0024696f662c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3332302c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e31610a000195010090960100bd0024746d702c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3237372c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e31610a00c9950100289701008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303230312c312c323832362e353938312c4e2c30373731382e363436352c452c3030302e302c3239382e35322c35323834372c32322c332c2d3233392c302c4c4c4c4c2c4e4e544e2c48482c302e32342c332e30332c3330313131363030312c302c56455230302e31610a00000091960100c09701008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303231322c312c323832362e353938312c4e2c30373731382e363437322c452c3030302e302c3037392e34322c35323834372c32332c332c2d3230352c302c4c4c4c4c2c4e4e544e2c48482c302e31352c332e30332c3330313131363030312c302c56455230302e31610a00000029970100589801008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303232322c312c323832362e353938302c4e2c30373731382e363438352c452c3030302e302c3037372e332c35323834372c32332c332c2d3137342c302c4c4c4c4c2c4e4e544e2c48482c302e31382c332e30332c3330313131363030312c302c56455230302e31610a00000000c1970100f09801008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303233312c312c323832362e353936342c4e2c30373731382e363437312c452c3030302e302c3133312e352c35323834372c32322c342c2d3134362c302c4c4c4c4c2c4e4e544e2c48482c302e31392c332e30332c3330313131363030312c302c56455230302e31610a0000000059980100889901008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303234332c312c323832362e353935382c4e2c30373731382e363436382c452c3030302e302c3133392e36362c35323834372c32322c342c2d3132312c302c4c4c4c4c2c4e4e544e2c48482c302e31322c332e30332c3330313131363030312c302c56455230302e31610a000000f1980100209a01008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303235332c312c323832362e353934392c4e2c30373731382e363436")));
+
+ verifyFrame(
+ binary("246c6f632c3836343530323033303335323734342c32393131323031372c3038333034392c312c323533342e363733312c4e2c30383733342e363735352c452c3033382e302c3037372e31392c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c332c56455230302e3161"),
+ decoder.decode(null, null, binary("246c6f632c3836343530323033303335323734342c32393131323031372c3038333034392c312c323533342e363733312c4e2c30383733342e363735352c452c3033382e302c3037372e31392c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c332c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333035392c312c323533342e363634322c4e2c30383733342e373333352c452c3032392e302c3132322e37302c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333130392c312c323533342e363531362c4e2c30383733342e373938312c452c3034342e302c3039342e39382c36303631322c31312c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31372c332e31302c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333131392c312c323533342e363432312c4e2c30383733342e383639312c452c3033352e302c3130312e37332c36303631322c31322c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333132392c312c323533342e363335352c4e2c30383733342e393135322c452c3032302e302c3131312e31322c36303631322c31322c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31362c332e31312c3330313131363030312c342c56455230302e31610a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java
new file mode 100644
index 000000000..5df2378d9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TmgProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TmgProtocolDecoder decoder = new TmgProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$loc,869309013800417,08032014,094459,1,2826.1956,N,07659.7690,E,0.0,2.5,4441,31,6,95,1,LLLL,NNTN,HH,0.15,0.26,HR38AU1389,0,SW0.1a"));
+
+ verifyPosition(decoder, text(
+ "$bak,864502037999604,21112017,120243,1,2826.5958,N,07718.6468,E,000.0,139.66,52847,22,4,-174,0,LLLL,NNTN,HH,0.12,3.03,301116001,0,VER00.1a"));
+
+ verifyNull(decoder, text(
+ "$iof,864502037999604,2282132017,8124280,0,299136216.-482258576,¥,1379245398.818734676,,-69969973.0,1153454437.-1986834092,492097799,20,0,-320,0,LLLL,NNTN,HH,0.17,3.01,301116001,0,VER00.1a"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868325023006341,14022017,103947,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000010,00000000,0111,00.0,00.0,0.0,SW10.12,NA,#"));
+
+ verifyPosition(decoder, text(
+ "$rid,L,868325023006341,14022017,103706,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000011,00000000,0111,00.0,00.0,0.0,SW10.12,0004909463,#"));
+
+ verifyPosition(decoder, text(
+ "$ion,H,868324023777431,27012017,101057,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,1.22,8,20,N.A,0,N.A,1,4.09,00.0,00000111,00000000,1101,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$iof,H,868324023777431,27012017,101111,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,0.87,11,21,N.A,25,N.A,0,4.09,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$rmv,L,868324023777431,27012017,101141,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,0.86,12,21,VODAFONE - DELH,24,3220,0,4.11,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$rnc,H,868324023777431,27012017,101013,4,2830.2923,N,07705.2551,E,0.0,9.65,226.0,0.88,12,21,VODAFONE - DELH,28,3220,0,4.14,07.4,00000111,00000000,1111,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$ebl,H,868324023777431,27012017,101046,4,2830.2923,N,07705.2551,E,0.0,9.65,226.0,0.97,11,21,VODAFONE - DELH,25,3220,0,4.11,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868324023777431,17012017,001023,4,2830.2977,N,07705.2478,E,0.0,207.07,229.2,0.97,11,22,IDEA CELLULAR L,18,DCDE,0,4.09,12.9,00000111,00000000,1111,00.0-00.0,00.0-0.0,3.59,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868324023777431,17012017,001523,4,2830.2939,N,07705.2527,E,0.0,50.96,236.5,1.05,11,21,IDEA CELLULAR L,18,DCDE,0,4.09,12.8,00000111,00000000,1111,00.0-00.0,00.0-0.0,3.59,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,869309999985699,24062015,094459,4,2826.1956,N,07659.7690,E,67.5,2.5,167,0.82,15,22,airtel,31,4441,1,4.1,12.7,00000011,00000011,1111,0.0,0.0,21.3,SW00.01,#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java
new file mode 100644
index 000000000..666a48bfa
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TopflytechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TopflytechProtocolDecoder decoder = new TopflytechProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(880316890094910BP00XG00b600000000L00074b54S00000000R0C0F0014000100f0130531152205A0706.1395S11024.0965E000.0251.25"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java
new file mode 100644
index 000000000..8fb5f8d54
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TotemFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TotemFrameDecoder decoder = new TotemFrameDecoder();
+
+ verifyFrame(
+ binary("24243030323542423836323031303033373239343836313345"),
+ decoder.decode(null, null, binary("24243030323542423836323031303033373239343836313345")));
+
+ verifyFrame(
+ binary("24243030363545363836313137323033353932363639357c3137303931323135333235372c2d37392e3337333835332c34332e3736353631392c302c302c7c3441"),
+ decoder.decode(null, null, binary("24243030363545363836313137323033353932363639357c3137303931323135333235372c2d37392e3337333835332c34332e3736353631392c302c302c7c3441")));
+
+ verifyFrame(
+ binary("24243031303841413836343234343032363036333433377c3141303030303030313430313031303130313031343131313030303032374241304535373030333130303030303030302e3030303030303030303030302e303030304e30303030302e3030303045303438313536"),
+ decoder.decode(null, null, binary("24243031303841413836343234343032363036333433377c3141303030303030313430313031303130313031343131313030303032374241304535373030333130303030303030302e3030303030303030303030302e303030304e30303030302e3030303045303438313536")));
+
+ verifyFrame(
+ binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c34463945"),
+ decoder.decode(null, null, binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c344639450d0a")));
+
+ verifyFrame(
+ binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c34463945"),
+ decoder.decode(null, null, binary("0d0a242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c344639450d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
new file mode 100644
index 000000000..287c54968
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
@@ -0,0 +1,116 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TotemProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TotemProtocolDecoder decoder = new TotemProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$0113AA862010037348253|588040001901220851494212000000753AE901655121700100000.800000002632.6084S02803.3289E29497E"),
+ position("2019-01-22 08:51:49.000", true, -26.54347, 28.05548));
+
+ verifyPosition(decoder, text(
+ "$$011602867119025755430|50099800180420045019401400000000000000B8797D110816811201.500002132615.7037S02801.8099E056149"));
+
+ verifyPosition(decoder, text(
+ "$$0108AB863835028447675|5004C0001710250234064214059828A058AE121010604000.600000320304.7772N10134.8238E11625B"));
+
+ verifyPosition(decoder, text(
+ "$$0108AA863835028447675|5004C0001710250234134114057728A058AE112108305100.600000660304.7787N10134.8719E116458"));
+
+ verifyPosition(decoder, text(
+ "$$0112AA864244026065291|180018001409160205244011000027BA0E57063100000001.200000002237.8119N11403.5075E05202D"));
+
+ verifyPosition(decoder, text(
+ "$$0116AA864244026065291|18001800140916020524401100000000000027BA0E57063100000001.200000002237.8119N11403.5075E052020"));
+
+ verifyPosition(decoder, text(
+ "$$0116AA867119025683137|108000001611020925324112000000000000616027F7001300000099.900000000000.0000N00000.0000E531824"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA864244026065291|18001800140916020524401100000000000000000000000027BA0E57063100000001.200000002237.8119N11403.5075E05202D"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA867965024919124|10010800160223032415401203270321032103270189000027BA0E4E001800200001.000000002237.7581N11403.5088E000957"),
+ position("2016-02-23 03:24:15.000", false, 22.62930, 114.05848));
+
+ verifyPosition(decoder, text(
+ "$$0108AA863835024426319|18004000160216160756411100007DCD0000111000000000.800000000316.3519N10228.5086E126522"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA867521029231005|1880100015101802314842140000000000000000000000001AB48366093127600000.900000000806.1947N09818.4795E080355"));
+
+ verifyPosition(decoder, text(
+ "$$0108AA864244026063437|1A0000001401010101014111000027BA0E57003100000000.000000000000.0000N00000.0000E048156"));
+
+ verifyPosition(decoder, text(
+ "$$BE863771024392112|AA$GPRMC,044704.000,A,1439.3334,N,12059.1417,E,0.00,0.00,200815,,,A*67|01.7|00.8|01.4|000000000000|20150820044704|14291265|00000000|4EECBF8B31|0000|0.0000|0002|00000|56E7"),
+ position("2015-08-20 04:47:04.000", true, 14.65556, 120.98570));
+
+ verifyPosition(decoder, text(
+ "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324"),
+ position("2015-03-14 05:10:02.000", true, 4.65437, 101.14907));
+
+ verifyPosition(decoder, text(
+ "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324\r"));
+
+ verifyNull(decoder, text(
+ "$$BB862170017856731|AA$GPRMC,000000.00,V,0000.0000,N,00000.0000,E,000.0,000.0,000000,,,A*73|00.0|00.0|00.0|000000001000|20000000000000|13790000|00000000|00000000|00000000|0.0000|0007|8C23"));
+
+ verifyPosition(decoder, text(
+ "$$B8862170017856731|AA$GPRMC,171849.00,A,3644.9893,N,01012.9927,E,0.049,51,200813,,,A*73|1.59|0.97|1.25|100000001000|20130820171849|13690000|00000000|019BD508|00000000|0.0000|0026|1B2C"));
+
+ verifyPosition(decoder, text(
+ "$$B2359772032984289|AA$GPRMC,104446.000,A,5011.3944,N,01439.6637,E,0.00,,290212,,,A*7D|01.8|00.9|01.5|000000100000|20120229104446|14151221|00050000|046D085E|0000|0.0000|1170|29A7"));
+
+ verifyPosition(decoder, text(
+ "$$8B862170017861566|AA180613080657|A|2237.1901|N|11402.1369|E|1.579|178|8.70|100000001000|13811|00000000|253162F5|00000000|0.0000|0014|2B16"),
+ position("2013-06-18 08:06:57.000", true, 22.61984, 114.03562));
+
+ verifyPosition(decoder, text(
+ "$$72862170017856731|3913090911165280000370000000000000000019BD508A0400000003.400000093644.9817N01012.9944E00506F2E"));
+
+ verifyPosition(decoder, text(
+ "$$B0456123|61$GPRMC,114725.00,A,1258.68276,N,07730.60237,E,0.410,,080113,,,A*79|1.44|0.66|1.27|000000000000|20130108114425|03600000|00000000|053C2BFE|0000|0.3325|0063|2005"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA000000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|000000000000|20090215000153|13601435|00000000|00000000|0000|0.0000|0007|2DAA"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA000000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|00000000|20090215001204|14182037|00000000|0012D888|0000|0.0000|0016|5B51"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA00000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|00000000000|20090215001337|14182013|00000000|0012D888|0000|0.0000|0017|346E"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|60$GPRMC,094859.000,A,3648.2229,N,01008.0976,E,0.00,,221211,,,A*79|02.3|01.3|02.0|000000000000|20111222094858|13360808|00000000|00000000|0000|0.0000|0001||A977"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|09$GPRMC,094905.000,A,3648.2229,N,01008.0976,E,0.00,,221211,,,A*71|02.1|01.3|01.7|000000000000|20111222094905|03210533|00000000|00000000|0000|0.0000|0002||FA58"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,093911.000,A,3648.2146,N,01008.0977,E,0.00,,140312,,,A*7E|02.1|01.1|01.8|000000000000|20120314093910|04100057|00000000|0012D887|0000|0.0000|1128||C50E"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,094258.000,A,3648.2146,N,01008.0977,E,0.00,,140312,,,A*7F|02.1|01.1|01.8|000000000000|20120314094257|04120057|00000000|0012D887|0000|0.0000|1136||CA32"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,234603.000,A,3648.2179,N,01008.0962,E,0.00,,030412,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674||940B"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,234603.000,A,3648.2179,N,01008.0962,E,0.00,,030412,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674|940B"));
+
+ verifyPosition(decoder, text(
+ "$$B2356895037578518|AA$GPRMC,173829.000,A,3740.4107,N,02129.9815,E,0.00,,111113,,,A*7B|02.6|01.6|02.1|000000000000|20131111173829|14041251|00000000|002E0DD7|0000|0.0240|6010|8128"));
+
+ verifyPosition(decoder, text(
+ "$$B2356895037578518|AA$GPRMC,203823.000,A,3740.3285,N,02129.9295,E,0.00,,111113,,,A*79|01.5|01.0|01.1|000000000000|20131111203823|14041251|00000000|002E0DD7|0000|0.0000|6371|3824"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
new file mode 100644
index 000000000..5a47f74cc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class TotemProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ TotemProtocolEncoder encoder = new TotemProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(2);
+ command.setType(Command.TYPE_ENGINE_STOP);
+ command.set(Command.KEY_DEVICE_PASSWORD, "000000");
+
+ assertEquals("*000000,025,C,1#", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java
new file mode 100644
index 000000000..76355066b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tr20ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tr20ProtocolDecoder decoder = new Tr20ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%%123456789012345,A,120101121800,N6000.0000E13000.0000,0,000,0,01034802,150,[Message]"));
+
+ verifyNull(decoder, text(
+ "%%TRACKPRO01,1"));
+
+ verifyPosition(decoder, text(
+ "%%868873457748532,A,181109121248,N2237.4181E11403.2857,000,282,NA,47010000,108"));
+
+ verifyPosition(decoder, text(
+ "%%TR-10,A,050916070549,N2240.8887E11359.2994,0,000,NA,D3800000,150,CFG:resend|"),
+ position("2005-09-16 07:05:49.000", true, 22.68148, 113.98832));
+
+ verifyPosition(decoder, text(
+ "%%TR-10,A,050916070549,N2240.8887E11359.2994,0,000,NA,D3800000,150,CFG:resend|"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java
new file mode 100644
index 000000000..92fe0da29
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tr900ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tr900ProtocolDecoder decoder = new Tr900ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">00001001,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!"),
+ position("2015-06-26 13:12:52.000", true, -31.62131, -58.50496));
+
+ verifyPosition(decoder, text(
+ ">12345678,1,1,070201,144111,W05829.2613,S3435.2313,,00,034,25,00,126-000,0,3,11111111*2d!"));
+
+ verifyPosition(decoder, text(
+ ">00001001,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java
new file mode 100644
index 000000000..e83824fb4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrackboxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrackboxProtocolDecoder decoder = new TrackboxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "a=connect&v=11&i=111111111111111"));
+
+ verifyPosition(decoder, text(
+ "183457.999,5126.0247N,00002.8686E,5.2,70.4,3,57.63,32.11,17.32,150507,05"),
+ position("2007-05-15 18:34:57.999", true, 51.43375, 0.04781));
+
+ verifyPosition(decoder, text(
+ "183558.999,5126.3979N,00003.0745E,5.2,70.4,3,57.63,32.11,17.32,150507,05"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java
new file mode 100644
index 000000000..4352fc935
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrakMateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrakMateProtocolDecoder decoder = new TrakMateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "^TMSTP|352984083995323|116|13.07809|77.55979|131508|131118|0.0|146.51|7|0|71 -2 248|0|13.1|0.0|10.5|1|0|0|0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMPER|354678456723764|1|12.59675|77.56789|123456|030414|2.3|34.0|1|0|0|0.015|3.9|12.0|23.4|23.4|1|1|0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMALT|354678456723764|3|2|1|12.59675|77.56789|123456|030414|1.2|34.0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMSRT|354678456723764|12.59675|77.56789|123456|030414|1.03|1.01|#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java
new file mode 100644
index 000000000..f482a00bb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TramigoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TramigoFrameDecoder decoder = new TramigoFrameDecoder();
+
+ verifyFrame(
+ binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"),
+ decoder.decode(null, null, binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46")));
+
+ verifyFrame(
+ binary("80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46"),
+ decoder.decode(null, null, binary("80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java
new file mode 100644
index 000000000..d35c5c54e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java
@@ -0,0 +1,63 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TramigoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TramigoProtocolDecoder decoder = new TramigoProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "8000c426b000a6000101c557037598050d5c8a595472616d69676f3a204d6f76696e672c20302e3132206b6d2045206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333134392c203130302e37333038352c204e572077697468207370656564203130206b6d2f682c2030303a34393a30382041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000c526b000a6000101f17d03759805115c8a595472616d69676f3a204d6f76696e672c20302e3133206b6d205345206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333132322c203130302e37333037382c204e4520776974682073706565642039206b6d2f682c2030303a34383a35332041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d426b0009f00010184f20375980593638a595472616d69676f3a204d6f76696e672c20302e3039206b6d204e57206f66204a616c616e2053696d70616e672042617475204d61726b65742c2054616970696e672c20506572616b2c204d592c20342e38333034332c203130302e37323230342c20532077697468207370656564203130206b6d2f682c2030313a32313a31322041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d626b0007f0001013c0b037598051d648a595472616d69676f3a2053746f707065642c206174204a616c616e2053696d70616e672042617475204d61726b65742c2054616970696e672c20506572616b2c204d592c20342e38323937322c203130302e37323233322c2030313a32323a34342041756720392020454f46"));
+
+ verifyNull(decoder, binary(
+ "80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d316b000860001018f8703771bee11fdf2585472616d69676f3a205061726b65642c20302e3131206b6d2053206f6620492e452e532e2050756572746120426f6e6974612c204361726162616e6368656c2c204d61647269642c2045532c2034302e33373736362c202d332e37333833352c2030353a3131204170722031362020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80009e08b00086000101bc1803778a59c58dea57546573742054323320547261636b65723a204d6f76696e672c20312e3639206b6d204e57206f66205574656b6f6e2c2045646f2c204e472c20362e34363137302c20352e36313938322c20452077697468207370656564203333206b6d2f682c2031363a3138205365702032372020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000853eb000b8000101fcff032f14665a89e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3135206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736352c20332e33343735352c2031303a3031204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000973eb000b90001012128032f14667794e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c2073746f707065642c20302e3134206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736372c20332e33343737332c2031303a3438204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000b73eb000ad000101fdd2032f1466c9cbe2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3131206b6d2045206f6620416c68616a69204d6173686120526f616420466f6f746272696467652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303031342c20332e33353434332c2031343a3434204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000883eb000d3000101b223032f1466fc89e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30323a34312c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639332c20332e33343636302c2031303a3033204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80009a3eb000d300010109ff032f1466b195e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30353a31342c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639312c20332e33343636322c2031303a3533204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000bc3eb000ba000101622c032f1466bacce2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30343a30302c206d6f76696e672c20617420416b6572656c6520526f61642d4f67756e6c616e612044726976652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303630332c20332e33353232382c2031343a3438204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80001d3cb000b3000101160f032f1466b475e0564176656e7369732053797353657276653a205374617475732c204750533a203931252c2047534d3a203737252c20475052533a20436f6e6e65637465642c20626174746572793a20313030252c207265706f7274733a2049676e6974696f6e20286f6666292c205374617475732028352c322e302c3732302c3330292c20362e34393239382c20332e33343836352c2031393a3038204d6172203920454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80005408b000af000101b23903677f00c8436d3842616c697365204f6e653a20416c6c756d616765206d61726368652064e974656374e92c20676172e92c20302e3735206b6d20452064652045636f6c65204175746f726f757465206465204b696e73686173612c2056696c6c65206465204b696e73686173612c204b696e73686173612c2043442c202d342e33343130362c2031352e33343931352c2030313a3030204a616e2031202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000011bb0009e0001015b93032ef6f35994a9545472616d69676f3a204d6f76696e672c20302e3930206b6d205345206f66204372616e6562726f6f6b20466972652053746174696f6e2c2050656e726974682c205379646e65792c2041552c202d33332e37303732322c203135302e37313735392c2053452077697468207370656564203337206b6d2f682c2031393a3438204a616e20342020454f46"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java
new file mode 100644
index 000000000..2fdb86218
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java
@@ -0,0 +1,67 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrvProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrvProtocolDecoder decoder = new TrvProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "TRVAP00352121088015548"));
+
+ verifyNotNull(decoder, text(
+ "IWAP02,zh_cn,0,6,260,1,11002|39252|9,11002|35112|23,11002|11043|24,11002|39253|24,11002|13751|24,11018|8102|26,3,a|c0-4a-00-b6-9c-f5|64&a|c0-4a-00-b6-9c-f5|64&a|18-a6-f7-92-35-da|84"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01170905A5227.1382N00541.4256E001.7095844000.0008100610020100,204,8,3230,13007"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,07800010010000602001206001120124"));
+
+ verifyNull(decoder, text(
+ "IWAP00353456789012345"));
+
+ verifyPosition(decoder, text(
+ "IWAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97& Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97"));
+
+ verifyNotNull(decoder, text(
+ "IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|3675|9,9520|3676|8,9520|3677|7,4,1|D8-24-BD-79-FA-1F|59&2|3C-46-D8-6D-CE-01|81&3|0C-4C-39-1A-7C-65|69&4|70-A8-E3-5D-D7-C0|65"));
+
+ verifyPosition(decoder, text(
+ "IWAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00,HOME|74-DE-2B-44-88-8C|97&HOME1|74-DE-2B-44-88-8C|97&HOME2|74-DE-2B-44-88-8C|97&HOME3|74-DE-2B-44-88-8C|97"));
+
+ verifyNull(decoder, text(
+ "IWAP03,06000908000102,5555,30"));
+
+ verifyNull(decoder, text(
+ "TRVAP00353456789012345"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,06000908000102"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,100007100000001020151060011"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01160211A2544.5118N05553.7586E105.711185941.52010001010010000,424,030,3011,27003"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01160209A2540.8863N05546.6125E005.6075734123.7910000810010000,424,030,3012,27323"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671"),
+ position("2008-05-24 06:18:30.000", true, 22.54968, 114.08226));
+
+ verifyPosition(decoder, text(
+ "TRVAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
new file mode 100644
index 000000000..e3833bcc7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tt8850ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tt8850ProtocolDecoder decoder = new Tt8850ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,007F,0,GTFRI,020102,867844000667538,4142726856,0,0,1,3,1.6,0,997.3,-66.830786,10.483394,20171212171418,0734,0004,041A,4220,69,20171212171657,FF61"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTFRI,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTGEO,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTNMR,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTNMR,,867844000400914,,0,41,1,2,0.0,0,1504.2,-75.569202,6.242850,20150404162835,,,,,97,20150404162836,05EF"));
+
+ verifyNull(decoder, text(
+ "\u0000\u0004,0017,0,GTPNA,,867844000400914,,0,0,1,0,,,,0,0,,,,,,99,20150404190153,0601"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTEPN,,867844000400914,,0,0,1,0,0.0,0,1717.4,-75.598445,6.278578,20150405003116,,,,,95,20150405003358,0607"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java
new file mode 100644
index 000000000..ed75cee38
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TytanProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TytanProtocolDecoder decoder = new TytanProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "B500192000001405125652CA9B1A325FC98D11A9990018020118FC0D"));
+
+ verifyPositions(decoder, binary(
+ "B500197800007422125652D7AC32325FD08D11A69900180200188280"));
+
+ verifyPositions(decoder, binary(
+ "B500181000001405115652DEEB2A325FC68D11A7D00005012A2AE1"));
+
+ verifyPositions(decoder, binary(
+ "B5005690000068494F561CEAE932325FD28D11A299000702000063045532030066013567018768014B6901286B0240396C04030785986D013E7F040000A7CE81040000A76C82027EAB83080FA01068FFFF0F3C880202583156"));
+
+ verifyPositions(decoder, binary(
+ "b50069a00000689d315604512b32378f1a8e9fe094005a04d7c84b41020300ab250402140c0702c0006501006601006b0280646c0402883db0315604525732378f1d8e9fdd94005a04d7c84b41020300ab250402140c0702c0006501006601006b0280646c0402883db08887"));
+
+ verifyPositions(decoder, binary(
+ "b50028080000689d215602772f00378f1b8e9fdd98005a042efb3e4102030000000402140c070200000901"));
+
+ verifyPositions(decoder, binary(
+ "b500280a0000689d215602772f00378f1b8e9fdd98005a042efb3e4102030000000402140c07020000da20"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
new file mode 100644
index 000000000..bd1fc71f4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
@@ -0,0 +1,53 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TzoneProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TzoneProtocolDecoder decoder = new TzoneProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "545a005b24240406010800000866050033819630120911071824000472bd8e5b0008aac01b07019b04bb002f00040b06161154000e100132ff2006161152000e080096ff4606161151000e1e0101ff1406161156000db6405bff490024469e0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "545a006624240406010800000866050033819630120911070e1d000472bd8e5b0008aac01b17019b04bc003a00050b06161151000e1e00ffff1406161152000e08008aff4706161154000e100134ff1f06161156000db0406cff4906161155000df44011ff4e0023811a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a005624240111010e0000086169303626931411091b151d2600160801de26ec002f633411091b151d2500000000160c0000040d2a34df000eaa4000001b37016000000000319c0000000000000000000000000000003a84240d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a005024240153011000000863835025559464110103080a22001609011bed79245964a9110103080a22000a0000550c00000604396f04222c000daac000151701a204870000000000000003000959000546190d0a"));
+
+ verifyPosition(decoder, binary(
+ "545A005224240153010E000008638350256668551008130616090016050079F63D2527FAF710081306160900A000002F0D33000803015B07013D7976000DAAE0400537016C049E000000000000000300800002B65EEA0D0A"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b022000000860041028904798100803030c2700160a007da96203356669100803030c2700000000000e000004002813730010aa4000000617017100000000000080000000000000000000000000000000007701fe0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b022000000860041028904798100803030d1a001609007da9620335666a100803030d1900000000000e000004002813730010aa400000063701720000000000008000000000000000000000000000000000787f0c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b021e000008637710239476270f080b0a3228001600000000000000000000000000000000000000000000000401a00822001088c00020183701a6053800000000800000000000000000000000000000000077c9860d0a"),
+ position("1999-11-30 00:00:00.000", false, 0.0, 0.0));
+
+ verifyPosition(decoder, binary(
+ "545A00912424010B021E000008661040203754350F061807083800160400CE5ADC041447620F0618070838000A0000060C7C0004253378370010AAC000000C37018504E500000000800000000000000000390B0A0014061113000000051200140610600014061220001000133800140610070010001473001000151100101500640010000920001000148400000000000000F2EF570D0A"),
+ position("2015-06-24 07:08:56.000", true, 22.53946, 114.06310));
+
+ verifyAttributes(decoder, binary(
+ "545A009E2424010A0205000008637710225481290F010F081E33000000000010A0C000310E35000005840000000000000000000000000066140A00140612200010001511001406101000140612490014061308001015006400051400170014061012000000050200140612470000000504001406100700140612510014061260001015012000000005080014061252001406130900101501410000000506000853A40D0A"));
+
+ verifyAttributes(decoder, binary(
+ "545A00992424010A0205000008637710225481290F010F082634000000000010A0C000311035000005870000000000000000000000000061130A000000050800101500640014061251001406130800051400170010150141001406101000140612200014061309000000050200140610070014061260001406124900140612470014061012001406125200100015110010150120000000050400183E8A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "545A00942424010A0205000008637710225481290F010F091C1F000000000010A1C000310F3500000586000000000000000000000000005C120A001406101000140612490014061012001406125200000005040000000502001015012000000005080010001511001406122000140612600014061247001406130900140610070010150141000514001700140612510010150064007A907C0D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
new file mode 100644
index 000000000..d3d0429d6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class UlbotechFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UlbotechFrameDecoder decoder = new UlbotechFrameDecoder();
+
+ assertEquals(
+ binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"),
+ decoder.decode(null, null, binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8")));
+
+ assertEquals(
+ binary("2a545330312c33353430343330353133383934363023"),
+ decoder.decode(null, null, binary("2a545330312c33353430343330353133383934363023")));
+
+ assertEquals(
+ binary("f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f705060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8"),
+ decoder.decode(null, null, binary("f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f70005060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
new file mode 100644
index 000000000..1c29ccd4a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -0,0 +1,90 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class UlbotechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "*TS01,353323081464660#"));
+
+ verifyAttributes(decoder, buffer(
+ "*TS01,868323025245751,134955140317,WFE:0#"));
+
+ verifyPosition(decoder, binary(
+ "f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"));
+
+ verifyPosition(decoder, binary(
+ "F8010103596580420045259CFB3329010E015ED91506BDE5A800000000009E030402420000040400492AA405060344197E220D071131058F410C1591310D48312F8F413107C60804027666B00C138254D182607A826EE083BE554385F50019423CAD1DF8"));
+
+ verifyNotNull(decoder, binary(
+ "F8010108683230231070781EA3676E020BFFFFFFFFFFFFFFFFFFFF780304000000030404000002C20506032A1790220E100101AC72F8"));
+
+ verifyNotNull(decoder, binary(
+ "f8010108683230220996561ea6ce1c020bffffffffffffffffffff78030400000000040400087b710506035519ad2214060800000000000000006220f8"));
+
+ verifyAttributes(decoder, binary(
+ "f8010108683230220996561ea6ce3f020b02cc00114e86000002f153030400000000040400087b710506035619a4221406080000000000000000fbcff8"));
+
+ verifyAttributes(decoder, binary(
+ "f8010108683230211861161e9d8c48020b00de0063eb730128b56161030400010001040400127d0705060174179422021005e000000001db06f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108683230220996561ea6cdf9010eff47465cfb68d7a000000000270f030400000000040400087b710506035119ba22140608000000000000000022cef8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108683230220996561ea74274010eff47477bfb68d89000000000270f030400000000040400087b710506035419472214060800000000000000006661f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103515790566431569e5fbb9d010e015ee2b906bde4a000000000009f03040a4000000404000115fe05060340173f22030711310583410c0000310d00312f834131000008040000b78c09077320290082c021100101120af8"));
+
+ verifyNull(decoder, buffer(
+ "*TS01,354043051389460#"));
+
+ verifyPosition(decoder, binary(
+ "f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f705060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108679650230651689dc8e45b010e01194a26fbd47fa6001f003c0054030402420000040400024d7b0506037c18692212071131057f410c0ee0310d1b312f41413112ef0804000dd59fcc32f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103596580419465449da89d16010efe5580fe0923d82100140129005903040242000004040001a7f10506037818be220e070e31057b410c1324310d144131fa3208040020b1418297f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103596580419465449da8564e010efe55a1800923d04b0000000000710304000000000404000178d2050603571876220ec3caf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103545500500179009ccb4b62010e00144db906310d3f0000000000cb0304000000000404000a8123050603211860221006080000000100000000ef97f8"));
+
+ verifyPosition(decoder, binary(
+ "F80101035785203457289495D60235010E016175A506C2C838000000000064"));
+
+ verifyPosition(decoder, binary(
+ "F8010108621060211481299C4247FA010E015EE1D606BDE797000301370081030402420000040400523CAF050603921743220706080000000000000000071131058E410C0E30310D48312F8E4131046A080402C8F2545445F8"));
+
+ verifyPosition(decoder, binary(
+ "F8010108621060211481299C4249FA010E015EE27506BDE80900020000008F030402420000040400523CAF05060392173F220706080000000000000000071131058E410C0E40310D48312F8E41310884080402CA60E43872F8"));
+
+ verifyNotNull(decoder, binary(
+ "f8010108653280262660481cdacf830209ffffffffffffffff780304000300000404000000030506017418a021f99697f8"));
+
+ verifyNotNull(decoder, binary(
+ "f801010865328026266048fffeae800209ffffffffffffffff7803040200000004040000000005060375175421f3060800000000000000009c28f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce260b4010e01e757bd022340d7002b010d01570304020200000404000000260506036a17d42200060800000000000000000a0101ab9ff8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce260df010e01e756f30223384a0003010a02a80304020200000404000001280506036217fe22010608000000000000000005aaf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce26128010e01e769ac022336290014010300730304020200000404000003c905060371181c2201060800000000000000000a0140e471f8"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
new file mode 100644
index 000000000..dbbe4591f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
@@ -0,0 +1,65 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class UproProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UproProtocolDecoder decoder = new UproProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "*HQ200861810538000002,BA&A0206033302618209658563620115180119&B0100000040&C6328680=&F0039&R2710&V0036&T09&K50000&N04&P0200#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ200999999,AB1&A1656512233362911356523660000230618&B0100060010&C00000<6<&F0000&R2405&V0109&W0000003E&K00100&T65&X(k89860045191536000374)#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ20113800138000,YAA&A0732142233550011405829060520190314&B0100000000&C00001234&R3109&T80#"));
+
+ verifyPosition(decoder, binary(
+ "2a4d473230313836383530303032303030343836372c414226413035303032343138313438373536303636303131373732323030303031313132313626583331302c3236302c34383837322c353639312c37333b34383837322c3732322c38363b34383837322c353639332c38383b34383837322c323336332c39303b34383837322c323336322c393726423030303030303030303026573030264e3230265a31342659313430303323"));
+
+ verifyPosition(decoder, binary(
+ "2a4d473230303639333530323030303033353537332c42412641303834313237333332363334353230373033383933373630303030303235313131362642303130303030303030302647303036323030264d393930264e3235264f3035303026433030313a363b363926510411058c0c125c0d0a2fff4237ee614d66454c140826555f50000000000300000000000000000026543139333723"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000035441,BA&A1213073325458307036690710000151116&P0730000032ce4fb3&D1&B0000000000&C005799?7&S3,20161115120025,07035.54659E,3324.87721N,3000,0,0,0,0,847,599,8,40,0,19,20&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0&T0107"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0800253335360507036975710000091116&P0730000032d2a94d&B0000000000&N13&Z12&U_P\0\0\0\u0004\0\0\0\0\0\0\0\0\0\0"),
+ position("2016-11-09 08:00:25.000", true, -33.58934, -70.61626));
+
+ verifyNull(decoder, buffer(
+ "*MG20113800138000,AH"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0200183324418107033792800009051116&B0000000000&N15&Z94&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0200543324412007033805910000051116&P0730000032d66785&B0000000000&N15&Z92&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905447674,BA&A2003064913201201845107561627121016&B0100000000&C05>8=961&F0333&K023101002154A7"));
+
+ verifyPosition(decoder, buffer(
+ "*AI200905300036,AH&A0317264913209801844913060000251115&B0500000000&C0;4?72:9&F0000"),
+ position("2015-11-25 03:17:26.000", false, 49.22016, 18.74855));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AS&A1647304913209801844913060000251115&B0400000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AC1&A1648014913209801844913060000251115&B0400000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AB1&A1702464913231101844949860000251115&B0500000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AD1&A1703054913231101844949860000251115&B0500000000&C0;4?72:9&F0000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java
new file mode 100644
index 000000000..e3761c3ef
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java
@@ -0,0 +1,77 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class V680ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ V680ProtocolDecoder decoder = new V680ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#867967020910610#01234567890#1#0000#AUT#1#0500000000120000#114.036291,E,22.665795,N,111.00,000.00#111116#193333##"),
+ position("2016-11-11 19:33:33.000", true, 22.66579, 114.03629));
+
+ verifyPosition(decoder, text(
+ "#355488020168617##1#0000#AUT#01#260001a412966f#1834.790700,E,5302.748800,N,0.00,0.00#310316#174538.000##"));
+
+ verifyPosition(decoder, text(
+ "#355488020168617##1#0000#AUT#01##1834.770100,E,5302.742800,N,0.62,0.00#310316#211537.000##"));
+
+ verifyNull(decoder, text(
+ "#353588102019155"));
+
+ verifyPosition(decoder, text(
+ "#135790246811222#13486119277#1#0000#SOS#1#27bc10af#11407.4182,E,2232.7632,N,0.00,79.50#070709#134147.000##"));
+
+ verifyPosition(decoder, text(
+ "#356823031193431##0#0000#SF#1#72403#V#04702.3025,W,2252.18380,S,008.18,0#090413#134938"),
+ position("2013-04-09 13:49:38.000", false, -22.86973, -47.038375));
+
+ verifyPosition(decoder, text(
+ "#356823033219838#1000#0#1478#AUT#1#66830FFB#03855.6628,E,4716.6821,N,001.41,259#130812#143905"));
+
+ verifyPosition(decoder, text(
+ "#353588102019155##1#0000#AUT#01#7240060be7873f#4849.079800,W,2614.458200,S,0.00,0.00#130413#182110.000"));
+
+ verifyPosition(decoder, text(
+ "#353588302045917##1#0000#AUT#01#7243141c2b14c3#4738.442300,W,2334.874000,S,0.00,0.30#170413#004831.000"));
+
+ verifyPosition(decoder, text(
+ "#352897045085282##0#0000#AUT#1#72400510730208,00d36307,10734fc4#4647.8922,W,2339.1956,S,2.60,63.74#200413#094310.000"));
+
+ verifyPosition(decoder, text(
+ "#356823033537791##0#0000#AUT#1#V#03610.2179,E,5004.5796,N,000.01,349#180513#073758"));
+
+ verifyPosition(decoder, text(
+ "#356823031236214##0#0000#AUT#1#V#01904.5491,E,6941.0085,N,000.09,248#170513#160140"));
+
+ verifyNull(decoder, text(
+ "#353588550032869##1#0000#AUT#01#72400401cd01a5#00000.0000,E,0000.0000,N,0.00,#000000#000000.000"));
+
+ verifyPosition(decoder, text(
+ "#352897045085282##0#0000#AUT#1#72400510730208,00d36307,10734fc4#4647.8922,W,2339.1956,S,2.60,63.74#200413#094310.000##"));
+
+ verifyPosition(decoder, text(
+ "#352165050199210##13#0000#AUT#1#72400605471305,054712fd,054712ff#05144.0008,W,3005.5011,S,0.11,201.46#260713#172647.000##"));
+
+ verifyPosition(decoder, text(
+ "#356823031166908#13001190527#0#0000#AUT#4#V#07136.4070,W,1040.0575,N,000.35,257#280813#142836#V#07136.4088,W,1040.0580,N,000.49,288#280813#142846#V#07136.4098,W,1040.0590,N,000.59,264#280813#142856#V#07136.4093,W,1040.0605,N,000.30,264#280813#142906##"));
+
+ verifyPosition(decoder, text(
+ "#355488020132015##1#0000#AUT#01#510089246a34c0#10641.338800,E,619.427100,S,0.00,0.00#011113#161942.000##"));
+
+ verifyPosition(decoder, text(
+ "#359094025419110#bigfriend#0#1234#AUTO#1##04632.8846,W,2327.2264,S,0.00,0.00#220913#234808##"));
+
+ verifyPosition(decoder, text(
+ "#353588102031599##1#0000#AUT#01#41300304843fc1#7955.124400,E,642.095500,N,5.28,95.21#041213#074431.000##"));
+
+ verifyPosition(decoder, text(
+ "1#0000#AUT#01#23403007fa650e#16.747700,W,5136.356500,N,0.00,0.00#040415#002051.000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java
new file mode 100644
index 000000000..11596945e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java
@@ -0,0 +1,40 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class VisiontekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VisiontekProtocolDecoder decoder = new VisiontekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$1,117,28,01,16,15,05,48,1725.0518N,07824.5298E,0620,11,0,185,2062,0,0,0,1,1,1,1,24,00.0000,00.3740,00.0000,VAJRA V1.00,A"));
+
+ verifyPosition(decoder, text(
+ "$1,VMC,358072044271838,26,10,15,10,43,20,17.066418N,080.395667E,000.0,285,00.8,0074,6390,0,0,0,0,0,0,0,0,00.00,00.00,00,00,0000,12.7,4.0,24,10,0000000000000,A,0"));
+
+ verifyNull(decoder, text(
+ "$1,VMC,358072044271838,25,10,15,09,19,40,00.0000000,000.0000000,000.0,000,00.0,0000,6070,0,0,0,0,0,0,0,0,00.00,00.00,00,00,0000,12.5,4.0,99,00,0000000000000,V,0"));
+
+ verifyPosition(decoder, text(
+ "$1,AP116,05,06,15,11,48,32,1725.0460N,07824.5289E,0617,07,0,030,2091,0,0,0,1,1,1,1,20,00.0000,00.3820,00.0000,VAJRA V1.00,A"));
+
+ verifyPosition(decoder, text(
+ "$1,AP09BU9397,861785006462448,20,06,14,15,03,28,17267339N,078279407E,060.0,073,0550,11,0,1,0,0,1,1,26,A,0000000000"),
+ position("2014-06-20 15:03:28.000", true, 17.44556, 78.46567));
+
+ verifyNull(decoder, text(
+ "$1,AP09BU9397,861785006462448,20,06,14,15,03,28,000000000,0000000000,000.0,000,0000,00,0,1,0,0,1,1,24,V,0000000000"));
+
+ verifyNull(decoder, text(
+ "$1,1234567890,02,06,11,17,07,45,00000000,000000000,00.0,0,0,V"));
+
+ verifyPosition(decoder, text(
+ "$1,1234567890,02,06,11,17,07,45,17267690N,078279340E,060.0,113,0,A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java
new file mode 100644
index 000000000..9422f6d74
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Vt200FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Vt200FrameDecoder decoder = new Vt200FrameDecoder();
+
+ verifyFrame(
+ binary("28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129"),
+ decoder.decode(null, null, binary("28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129")));
+
+ verifyFrame(
+ binary("28631037309456208400340102f51306171327294418267501208948170231071f0000044300200100005f02180000667500000000000000000000080000624629"),
+ decoder.decode(null, null, binary("28631037309456208400340102f513061713273d144418267501208948170231071f0000044300200100005f02180000667500000000000000000000080000624629")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java
new file mode 100644
index 000000000..9c224bc8e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java
@@ -0,0 +1,60 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Vt200ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Vt200ProtocolDecoder decoder = new Vt200ProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "28192030961807208200210101b919011818375801245774036424612500160917000003aa008800007b00aa3429"));
+
+ verifyNull(decoder, binary(
+ "286310373094563082002701033d010817143327c68a14841e00c27f550e9a000000000c000000084700200120007d01af260b29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062033d862631037309456f222014604362936f010817140954010817144135076b00002a3800003b7d6127cc91040000000000000000000000005a0000088e000001ce02630000263b000009b401ff00000cb40000069c02af000018190200000102019729"));
+
+ verifyPosition(decoder, binary(
+ "286310373094562086002101033d010817143328441790420114817637207d090a00000847002001207f00d6f229"));
+
+ verifyPosition(decoder, binary(
+ "286310373094562086002101033d0108171433354417932101148139772c9d080a00000847002001207f00dc6729"));
+
+ verifyNull(decoder, binary(
+ "2863103730945600880012180108171433004418103801148375470000dd29"));
+
+ verifyNull(decoder, binary(
+ "28631037309456108800002e29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062033c862631037309456f222014604362936f01081713365601081713571904c800001b2c000034f66827f0840000000000000000000000000047000006e7000001b9022a000023ff000007f2018a00000a10000003f300cd00000d8d0300000302002729"));
+
+ verifyNull(decoder, binary(
+ "28631037309456008e000801042307171804584229"));
+
+ verifyNull(decoder, binary(
+ "28631037309456108800002e29"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208200210103302307171805444417097301147188170198090f0000073a002000007e00074429"));
+
+ verifyNull(decoder, binary(
+ "286310373094563089001200032f2107171740144417075001147188872c29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062032f862631037309456f222014604362936f21071717373221071717401400a100000cd700000004020d3c8e0000000000000000000000000000000000000000000000000000000000000000000a000000040000000e009700000cc9000000000000e929"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208400340102dc090617161654441577230114439597368c0a0c0000030500200100417c1baa349d3290510000006a00007000003d15004c11c629"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
new file mode 100644
index 000000000..a5eb0b49b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class VtfmsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VtfmsFrameDecoder decoder = new VtfmsFrameDecoder();
+
+ assertEquals(
+ buffer("(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105"),
+ decoder.decode(null, null, buffer("(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java
new file mode 100644
index 000000000..ede5dc7ac
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class VtfmsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VtfmsProtocolDecoder decoder = new VtfmsProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(861359037432331,0EF87,00,0,21,2,01,,A,154559,230119,1101.4046,07656.3859,241,000,00078,00000,K,0000812,1,12.7,,,,,,1,0,0,0,1,1,1,+919566531111*+919994462226,)054"),
+ position("2019-01-23 15:45:59.000", true, 11.02341, 76.93977));
+
+ verifyPosition(decoder, text(
+ "(865733028143493,00I76,00,000,,,,,A,133755,210617,10.57354,077.24912,SW,000,00598,00000,K,0017368,1,12.7,,,0.000,,,0,0,0,0,1,1,0,,)074"));
+
+ verifyPosition(decoder, text(
+ "(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java
new file mode 100644
index 000000000..4e40eea86
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WatchFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WatchFrameDecoder decoder = new WatchFrameDecoder();
+
+ verifyFrame(
+ binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d"),
+ decoder.decode(null, null, binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d")));
+
+ verifyFrame(
+ binary("5b33472a383330383430363237392a303030382a72636170747572655d"),
+ decoder.decode(null, null, binary("5b33472a383330383430363237392a303030382a72636170747572655d")));
+
+ verifyFrame(
+ binary("5b33472a383330383430363237392a303030392a4c4b2c302c302c38345d"),
+ decoder.decode(null, null, binary("5b33472a383330383430363237392a303030392a4c4b2c302c302c38345d")));
+
+ verifyFrame(
+ binary("5b5a4a2a3031343131313030313335303330342a303033342a303030392a4c4b2c302c302c31395d"),
+ decoder.decode(null, null, binary("5b5a4a2a3031343131313030313335303330342a303033342a303030392a4c4b2c302c302c31395d")));
+
+ verifyFrame(
+ concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), binary("7d5b5d2c2aff"), buffer("]")),
+ decoder.decode(null, null, concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), binary("7d017d027d037d047d05ff"), buffer("]"))));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
new file mode 100644
index 000000000..539e63253
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
@@ -0,0 +1,128 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.junit.Test;
+import org.traccar.Context;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class WatchProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WatchProtocolDecoder decoder = new WatchProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001332708*0075*0064*AL,040418,052156,A,22.536207,N,113.938673,E,0,0,0,5,100,82,1000,50,00100000,1,255,460,0,9340,3663,35]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*352661090143150*006C*UD,150817,132115,V,28.435142,N,81.354333,W,2.2038,000,99,00,70,100,0,50,00000000,1,1,310,260,1091,30082,139,,00]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*4700609403*0013*bphrt,120,79,73,,,,]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*8308373902*0080*AL,230817,095346,A,47.083950,N,15.4821850,E,7.60,273.8,0.0,4,15,44,0,0,00200010,2,255,232,1,7605,42530,118,7605,58036,119,0,65.8]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051007430*006C*UD,150817,132115,V,28.435142,N,81.354333,W,2.2038,000,99,00,70,100,0,50,00000000,1,1,310,260,1091,30082,139,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6005412902*011F*WT,170517,133811,V,18.512200,N,73.7750283,E,0.00,0.0,0.0,0,92,82,4262,0,00000010,2,1,404,22,10125,8301,141,10125,13921,122,5,Skynet,28:c6:8e:be:87:c0,-60,Intel Wi-Fi,4c:60:de:32:3d:38,-70,Nirvanic-2,40:e3:d6:4a:d9:c2,-73,A4-Guest,40:e3:d6:4a:d9:c4,-73,A4Idatix,40:e3:d6:4a:d9:c3,-73,13.8]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*8308406279*00CC*UD3,170417,190930,V,54.739618,N,25.273213,E,0.0,323.53,175.1,6,51,83,0,0,00000000,1,1,246,01,200,13242758,51,3,TEO-189835,00:8c:54:58:1d:64,-84,Cgates_7137,78:54:2e:e3:71:37,-85,ASUS,9c:5c:8e:b8:d4:78,-93]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051004074*0058*AL,120117,145602,V,40.058413,N,76.336618,W,11.519,188,99,00,01,80,0,50,00000000,0,1,0,0,,10]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051000884*009B*UD,030117,161129,V,52.745450,N,0.369512,,0.1481,000,99,00,70,5,0,50,00000000,5,1,234,15,893,3611,135,893,3612,132,893,3993,131,893,30986,129,893,40088,126,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6430073509*00E7*UD2,241016,081622,V,09.951861,N,-84.1422119,W,0.00,0.0,0.0,0,39,94,0,0,00000000,1,0,712,3,2007,18961,123,4,Luz,00:23:6a:34:ee:76,-70,familia,b0:c5:54:b9:90:ef,-78,fam salas delgado,fc:b4:e6:5d:50:ea,-81,QWERTY,c8:3a:35:43:0f:e8,-93]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6105117105*008D*UD2,210716,231601,V,-33.480366,N,-70.7630692,E,0.00,0.0,0.0,0,100,34,0,0,00000000,3,255,730,2,29731,54315,167,29731,54316,162,29731,54317,145]"),
+ position("2016-07-21 23:16:01.000", false, -33.48037, -70.76307));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700222306*0077*UD,120316,140610,V,48.779045,N, 9.1574736,E,0.00,0.0,0.0,0,25,83,0,0,00000000,2,255,262,1,21041,9067,121,21041,5981,116]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700222306*011F*UD2,120316,140444,A,48.779045,N, 9.1574736,E,0.57,12.8,0.0,7,28,77,0,0,00000000,2,2,262,1,21041,9067,121,21041,5981,116,5,WG-Superlativ,34:31:c4:c8:a9:22,-67,EasyBox-28E858,18:83:bf:28:e8:f4,-70,MoMaXXg,be:05:43:b7:19:15,-72,MoMaXX2,bc:05:43:b7:19:15,-72,Gastzugang,18:83:bf:28:e8:f5,-72]"));
+
+ verifyNull(decoder, buffer(
+ "[SG*9081000548*0009*LK,0,100]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9081000548*00A9*UD,110116,113639,V,16.479064,S,68.119072,,0.7593,000,99,00,80,80,0,50,00000000,5,1,736,2,10103,10732,153,10103,11061,152,10103,11012,152,10103,10151,150,10103,10731,143,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*2256002206*0079*UD2,100116,153723,A,38.000000,N,-9.000000,W,0.44,299.3,0.0,7,100,86,0,0,00000008,2,0,268,3,3010,51042,146,3010,51043,132]"));
+
+ verifyNull(decoder, buffer(
+ "[3G*8800000015*0003*TKQ]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700186508*00B1*UD,301015,084840,V,45.853100,N,14.6224899,E,0.00,0.0,0.0,0,84,61,0,11,00000008,7,255,293,70,60,6453,139,60,6432,139,60,6431,132,60,6457,127,60,16353,126,60,6451,121,60,16352,118]"));
+
+ verifyNull(decoder, buffer(
+ "[SG*8800000015*0002*LK]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*4700186508*000B*LK,0,10,100]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*UD,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"),
+ position("2014-04-22 13:46:52.000", true, 22.57171, 113.86140));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*UD,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0088*UD2,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*AL,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0001,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyAttributes(decoder, buffer(
+ "[CS*8800000015*0008*PULSE,72]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*6005412902*0007*heart,0]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*6005412902*0008*heart,71]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0033*0064*UD,070318,020827,V,00.000000,N,000.000000,E,0,0,0,0,100,19,1000,50,00000000,1,255,460,0,9346,5223,42]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0035*0097*UD,070318,020857,V,00.000000,N,000.000000,E,0,0,0,0,100,19,1000,50,00000000,5,255,460,0,9346,5223,42,9346,5214,21,9784,4083,13,9346,5222,11,9346,5221,8]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0038*008a*UD,070318,021027,V,00.000000,N,000.000000,E,0,0,0,0,100,18,1000,50,00000000,4,255,460,0,9346,5223,42,9346,5214,20,9784,4083,11,9346,5221,5]"));
+
+ }
+
+ @Test
+ public void testDecodeVoiceMessage() throws Exception {
+
+ WatchProtocolDecoder decoder = new WatchProtocolDecoder(null);
+
+ verifyNull(decoder.decode(null, null, buffer("[CS*1234567890*0004*TK,1]")));
+
+ ByteBuf data = binary("7d5b5d2c2aff");
+
+ Object decodedObject = decoder.decode(null, null, concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), data.resetReaderIndex(), buffer("]")));
+ assertEquals("1234567890/mock.amr", ((Position) decodedObject).getAttributes().get("audio"));
+
+ verifyFrame(concatenateBuffers(buffer("#!AMR"), data.resetReaderIndex()), ((MockMediaManager) Context.getMediaManager()).readFile("1234567890/mock.amr"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
new file mode 100644
index 000000000..a0631be3b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
@@ -0,0 +1,83 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class WatchProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ WatchProtocolEncoder encoder = new WatchProtocolEncoder();
+
+ Command command;
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+ verifyFrame(buffer("[CS*123456789012345*0005*RESET]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_PHONE, "123456789");
+ verifyFrame(buffer("[CS*123456789012345*000e*SOS1,123456789]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "2321414d520a2573");
+ verifyFrame(buffer("[CS*123456789012345*000b*TK,#!AMR\n%s]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "7d5b5d2c2a");
+ verifyFrame(concatenateBuffers(buffer("[CS*123456789012345*000d*TK,"), binary("7d017d027d037d047d05"), buffer("]")), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "ff");
+ verifyFrame(concatenateBuffers(buffer("[CS*123456789012345*0004*TK,"), binary("ff"), buffer("]")), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_MESSAGE);
+ command.set(Command.KEY_MESSAGE, "text");
+ verifyFrame(buffer("[CS*123456789012345*0018*MESSAGE,0074006500780074]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "WORK,6-9,11-13,13-15,17-19");
+ verifyFrame(buffer("[CS*123456789012345*001a*WORK,6-9,11-13,13-15,17-19]"), encoder.encodeCommand(null, command));
+
+ }
+
+ @Test
+ public void testEncodeTimezone() {
+
+ WatchProtocolEncoder encoder = new WatchProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+
+ command.set(Command.KEY_TIMEZONE, "Europe/Amsterdam");
+ verifyFrame(buffer("[CS*123456789012345*0006*LZ,,+1]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "GMT+01:30");
+ verifyFrame(buffer("[CS*123456789012345*0008*LZ,,+1.5]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "Atlantic/Azores");
+ verifyFrame(buffer("[CS*123456789012345*0006*LZ,,-1]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "GMT-11:30");
+ verifyFrame(buffer("[CS*123456789012345*0009*LZ,,-11.5]"), encoder.encodeCommand(null, command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java
new file mode 100644
index 000000000..40b0469ea
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java
@@ -0,0 +1,67 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WialonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WialonProtocolDecoder decoder = new WialonProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "#L#2.0;42001300083;;CE45"));
+
+ verifyNull(decoder, text(
+ "#L#123456789012345;test"));
+
+ verifyNull(decoder, text(
+ "#L#2002;NA"));
+
+ verifyNull(decoder, text(
+ "#P#"));
+
+ verifyPosition(decoder, text(
+ "#D#101118;061143;0756.0930;N;12338.6403;E;18.223;99.766;-4.000;10;0.800;NA;NA;NA;NA;101_521347:1:521249,101_521126:1:6593598,101_521127:1:774780,101_521072_21.1:1:0,101_521072_21.2:1:71353;F24A"));
+
+ verifyPosition(decoder, text(
+ "#D#151216;135910;5321.1466;N;04441.7929;E;87;156;265.000000;12;1.000000;241;NA;NA;NA;odo:2:0.000000,total_fuel:1:430087,can_fls:1:201,can_taho:1:11623,can_mileage:1:140367515"));
+
+ verifyPosition(decoder, text(
+ "#D#151216;140203;5312.59514;N;04830.37834;E;53;273;NA;10;NA;NA;NA;NA;NA;EvId:1:1,Board:2:12.81,Accum:2:4.28"));
+
+ verifyPosition(decoder, text(
+ "#SD#270413;205601;5544.6025;N;03739.6834;E;1;2;3;4"),
+ position("2013-04-27 20:56:01.000", true, 55.74338, 37.66139));
+
+ verifyPosition(decoder, text(
+ "#SD#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5"));
+
+ verifyPosition(decoder, text(
+ "#D#270413;205601;5544.6025;N;03739.6834;E;1;2;3;4;0.0;0;0;14.77,0.02,3.6;NA;count1:1:564,fuel:2:45.8,hw:3:V4.5"));
+
+ verifyPosition(decoder, text(
+ "#D#190114;051312;4459.6956;N;04105.9930;E;35;306;204.000000;12;NA;452986639;NA;106.000000;NA;sats_gps:1:9,sats_glonass:1:3,balance:2:12123.000000,stay_balance:1:0"));
+
+ verifyPosition(decoder, text(
+ "#D#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5;1.74;NA;NA;NA;NA;NA"));
+
+ verifyPosition(decoder, text(
+ "#D#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5;1.74;NA;NA;;NA;NA"));
+
+ verifyPositions(decoder, text(
+ "#B#080914;073235;5027.50625;N;03026.19321;E;0.700;0.000;NA;4;NA;NA;NA;;NA;Батарея:3:100 %|080914;073420;5027.50845;N;03026.18854;E;1.996;292.540;NA;4;NA;NA;NA;;NA;Батарея:3:100 %"));
+
+ verifyPositions(decoder, text(
+ "#B#110914;102132;5027.50728;N;03026.20369;E;1.979;288.170;NA;NA;NA;NA;NA;;NA;Батарея:3:100 %"));
+
+ verifyPositions(decoder, text(
+ "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;;NA;Uacc:2:3.4,Iacc:2:0.000,Uext:2:13.2,Tcpu:2:14.4,Balance:2:167.65,GPS:3:Off"));
+
+ verifyPositions(decoder, text(
+ "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;14.77,0.02,3.6;AB45DF01145;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
new file mode 100644
index 000000000..642473f2d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class WondexFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WondexFrameDecoder decoder = new WondexFrameDecoder();
+
+ assertNull(
+ decoder.decode(null, null, binary("f0d70b0001ca9a3b")));
+
+ assertEquals(
+ binary("313034343938393630312c32303133303332333039353531352c31332e3537323737362c35322e3430303833382c302c3030302c37322c302c32"),
+ decoder.decode(null, null, binary("313034343938393630312c32303133303332333039353531352c31332e3537323737362c35322e3430303833382c302c3030302c37322c302c320d0a")));
+
+ assertEquals(binary("d0d70b0001ca9a3b"),
+ decoder.decode(null, null, binary("d0d70b0001ca9a3b")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java
new file mode 100644
index 000000000..f01a763a1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java
@@ -0,0 +1,62 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WondexProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WondexProtocolDecoder decoder = new WondexProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "2000000108,20151030145404,76.948633,43.354700,0,140,15,100,1,1325,125.4,10.5,0.0"),
+ position("2015-10-30 14:54:04.000", true, 43.35470, 76.94863));
+
+ verifyPosition(decoder, buffer(
+ "2000000257,20151030145351,69.379976,53.283905,0,0,16,2,0,0,469.1,58.9,0.0"),
+ position("2015-10-30 14:53:51.000", true, 53.28390, 69.37998));
+
+ verifyPosition(decoder, buffer(
+ "2000000232,20151030145206,51.166900,43.651353,0,132,11,2,0,0,0.0,0.0,0.0"));
+
+ verifyPosition(decoder, buffer(
+ "2000000259,20151030145653,69.380826,53.283890,9,10,15,2,1,695,1002.6,108.2,0.0"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130323074605,0.000000,90.000000,0,000,0,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "123456789000001,20120101123200,130.000000,60.000000,0,000,0,0,0,0"));
+
+ verifyPosition(decoder, buffer(
+ "210000001,20070313170040,121.123456,12.654321,0,233,0,9,2,0.0,0,0.00,0.00,0"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130322172647,13.572583,52.401070,22,204,49,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130322172647,13.572583,52.401070,22,204,-49,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "3997324533,20140326074908,28.797603,47.041635,0,48,0,6,2,3.90V,0"));
+
+ verifyPosition(decoder, buffer(
+ "2000000001,20140529213210,-63.179111,9.781493,0,0,54.0,8,2,0.0,0,0.01,0.01,0,0,0,0"));
+
+ verifyNotNull(decoder, buffer(
+ "$OK:VER=M7 2.003 DVB rev02c,V2"));
+
+ verifyNotNull(decoder, buffer(
+ "$OK:REBOOT"));
+
+ verifyNotNull(decoder, buffer(
+ "$ERR:GETLOCATION=1"));
+
+ verifyNull(decoder, binary(
+ "d0d70b0001ca9a3b"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
new file mode 100644
index 000000000..8209fc412
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class WondexProtocolEncoderTest extends ProtocolTest {
+ @Test
+ public void testEncode() throws Exception {
+
+ WondexProtocolEncoder encoder = new WondexProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(2);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+ command.set(Command.KEY_DEVICE_PASSWORD, "0000");
+
+ assertEquals("$WP+GETLOCATION=0000", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java
new file mode 100644
index 000000000..5635ce3d4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WristbandProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WristbandProtocolDecoder decoder = new WristbandProtocolDecoder(null);
+
+ verifyNotNull(decoder, binary(
+ "000102004459583836383730343034343735303035357c56312e307c317c7b4630342331382c30372c332c3539303139322c33303a31382c30372c332c3539303139322c33307d0d0afffefc"));
+
+ verifyNotNull(decoder, binary(
+ "000102009c59583836383730343034343735303035357c56312e307c317c7b4630332330383a30353a38313a64383a31383a38372c2d37365f30303a37663a32383a63373a62613a63312c2d37375f39633a33643a63663a65643a62643a36622c2d36335f64383a65623a39373a65653a37373a32342c2d38327c31382c30372c332c3539303735342c33303a31382c30372c332c3539303735342c33307d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102002259583836383730343034343735303035357c56312e307c307c7b46363423317d0d0afffefc"));
+
+ verifyPositions(decoder, binary(
+ "00010200bc59583836383730343034343735303035357c56312e307c317c7b4630322337372e3437373831372c2d33382e3839363239322c3230313831323239313235352c302e35387c37372e3437373739362c2d33382e3839363234352c3230313831323239313235352c302e30307c37372e3437373738392c2d33382e3839363233322c3230313831323239313235352c302e30307c37372e3437373737362c2d33382e3839363232322c3230313831323239313235352c302e30307d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102004759583836383730343034343735303035357c56312e307c317c7b463931233331305f30307c30307c30307c30307c57414e444149323031382f31322f31342031353a35367d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102004159583336373535313631303030303934347c56312e307c317c7b4639312330317c30307c30307c33475f7065745f323031382f30352f31362031313a30307d0d0afffefc"));
+
+ verifyPositions(decoder, false, binary(
+ "000102003559583836383730343034343735303035357c56312e307c317c7b4630312339342c312c3130302c302c33313030302c3930307d0d0afffefc"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
new file mode 100644
index 000000000..8fc628bdb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class XexunFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ XexunFrameDecoder decoder = new XexunFrameDecoder();
+
+ assertEquals(
+ binary("4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c"),
+ decoder.decode(null, null, binary("313531303132313435392c2b33313635323435343932372c4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c31323249")));
+
+ assertEquals(
+ binary("4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c"),
+ decoder.decode(null, null, binary("3135303631353132333733312c2b33333634373338343631312c4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c30342c333532322e392c463a332e3732562c302c3134322c32313734342c3230382c30312c303730322c394338430a0d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
new file mode 100644
index 000000000..8b0f245a2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
@@ -0,0 +1,118 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class XexunProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ XexunProtocolDecoder decoder = new XexunProtocolDecoder(null, false);
+
+ verifyAttributes(decoder, text(
+ "GPRMC,.000,A,0.000000,S,0.0000,W,0.00,0.00,,00,0000.0,A*55,L,,imei:353579010727036,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,113518.000,A,5303.4150,N,10.2368,E,60.73,207.42,260216,00,0000.0,A*74,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,215853.000,A,5304.9600,N,6.7907,E,1.43,80.67,250216,00,0000.0,A*47,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,121535.000,A,5417.2666,N,04822.1264,E,1.452,30.42,031014,0.0,A*4D\r\n,L,imei:355227042011730,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,150120.000,A,3346.4463,S,15057.3083,E,0.0,117.4,010911,,,A*76,F,imei:351525010943661,"),
+ position("2011-09-01 15:01:20.000", true, -33.77411, 150.95514));
+
+ verifyPosition(decoder, text(
+ "GPRMC,010203.000,A,0102.0003,N,00102.0003,E,1.02,1.02,010203,,,A*00,F,,imei:10000000000000,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,233842.000,A,5001.3060,N,01429.3243,E,0.00,,210211,,,A*74,F,imei:354776030495631,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,080303.000,A,5546.7313,N,03738.6005,E,0.56,160.13,100311,,,A*6A,L,imei:354778030461167,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,220828.678,A,5206.1446,N,02038.2403,,0,0,160912,,,E*23,L,imei:358948012501019,"));
+
+ verifyPosition(decoder, text(
+ "GNRMC,134418.000,A,5533.8973,N,03745.4398,E,0.00,308.85,160215,,,A*7A,F,, imei:864244028033115,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,093341.000,A,1344.5716,N,10033.6648,E,0.00,0.00,240215,,,A*68,F,,imei:865328028306149,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,103731.636,A,4545.5266,N,00448.8259,E,21.12,276.01,150615,,,A*57,L,, imei:013949002026675,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,014623.000,A,4710.8260,N,1948.1220,E,0.11,105.40,111212,00,0000.0,A*49,F,,imei:357713002048962,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,043435.000,A,811.299200,S,11339.9500,E,0.93,29.52,160313,00,0000.0,A*65,F,,imei:359585014597923,"));
+
+ decoder = new XexunProtocolDecoder(null, true);
+
+ verifyPosition(decoder, text(
+ "171007160505,,GPRMC,160505.000,A,5323.4680,N,00252.4202,W,000.0,129.7,071017,,,A*7A,F,ACCStart, imei:864504031916915,10,41.1,F:4.28V,1,135,19824,234,15,0062,B7D5"));
+
+ verifyPosition(decoder, text(
+ "171007160525,,GPRMC,160525.000,A,5323.4680,N,00252.4202,W,000.0,129.7,071017,,,A*78,F,ACCStop, imei:864504031916915,10,41.1,F:4.28V,1,134,42896,234,15,0062,B7D5"));
+
+ verifyPosition(decoder, text(
+ "170505103845,TELKOMSEL,GPRMC,103845.000,A,0340.2482,N,09841.9689,E,0.00,68.23,050517,,,A*5D,F,ACC On, imei:013227002782161,05,-8.2,F:4.22V,1,141,44712,510,10,2BE5,EC47"));
+
+ verifyPosition(decoder, text(
+ "170607031932,+6282167979090,GPRMC,031932.000,A,0347.2515,N,09841.9433,E,0.00,261.22,070617,,,A*6C,F,ACC OFF, imei:013226004613135,11,23.1,F:4.25V,1,148,44989,510,10,2B34,0268"));
+
+ verifyNull(decoder, text(
+ ",+48606717068,,L,, imei:012207005047292,,,F:4.28V,1,52,11565,247,01,000E,1FC5"));
+
+ verifyPosition(decoder, text(
+ "130302125349,+79604870506,GPRMC,085349.000,A,4503.2392,N,03858.5660,E,6.95,154.65,020313,,,A*6C,F,, imei:012207007744243,03,-1.5,F:4.15V,1,139,28048,250,01,278A,5072"),
+ position("2013-03-02 08:53:49.000", true, 45.05399, 38.97610));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120009.590,A,4639.6774,N,01418.5737,E,0.00,0.00,111111,,,A*68,F,, imei:359853000144328,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,L:3.5V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,L:3.5V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,204530.4,A,6000.0000,N,13000.0000,E,0.0,,010112,0.0,E,A*68,F,imei:123456789012345,04,123.5,F:3.55V,0,139,,232,03,272CE1,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,204530.4,A,6000.000,N,01000.6288,E,0.0,0.00,230713,0.0,E,A*3C,F,imei:123456789012345,00,,F:3.88V,0,125,,262,01,224CE1,379B"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,215840.7,A,6000.000,N,01000.6253,E,0.0,0.00,230713,0.0,E,A*34,F,imei:123456789012345,00,,F:3.9V,0,124,,262,01,224CE1,379B"));
+
+ verifyPosition(decoder, text(
+ "130725134142,,GPRMC,134142.591,A,3845.6283,N,00909.8876,W,2.08,287.33,250713,,,A*71,F,, imei:013227000526784,03,-50.7,L:3.69V,0,128,65337,268,03,177A,119F"));
+
+ verifyPosition(decoder, text(
+ "140602152533,TESCO_INFO,GPRMC,152533.000,A,5145.4275,N,00000.3448,E,0.00,0.00,020614,,,A*66,F,, imei:013227002781643,06,35.1,F:4.15V,1,135,38950,234,10,10B4,5235"));
+
+ verifyPosition(decoder, text(
+ "150216154418,5277,GNRMC,134418.000,A,5533.8973,N,03745.4398,E,0.00,308.85,160215,,,A*7A,F,, imei:864244028033115,10,169.8,F:4.28V,1,132,48269,250,99,6D0D,8572"));
+
+ verifyPosition(decoder, text(
+ "150224173341,+66961544651,GPRMC,093341.000,A,1344.5716,N,10033.6648,E,0.00,0.00,240215,,,A*68,F,,imei:865328028306149,05,106.4,F:4.01V/ADC1=0.20V/ADC2=0.00V,0,159,955,520,01,5DE8,0399,6.21km"));
+
+ verifyPosition(decoder, text(
+ "150316182840,07872167745,GPRMC,182840.000,A,5126.1310,N,00055.5573,W,0.00,0.00,160315,,,A*7C,F,,imei:865328023469306,06,54.3,F:4.10V/ADC1=0.76V/ADC2=0.00V,0,157,38486,234,10,34DC,48A6,3.70km"));
+
+ verifyPosition(decoder, text(
+ "150615123731,+33647384611,GPRMC,103731.636,A,4545.5266,N,00448.8259,E,21.12,276.01,150615,,,A*57,L,, imei:013949002026675,04,3522.9,F:3.72V,0,142,21744,208,01,0702,9C8C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
new file mode 100644
index 000000000..f795742fd
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
@@ -0,0 +1,76 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class XirgoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeCustom() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,HD,SV,HP,BV,CQ,GS,SI,IG,OT");
+
+ verifyPosition(decoder, text(
+ "$$183900034,4002,03/30/2019,02:15:22,46.848577,-114.022213,978,0.0,172.3,16,1.2,13.291,20,3,2,2,1##"));
+
+ verifyPosition(decoder, text(
+ "$$184800793,4002,03/30/2019,02:10:13,46.848600,-114.022256,9723,0.0,1645,17,1.2,13.283,18,3,89011703278246523594,2,0##"));
+
+ decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,SV,HP,BV,CQ,MI,GS,SI,IG,OT");
+
+ verifyPosition(decoder, text(
+ "$$184800793,4002,03/15/2019,21:30:21,46.848582,-114.022237,9733,0.0,18,1.1,13.605,20,0,3,89011703278246523602,2,0##"));
+
+ }
+
+ @Test
+ public void testDecodeNew() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$352054058132185,4001,2017/04/21,00:01:05,32.54659,-116.90670,143.2,0,0,0,598,0.0,12,0.9,765840,7.0,14.5,19,1,1,0011,8.5,63.2,5,21999,184,255,671,207,100,185##"));
+
+ verifyPosition(decoder, text(
+ "$$352054058132185,6011,2017/04/21,04:57:10,32.49658,-116.85957,250.9,0,0,0,602,0.0,12,0.8,765876,7.0,14.1,21,1,1,0011,10.1,0.0,5,170917890,280,255,627,0,100,167##"));
+
+ verifyPosition(decoder, text(
+ "$$355922061611345,6001,2016/08/25,20:10:51,51.13042,-114.22752,1197,44.7,0.0,0.0,2622,27,12,0.8,1,0.0,13.9,24,1,0,0.0,-70,-809,688##"));
+
+ verifyPosition(decoder, text(
+ "$$355922061611345,6001,2016/08/25,20:10:38,51.12948,-114.22637,1203,34.8,0.0,0.0,1377,215,12,0.8,1,0.0,13.8,28,1,0,0.0,-309,-566,754##"));
+
+ verifyPosition(decoder, text(
+ "$$354898045650537,6031,2015/02/26,15:47:26,33.42552,-112.30308,287.8,0,0,0,0,0.0,7,1.2,2,0.0,12.2,22,1,0,82.3"));
+
+ verifyPosition(decoder, text(
+ "$$355922060162167,6015,2016/04/21,17:26:52,39.83267,-76.66139,230,0.0,0.0,0.0,779,0,8,1.2,0,0.0,13.0,19,1,1C4BJWDG4GL191009,X0z1-1137CD1,0402,3GATT,0,83.9,-70,-715,738##"));
+
+ verifyPosition(decoder, text(
+ "$$355922060162167,4002,2016/04/21,17:04:50,39.83253,-76.66102,232,0.0,0.0,0.0,0,0,12,1.2,0,0.0,9.2,15,1,0,0.0,35,-8,1059##"));
+
+ }
+
+ @Test
+ public void testDecodeOld() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$354660046140722,6001,2013/01/22,15:36:18,25.80907,-80.32531,7.1,19,165.2,11,0.8,11.1,17,1,1,3.9,2##"),
+ position("2013-01-22 15:36:18.000", true, 25.80907, -80.32531));
+
+ verifyPosition(decoder, text(
+ "$$357207059646786,4003,2015/05/19,15:54:56,-20.21422,-70.14927,37.5,1.8,0.0,11,0.8,12.9,31,297,1,0,0.0,0.0,0,1,1,1##"));
+
+ verifyPosition(decoder, text(
+ "$$354898045650537,6031,2015/02/26,15:47:26,33.42552,-112.30308,287.8,0,0,0,0,0.0,7,1.2,2,0.0,12.2,22,1,0,82.3"));
+
+ verifyPosition(decoder, text(
+ "$$357207059646786,4003,2015/05/19,15:55:27,-20.21421,-70.14920,33.6,0.4,0.0,11,0.8,12.9,31,297,1,0,0.0,0.0,0,1,1,1##"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
new file mode 100644
index 000000000..dd2e939c9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class XirgoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ XirgoProtocolEncoder encoder = new XirgoProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 0);
+ command.set(Command.KEY_DATA, 1);
+
+ assertEquals("+XT:7005,2,1", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
new file mode 100644
index 000000000..0f15f31b4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xrb28ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xrb28ProtocolDecoder decoder = new Xrb28ProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "*SCOR,OM,123456789123456,Q0,412,80,28#"));
+
+ verifyPosition(decoder, text(
+ "*SCOR,OM,867584030387299,D0,0,012102.00,A,0608.00062,S,10659.70331,E,12,0.69,151118,30.3,M,A#"));
+
+ verifyAttributes(decoder, text(
+ "*SCOR,OM,863158022988725,H0,0,412,28,80,0#"));
+
+ verifyAttributes(decoder, text(
+ "*HBCR,OM,123456789123456,R0,0,55,1234,1497689816#"));
+
+ verifyPosition(decoder, text(
+ "*HBCR,OM,123456789123456,D0,0,124458.00,A,2237.7514,N,11408.6214,E,6,0.21,151216,10,M,A#"));
+
+ verifyPosition(decoder, text(
+ "*SCOR,OM,863158022988725,D0,0,124458.00,A,2237.7514,N,11408.6214,E,6,0.21,151216,10,M,A#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
new file mode 100644
index 000000000..49476d694
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Xrb28ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() {
+
+ Xrb28ProtocolEncoder encoder = new Xrb28ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("\u00ff\u00ff*SCOS,OM,123456789012345,D1,300#\n", encoder.encodeCommand(null, command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() {
+
+ Xrb28ProtocolEncoder encoder = new Xrb28ProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "S7,0,3,0,0,20,25");
+
+ assertEquals("\u00ff\u00ff*SCOS,OM,123456789012345,S7,0,3,0,0,20,25#\n", encoder.encodeCommand(null, command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java
new file mode 100644
index 000000000..aa44929ab
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xt013ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xt013ProtocolDecoder decoder = new Xt013ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "TK,862950021650364,150131090859,+53.267863,+5.767363,0,38,12,0,F,204,08,C94,336C,24,,4.09,1,,,,,,,,"),
+ position("2015-01-31 09:08:59.000", true, 53.26786, 5.76736));
+
+ verifyPosition(decoder, text(
+ "TK,862950021650364,150118113832,+53.267722,+5.767143,0,86,12,0,F,204,08,C94,336C,22,,4.21,1,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "HI,862950021650364TK,862950021650364,150118113832,+53.267722,+5.767143,0,86,12,0,F,204,08,C94,336C,22,,4.21,1,,,,,,,,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
new file mode 100644
index 000000000..f4a78b5bd
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xt2400ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xt2400ProtocolDecoder decoder = new Xt2400ProtocolDecoder(null);
+
+ decoder.setConfig("\n:wycfg pcr[0] 001001030406070809570a13121714100565\n");
+
+ verifyPosition(decoder, binary(
+ "000a344f1f0259766ae002074289f8f1c4b200e80000026712068000130000029300883559464255524845364650323433343235"));
+
+ decoder.setConfig("\n:wycfg pcr[0] 000f01030406070809570a131217141005\n");
+
+ verifyPosition(decoder, binary(
+ "0009c4fb9b0b58a771e4020742d9f8f1c4c300bc0000000011077c0015000000000001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java
new file mode 100644
index 000000000..332d15fa5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class YwtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ YwtProtocolDecoder decoder = new YwtProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%RP,1222102985:1,170509033842,E102.146563,N14.582175,,0,320,10,0,00-00-00-00-00-00-00-00-00-00-00-00,,1db2-02b3-52004,3>941.523-32,7>1,19>-16,20>30.9V"));
+
+ verifyNull(decoder, text(
+ "%SN,0417061042:0,0,140117041203,404"));
+
+ verifyPosition(decoder, text(
+ "%GP,3000012345:0,090723182813,E114.602345,N22.069725,,30,160,4,0,00,,2794-10FF-46000,3>0-0"));
+
+ verifyPosition(decoder, text(
+ "%RP,3000012345:0,090807182815,E114.602345,N22.069725,,30,160,4,0,00"),
+ position("2009-08-07 18:28:15.000", true, 22.06973, 114.60235));
+
+ verifyPosition(decoder, text(
+ "%KP,3000012345:0,090807183115,E114.602345,N22.069725,,30,160,5,0,00;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/reports/ReportUtilsTest.java b/src/test/java/org/traccar/reports/ReportUtilsTest.java
new file mode 100644
index 000000000..01b9c276f
--- /dev/null
+++ b/src/test/java/org/traccar/reports/ReportUtilsTest.java
@@ -0,0 +1,390 @@
+package org.traccar.reports;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.model.Position;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
+
+public class ReportUtilsTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private Position position(String time, double speed, double totalDistance) throws ParseException {
+
+ Position position = new Position();
+
+ position.setTime(date(time));
+ position.setValid(true);
+ position.setSpeed(speed);
+ position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
+
+ return position;
+ }
+
+ @Test
+ public void testCalculateDistance() {
+ Position startPosition = new Position();
+ startPosition.set(Position.KEY_TOTAL_DISTANCE, 500.0);
+ Position endPosition = new Position();
+ endPosition.set(Position.KEY_TOTAL_DISTANCE, 700.0);
+ assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 200.0, 10);
+ startPosition.set(Position.KEY_ODOMETER, 50000);
+ endPosition.set(Position.KEY_ODOMETER, 51000);
+ assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 1000.0, 10);
+ }
+
+ @Test
+ public void testCalculateSpentFuel() {
+ Position startPosition = new Position();
+ Position endPosition = new Position();
+ assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.0, 0.01);
+ startPosition.set(Position.KEY_FUEL_LEVEL, 0.7);
+ endPosition.set(Position.KEY_FUEL_LEVEL, 0.5);
+ assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.2, 0.01);
+ }
+
+ @Test
+ public void testDetectTripsSimple() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000),
+ position("2016-01-01 00:07:00.000", 0, 3000));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectTripsSimpleWithIgnition() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000),
+ position("2016-01-01 00:07:00.000", 0, 3000));
+
+ data.get(5).set(Position.KEY_IGNITION, false);
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, true, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectTripsWithFluctuation() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 10, 3000),
+ position("2016-01-01 00:06:00.000", 10, 4000),
+ position("2016-01-01 00:07:00.000", 0, 5000),
+ position("2016-01-01 00:08:00.000", 10, 6000),
+ position("2016-01-01 00:09:00.000", 0, 7000),
+ position("2016-01-01 00:10:00.000", 0, 7000),
+ position("2016-01-01 00:11:00.000", 0, 7000));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:09:00.000"), itemTrip.getEndTime());
+ assertEquals(420000, itemTrip.getDuration());
+ assertEquals(8.57, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(7000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:09:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:11:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsOnly() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 1, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(300000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsWithTripCut() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 2, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getEndTime());
+ assertEquals(240000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsStartedFromTrip() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 2, 0),
+ position("2016-01-01 00:01:00.000", 1, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 0, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(180000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsMoving() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 5, 0),
+ position("2016-01-01 00:01:00.000", 5, 0),
+ position("2016-01-01 00:02:00.000", 3, 0),
+ position("2016-01-01 00:03:00.000", 5, 0),
+ position("2016-01-01 00:04:00.000", 5, 0),
+ position("2016-01-01 00:05:00.000", 5, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertTrue(result.isEmpty());
+
+ }
+
+ @Test
+ public void testDetectTripAndStopByGap() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 7, 100),
+ position("2016-01-01 00:01:00.000", 7, 300),
+ position("2016-01-01 00:02:00.000", 5, 500),
+ position("2016-01-01 00:03:00.000", 5, 600),
+ position("2016-01-01 00:04:00.000", 3, 700),
+ position("2016-01-01 00:23:00.000", 2, 700),
+ position("2016-01-01 00:24:00.000", 5, 800),
+ position("2016-01-01 00:25:00.000", 5, 900));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 200000, 200000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:04:00.000"), itemTrip.getEndTime());
+ assertEquals(240000, itemTrip.getDuration());
+ assertEquals(6.75, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(7, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(600, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ StopReport itemStop = stops.iterator().next();
+
+ assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:24:00.000"), itemStop.getEndTime());
+ assertEquals(1200000, itemStop.getDuration());
+ }
+
+}
diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java
new file mode 100644
index 000000000..5a79fbac2
--- /dev/null
+++ b/src/test/java/org/traccar/web/WebServerTest.java
@@ -0,0 +1,29 @@
+package org.traccar.web;
+
+import org.junit.Test;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class WebServerTest {
+
+ @Test
+ public void contextTest() throws NamingException {
+ DataSource mockDataSource = (DataSource) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[] { DataSource.class }, new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+
+ Context context = new InitialContext();
+ context.bind("java:/DefaultDS", mockDataSource);
+ }
+
+}