diff --git a/BBus/BBus.xcodeproj/project.pbxproj b/BBus/BBus.xcodeproj/project.pbxproj index a890e5a2..ea0c5594 100644 --- a/BBus/BBus.xcodeproj/project.pbxproj +++ b/BBus/BBus.xcodeproj/project.pbxproj @@ -159,6 +159,45 @@ 87038A90273C11630078EAE3 /* GetStationsByRouteListFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A8F273C11630078EAE3 /* GetStationsByRouteListFetcher.swift */; }; 87038A92273C12320078EAE3 /* GetBusPosByRtidFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A91273C12320078EAE3 /* GetBusPosByRtidFetcher.swift */; }; 87038A94273C12E20078EAE3 /* GetStationByUidItemFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A93273C12E20078EAE3 /* GetStationByUidItemFetcher.swift */; }; + 87075FFD27569066005A1E37 /* MovingStatusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA522E272FCEDF00EC0531 /* MovingStatusViewModel.swift */; }; + 87075FFE275690E0005A1E37 /* MovingStatusAPIUsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04468438275677C0007E440A /* MovingStatusAPIUsable.swift */; }; + 87075FFF2756914E005A1E37 /* BusRouteDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375AA273B9F330061ACDA /* BusRouteDTO.swift */; }; + 8707600027569158005A1E37 /* StationByRouteListDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375B4273BB98E0061ACDA /* StationByRouteListDTO.swift */; }; + 8707600127569160005A1E37 /* BusPosByRtidDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375B6273BE08F0061ACDA /* BusPosByRtidDTO.swift */; }; + 8707600227569254005A1E37 /* MovingStatusCalculatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0446843A275677CA007E440A /* MovingStatusCalculatable.swift */; }; + 8707600327569254005A1E37 /* MovingStatusCalculateUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B35D092754E71F00159791 /* MovingStatusCalculateUseCase.swift */; }; + 8707600527569303005A1E37 /* AverageSectionTimeCalculatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 875F1AAF2755BD86003F5BB1 /* AverageSectionTimeCalculatable.swift */; }; + 8707600627569309005A1E37 /* BaseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5100322754CDB100754B36 /* BaseUseCase.swift */; }; + 8707600727569319005A1E37 /* NotificationNameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A06AEE82743DAB20027222D /* NotificationNameExtension.swift */; }; + 8707600827569324005A1E37 /* JsonDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0446843C275679B9007E440A /* JsonDTO.swift */; }; + 870760092756932E005A1E37 /* PublisherExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A38E20827464015003A9D10 /* PublisherExtension.swift */; }; + 8707600A27569337005A1E37 /* BBusAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A38E20627463FF7003A9D10 /* BBusAPIError.swift */; }; + 87115EFA27564D0F00601770 /* RequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182A527550988001EA530 /* RequestFactory.swift */; }; + 87115EFB27565A3300601770 /* BusRouteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA520A272FCE5F00EC0531 /* BusRouteViewModel.swift */; }; + 87115EFC27565BAD00601770 /* BusRouteAPIUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA5208272FCE5A00EC0531 /* BusRouteAPIUseCase.swift */; }; + 87115EFD27565D9D00601770 /* BusRouteDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375AA273B9F330061ACDA /* BusRouteDTO.swift */; }; + 87115EFE27565DAE00601770 /* StationByRouteListDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375B4273BB98E0061ACDA /* StationByRouteListDTO.swift */; }; + 87115EFF27565DC200601770 /* BusPosByRtidDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049375B6273BE08F0061ACDA /* BusPosByRtidDTO.swift */; }; + 87115F00275667A600601770 /* BaseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5100322754CDB100754B36 /* BaseUseCase.swift */; }; + 87115F01275668E100601770 /* BusCongestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA5212272FCE8500EC0531 /* BusCongestion.swift */; }; + 87115F02275668F600601770 /* NotificationNameExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A06AEE82743DAB20027222D /* NotificationNameExtension.swift */; }; + 87115F032756693700601770 /* BusPosByVehicleIdDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ADB29BA274B6C8300554A4E /* BusPosByVehicleIdDTO.swift */; }; + 87115F042756694600601770 /* PublisherExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A38E20827464015003A9D10 /* PublisherExtension.swift */; }; + 87115F052756696F00601770 /* GetRouteListUsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182B627551ED8001EA530 /* GetRouteListUsable.swift */; }; + 87115F062756698D00601770 /* BBusAPIUseCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A81273B90A50078EAE3 /* BBusAPIUseCases.swift */; }; + 87115F0727566A4400601770 /* GetStationsByRouteListUsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182B027551E84001EA530 /* GetStationsByRouteListUsable.swift */; }; + 87115F0827566A5200601770 /* TokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182A32754E3CA001EA530 /* TokenManager.swift */; }; + 87115F0927566A5900601770 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A89273B96B60078EAE3 /* NetworkService.swift */; }; + 87115F0A27566A6200601770 /* PersistenceStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A8B273B96DF0078EAE3 /* PersistenceStorage.swift */; }; + 87115F0B27566A6900601770 /* RequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182A527550988001EA530 /* RequestFactory.swift */; }; + 87115F0C27566A7000601770 /* GetStationsByRouteListFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A8F273C11630078EAE3 /* GetStationsByRouteListFetcher.swift */; }; + 87115F0D27566A7800601770 /* ServiceFetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182A7275511D5001EA530 /* ServiceFetchable.swift */; }; + 87115F0E27566A8000601770 /* GetBusPosByRtidUsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182B227551EA9001EA530 /* GetBusPosByRtidUsable.swift */; }; + 87115F0F27566A8E00601770 /* GetBusPosByRtidFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87038A91273C12320078EAE3 /* GetBusPosByRtidFetcher.swift */; }; + 87115F1027566A9800601770 /* GetRouteListFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA294AE273C0E8D008E5497 /* GetRouteListFetcher.swift */; }; + 87115F1127566A9F00601770 /* BBusAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A38E20627463FF7003A9D10 /* BBusAPIError.swift */; }; + 87115F1227566AA700601770 /* PersistencetFetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5182AB275516A4001EA530 /* PersistencetFetchable.swift */; }; + 87115F1327566AB200601770 /* FavoriteItemDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A06AEC7274159D10027222D /* FavoriteItemDTO.swift */; }; 87115F172756758800601770 /* GetArrInfoByRouteListUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87115F162756758800601770 /* GetArrInfoByRouteListUseCase.swift */; }; 87115F19275675B900601770 /* GetStationsByRouteListUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87115F18275675B900601770 /* GetStationsByRouteListUseCase.swift */; }; 87115F1B275675E200601770 /* GetBusPosByRtidUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87115F1A275675E200601770 /* GetBusPosByRtidUseCase.swift */; }; @@ -176,6 +215,8 @@ 873D639827303A6800E79069 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873D639727303A6800E79069 /* AppCoordinator.swift */; }; 873D639A27303B0500E79069 /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873D639927303B0500E79069 /* HomeCoordinator.swift */; }; 873D639C27303B5000E79069 /* SearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873D639B27303B5000E79069 /* SearchCoordinator.swift */; }; + 875483782756810400136F16 /* BusRouteAPIUsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0446842E275676B3007E440A /* BusRouteAPIUsable.swift */; }; + 875483792756810D00136F16 /* JsonDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0446843C275679B9007E440A /* JsonDTO.swift */; }; 875F1AB02755BD86003F5BB1 /* AverageSectionTimeCalculatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 875F1AAF2755BD86003F5BB1 /* AverageSectionTimeCalculatable.swift */; }; 875F1AB22755BE08003F5BB1 /* AlarmSettingCalculateUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 875F1AB12755BE08003F5BB1 /* AlarmSettingCalculateUseCase.swift */; }; 87A5556C2728116400A9B5E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A5556B2728116400A9B5E3 /* AppDelegate.swift */; }; @@ -1710,6 +1751,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 87115EFA27564D0F00601770 /* RequestFactory.swift in Sources */, 4AF1E08F2756264600DE51C8 /* RequestFactoryTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1742,7 +1784,34 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 87115F0F27566A8E00601770 /* GetBusPosByRtidFetcher.swift in Sources */, + 875483792756810D00136F16 /* JsonDTO.swift in Sources */, + 87115F1027566A9800601770 /* GetRouteListFetcher.swift in Sources */, + 87115F0B27566A6900601770 /* RequestFactory.swift in Sources */, + 87115F042756694600601770 /* PublisherExtension.swift in Sources */, + 87115F02275668F600601770 /* NotificationNameExtension.swift in Sources */, + 87115F052756696F00601770 /* GetRouteListUsable.swift in Sources */, + 87115F1327566AB200601770 /* FavoriteItemDTO.swift in Sources */, + 87115F00275667A600601770 /* BaseUseCase.swift in Sources */, + 87115F01275668E100601770 /* BusCongestion.swift in Sources */, + 87115F062756698D00601770 /* BBusAPIUseCases.swift in Sources */, + 87115EFF27565DC200601770 /* BusPosByRtidDTO.swift in Sources */, + 87115F0D27566A7800601770 /* ServiceFetchable.swift in Sources */, + 87115F0C27566A7000601770 /* GetStationsByRouteListFetcher.swift in Sources */, + 87115F0827566A5200601770 /* TokenManager.swift in Sources */, + 87115F1227566AA700601770 /* PersistencetFetchable.swift in Sources */, + 87115F032756693700601770 /* BusPosByVehicleIdDTO.swift in Sources */, + 87115EFE27565DAE00601770 /* StationByRouteListDTO.swift in Sources */, + 875483782756810400136F16 /* BusRouteAPIUsable.swift in Sources */, + 87115F0927566A5900601770 /* NetworkService.swift in Sources */, + 87115EFD27565D9D00601770 /* BusRouteDTO.swift in Sources */, + 87115F0727566A4400601770 /* GetStationsByRouteListUsable.swift in Sources */, + 87115EFC27565BAD00601770 /* BusRouteAPIUseCase.swift in Sources */, + 87115EFB27565A3300601770 /* BusRouteViewModel.swift in Sources */, + 87115F0A27566A6200601770 /* PersistenceStorage.swift in Sources */, + 87115F1127566A9F00601770 /* BBusAPIError.swift in Sources */, 4AF1E0C32756269F00DE51C8 /* BusRouteViewModelTests.swift in Sources */, + 87115F0E27566A8000601770 /* GetBusPosByRtidUsable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1758,7 +1827,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8707600227569254005A1E37 /* MovingStatusCalculatable.swift in Sources */, + 8707600827569324005A1E37 /* JsonDTO.swift in Sources */, + 870760092756932E005A1E37 /* PublisherExtension.swift in Sources */, + 8707600327569254005A1E37 /* MovingStatusCalculateUseCase.swift in Sources */, + 8707600127569160005A1E37 /* BusPosByRtidDTO.swift in Sources */, + 8707600027569158005A1E37 /* StationByRouteListDTO.swift in Sources */, + 8707600527569303005A1E37 /* AverageSectionTimeCalculatable.swift in Sources */, + 87075FFF2756914E005A1E37 /* BusRouteDTO.swift in Sources */, + 87075FFE275690E0005A1E37 /* MovingStatusAPIUsable.swift in Sources */, + 8707600727569319005A1E37 /* NotificationNameExtension.swift in Sources */, + 87075FFD27569066005A1E37 /* MovingStatusViewModel.swift in Sources */, + 8707600627569309005A1E37 /* BaseUseCase.swift in Sources */, 4AF1E0DD275626B300DE51C8 /* MovingStatusViewModelTests.swift in Sources */, + 8707600A27569337005A1E37 /* BBusAPIError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2203,7 +2285,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2227,7 +2309,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2395,7 +2477,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2419,7 +2501,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2491,7 +2573,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2515,7 +2597,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = B3PWYBKFUK; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/BBus/BBus/Foreground/MovingStatus/UseCase/MovingStatusCalculateUseCase.swift b/BBus/BBus/Foreground/MovingStatus/UseCase/MovingStatusCalculateUseCase.swift index 3cc40025..898b9f35 100644 --- a/BBus/BBus/Foreground/MovingStatus/UseCase/MovingStatusCalculateUseCase.swift +++ b/BBus/BBus/Foreground/MovingStatus/UseCase/MovingStatusCalculateUseCase.swift @@ -61,7 +61,7 @@ final class MovingStatusCalculateUseCase: MovingStatusCalculatable { let order = Double(order - startOrd) let sect = Double((sect as NSString).floatValue) let fullSect = Double((fullSect as NSString).floatValue) - + print(order, sect, fullSect) return order + (sect/fullSect) } diff --git a/BBus/BBus/Foreground/MovingStatus/ViewModel/MovingStatusViewModel.swift b/BBus/BBus/Foreground/MovingStatus/ViewModel/MovingStatusViewModel.swift index d6d97be4..8df3b081 100644 --- a/BBus/BBus/Foreground/MovingStatus/ViewModel/MovingStatusViewModel.swift +++ b/BBus/BBus/Foreground/MovingStatus/ViewModel/MovingStatusViewModel.swift @@ -87,7 +87,6 @@ final class MovingStatusViewModel { self.bindLoader() self.bindHeaderInfo() self.bindStationsInfo() - self.bindBusesPosInfo() } private func bindHeaderInfo() { @@ -113,6 +112,7 @@ final class MovingStatusViewModel { }) .sink(receiveValue: { [weak self] stations in self?.convertBusStations(with: stations) + self?.bindBusesPosInfo() }) .store(in: &self.cancellables) } @@ -134,11 +134,11 @@ final class MovingStatusViewModel { currentOrd: currentOrd, count: count) - // Test 로직 + /* Test 용 사용자 gps 조작 코드 guard let y = self.buses.first?.gpsY, let x = self.buses.first?.gpsX else { return } - self.findBoardBus(gpsY: y, gpsX: x) + self.findBoardBus(gpsY: y, gpsX: x) */ } .store(in: &self.cancellables) } diff --git a/BBus/BBus/Global/DTO/BusPosByRtidDTO.swift b/BBus/BBus/Global/DTO/BusPosByRtidDTO.swift index 02f498ee..b19c81ff 100644 --- a/BBus/BBus/Global/DTO/BusPosByRtidDTO.swift +++ b/BBus/BBus/Global/DTO/BusPosByRtidDTO.swift @@ -49,4 +49,15 @@ struct BusPosByRtidDTO: Codable { self.gpsY = Double((try? container.decode(String.self, forKey: .gpsY)) ?? "") ?? 0 self.gpsX = Double((try? container.decode(String.self, forKey: .gpsX)) ?? "") ?? 0 } + + init(busType: Int, congestion: Int, plainNumber: String, sectionOrder: Int, fullSectDist: String, sectDist: String, gpsY: Double, gpsX: Double) { + self.busType = busType + self.congestion = congestion + self.plainNumber = plainNumber + self.sectionOrder = sectionOrder + self.fullSectDist = fullSectDist + self.sectDist = sectDist + self.gpsY = gpsY + self.gpsX = gpsX + } } diff --git a/BBus/BBus/Global/DTO/StationByRouteListDTO.swift b/BBus/BBus/Global/DTO/StationByRouteListDTO.swift index 3b45bfe5..09b30194 100644 --- a/BBus/BBus/Global/DTO/StationByRouteListDTO.swift +++ b/BBus/BBus/Global/DTO/StationByRouteListDTO.swift @@ -49,4 +49,15 @@ struct StationByRouteListDTO: Codable { self.lastTm = (try? container.decode(String.self, forKey: .lastTm)) ?? "" self.transYn = (try? container.decode(String.self, forKey: .transYn)) ?? "" } + + init(sectionSpeed: Int, sequence: Int, stationName: String, fullSectionDistance: Int, arsId: String, beginTm: String, lastTm: String, transYn: String) { + self.sectionSpeed = sectionSpeed + self.sequence = sequence + self.stationName = stationName + self.fullSectionDistance = fullSectionDistance + self.arsId = arsId + self.beginTm = beginTm + self.lastTm = lastTm + self.transYn = transYn + } } diff --git a/BBus/BusRouteViewModelTests/BusRouteViewModelTests.swift b/BBus/BusRouteViewModelTests/BusRouteViewModelTests.swift index dde97386..eb96290c 100644 --- a/BBus/BusRouteViewModelTests/BusRouteViewModelTests.swift +++ b/BBus/BusRouteViewModelTests/BusRouteViewModelTests.swift @@ -2,31 +2,194 @@ // BusRouteViewModelTests.swift // BusRouteViewModelTests // -// Created by 김태훈 on 2021/11/30. +// Created by Kang Minsang on 2021/12/01. // import XCTest +import Foundation +import Combine class BusRouteViewModelTests: XCTestCase { + + var busRouteViewModel: BusRouteViewModel? + var cancellables: Set = [] + + class DummyBusRouteAPIUseCase: BusRouteAPIUsable { + func searchHeader(busRouteId: Int) -> AnyPublisher { + let dummyDTO = BusRouteDTO(routeID: 100100260, + busRouteName: "5524", + routeType: .localLine, + startStation: "난향차고지", + endStation: "중앙대학교") + return Just(dummyDTO).setFailureType(to: Error.self).eraseToAnyPublisher() + } + + func fetchRouteList(busRouteId: Int) -> AnyPublisher<[StationByRouteListDTO], Error> { + let dummyStation1 = StationByRouteListDTO(sectionSpeed: 0, + sequence: 1, + stationName: "난곡종점", + fullSectionDistance: 0, + arsId: "21809", + beginTm: "04:00", + lastTm: "22:30", + transYn: "N") + let dummyStation2 = StationByRouteListDTO(sectionSpeed: 44, + sequence: 2, + stationName: "신림복지관앞", + fullSectionDistance: 247, + arsId: "21211", + beginTm: "04:00", + lastTm: "00:18", + transYn: "N") + let dummyStation3 = StationByRouteListDTO(sectionSpeed: 29, + sequence: 3, + stationName: "난우중학교입구", + fullSectionDistance: 190, + arsId: "21210", + beginTm: "04:00", + lastTm: "22:30", + transYn: "N") + return Just([dummyStation1, dummyStation2, dummyStation3]).setFailureType(to: Error.self).eraseToAnyPublisher() + } + + func fetchBusPosList(busRouteId: Int) -> AnyPublisher<[BusPosByRtidDTO], Error> { + let dummyBus1 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5255", + sectionOrder: 22, + fullSectDist: "0.351", + sectDist: "0", + gpsY: 37.4893, + gpsX: 126.927062) + let dummyBus2 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5254", + sectionOrder: 28, + fullSectDist: "0.378", + sectDist: "0.017", + gpsY: 37.486795, + gpsX: 126.947757) + let dummyBus3 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5252", + sectionOrder: 32, + fullSectDist: "0.41", + sectDist: "0.022", + gpsY: 37.48311, + gpsX: 126.954122) + return Just([dummyBus1, dummyBus2, dummyBus3]).setFailureType(to: Error.self).eraseToAnyPublisher() + } + } override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. + super.setUp() + self.busRouteViewModel = BusRouteViewModel(useCase: DummyBusRouteAPIUseCase(), busRouteId: 100100260) } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + self.busRouteViewModel = nil } - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func test_bindHeaderInfo_수신_성공() throws { + // given + guard let viewModel = self.busRouteViewModel else { + XCTFail("viewModel is nil") + return + } + let expectation = XCTestExpectation() + let answerDTO = BusRouteDTO(routeID: 100100260, + busRouteName: "5524", + routeType: .localLine, + startStation: "난향차고지", + endStation: "중앙대학교") + + // when + viewModel.$header + .receive(on: DispatchQueue.global()) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { header in + guard let header = header else { return } + // then + XCTAssertEqual(header.routeID, answerDTO.routeID) + XCTAssertEqual(header.busRouteName, answerDTO.busRouteName) + XCTAssertEqual(header.routeType, answerDTO.routeType) + XCTAssertEqual(header.startStation, answerDTO.startStation) + XCTAssertEqual(header.endStation, answerDTO.endStation) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. + + func test_bindBodysInfo_수신_성공() throws { + // given + guard let viewModel = self.busRouteViewModel else { + XCTFail("viewModel is nil") + return } + let expectation = XCTestExpectation() + let station1 = BusStationInfo(speed: 0, afterSpeed: 44, count: 3, title: "난곡종점", description: "21809 | 04:00-22:30", transYn: "N", arsId: "21809") + let station2 = BusStationInfo(speed: 44, afterSpeed: 29, count: 3, title: "신림복지관앞", description: "21211 | 04:00-00:18", transYn: "N", arsId: "21211") + let station3 = BusStationInfo(speed: 29, afterSpeed: nil, count: 3, title: "난우중학교입구", description: "21210 | 04:00-22:30", transYn: "N", arsId: "21210") + let answerStations = [station1, station2, station3] + + // when + viewModel.$bodys + .receive(on: DispatchQueue.global()) + .filter({ !$0.isEmpty }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { bodys in + // then + XCTAssertEqual(bodys[0].arsId, answerStations[0].arsId) + XCTAssertEqual(bodys[1].description, answerStations[1].description) + XCTAssertEqual(bodys[2].afterSpeed, answerStations[2].afterSpeed) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) + } + + func test_bindBusesPosInfo_수신_성공() throws { + // given + guard let viewModel = self.busRouteViewModel else { + XCTFail("viewModel is nil") + return + } + let expectation = XCTestExpectation() + let bus1 = BusPosInfo(location: CGFloat(21), number: "5255", congestion: .normal, islower: true) + let bus2 = BusPosInfo(location: CGFloat(27) + CGFloat(("0.017" as NSString).floatValue)/CGFloat(("0.378" as NSString).floatValue), number: "5254", congestion: .normal, islower: true) + let bus3 = BusPosInfo(location: CGFloat(31) + CGFloat(0.022)/CGFloat(0.41), number: "5252", congestion: .normal, islower: true) + let answerBuses = [bus1, bus2, bus3] + + // when + viewModel.$buses + .receive(on: DispatchQueue.global()) + .filter({ !$0.isEmpty }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { buses in + // then + XCTAssertEqual(buses[0].number, answerBuses[0].number) + XCTAssertEqual(buses[1].location, answerBuses[1].location) + XCTAssertEqual(buses[2].congestion, answerBuses[2].congestion) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) } - } diff --git a/BBus/MovingStatusViewModelTests/MovingStatusViewModelTests.swift b/BBus/MovingStatusViewModelTests/MovingStatusViewModelTests.swift index 53553e25..554ef15c 100644 --- a/BBus/MovingStatusViewModelTests/MovingStatusViewModelTests.swift +++ b/BBus/MovingStatusViewModelTests/MovingStatusViewModelTests.swift @@ -6,20 +6,259 @@ // import XCTest +import Foundation +import Combine class MovingStatusViewModelTests: XCTestCase { + var cancellables: Set = [] + + class DummyMovingStatusAPIUseCase: MovingStatusAPIUsable { + func searchHeader(busRouteId: Int) -> AnyPublisher { + let dummyDTO = BusRouteDTO(routeID: 100100260, + busRouteName: "5524", + routeType: .localLine, + startStation: "난향차고지", + endStation: "중앙대학교") + return Just(dummyDTO).setFailureType(to: Error.self).eraseToAnyPublisher() + } + + func fetchRouteList(busRouteId: Int) -> AnyPublisher<[StationByRouteListDTO], Error> { + let dummyStation1 = StationByRouteListDTO(sectionSpeed: 0, + sequence: 1, + stationName: "난곡종점", + fullSectionDistance: 0, + arsId: "21809", + beginTm: "04:00", + lastTm: "22:30", + transYn: "N") + let dummyStation2 = StationByRouteListDTO(sectionSpeed: 44, + sequence: 2, + stationName: "신림복지관앞", + fullSectionDistance: 247, + arsId: "21211", + beginTm: "04:00", + lastTm: "00:18", + transYn: "N") + let dummyStation3 = StationByRouteListDTO(sectionSpeed: 29, + sequence: 3, + stationName: "난우중학교입구", + fullSectionDistance: 190, + arsId: "21210", + beginTm: "04:00", + lastTm: "22:30", + transYn: "N") + return Just([dummyStation1, dummyStation2, dummyStation3]).setFailureType(to: Error.self).eraseToAnyPublisher() + } + + func fetchBusPosList(busRouteId: Int) -> AnyPublisher<[BusPosByRtidDTO], Error> { + let dummyBus1 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5255", + sectionOrder: 0, + fullSectDist: "0.351", + sectDist: "0", + gpsY: 37.4893, + gpsX: 126.927062) + let dummyBus2 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5254", + sectionOrder: 2, + fullSectDist: "0.378", + sectDist: "0.017", + gpsY: 37.486795, + gpsX: 126.947757) + let dummyBus3 = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5252", + sectionOrder: 4, + fullSectDist: "0.41", + sectDist: "0.022", + gpsY: 37.48311, + gpsX: 126.954122) + return Just([dummyBus1, dummyBus2, dummyBus3]).setFailureType(to: Error.self).eraseToAnyPublisher() + } + } + override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. + super.setUp() } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() } - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func test_bindHeaderInfo_수신_성공() throws { + // given + let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") + let expectation = XCTestExpectation() + let answerHeader = BusInfo(busName: "5524", type: .localLine) + + // when + viewModel.$busInfo + .receive(on: DispatchQueue.global()) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { header in + guard let header = header else { return } + // then + XCTAssertEqual(header.busName, answerHeader.busName) + XCTAssertEqual(header.type, answerHeader.type) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) + } + + func test_bindStationsInfo_수신_성공() throws { + // given + let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") + let expectation = XCTestExpectation() + let station1 = StationInfo(speed: 44, afterSpeed: 29, count: 2, title: "신림복지관앞", sectTime: 0) + let station2 = StationInfo(speed: 29, afterSpeed: nil, count: 2, title: "난우중학교입구", sectTime: Int(ceil(Double(11.4)/Double(21)))) + let answerStations = [station1, station2] + + // when + viewModel.$stationInfos + .receive(on: DispatchQueue.global()) + .filter({ !$0.isEmpty }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { stations in + // then + XCTAssertEqual(stations[0].title, answerStations[0].title) + XCTAssertEqual(stations[0].speed, answerStations[0].speed) + XCTAssertEqual(stations[1].sectTime, answerStations[1].sectTime) + XCTAssertEqual(stations[1].afterSpeed, answerStations[1].afterSpeed) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) + } + + func test_bindBusesPosInfo_수신_성공() throws { + // given + let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") + let expectation = XCTestExpectation() + let targetBus = BusPosByRtidDTO(busType: 1, + congestion: 0, + plainNumber: "서울74사5254", + sectionOrder: 2, + fullSectDist: "0.378", + sectDist: "0.017", + gpsY: 37.486795, + gpsX: 126.947757) + let answerBuses = [targetBus] + + // when + viewModel.$buses + .receive(on: DispatchQueue.global()) + .filter({ !$0.isEmpty }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { buses in + // then + XCTAssertEqual(buses[0].congestion, answerBuses[0].congestion) + XCTAssertEqual(buses[0].plainNumber, answerBuses[0].plainNumber) + XCTAssertEqual(buses[0].sectionOrder, answerBuses[0].sectionOrder) + XCTAssertEqual(buses[0].gpsX, answerBuses[0].gpsX) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) + } + + func test_updateRemainingStation() throws { + // given + let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") + let expectation = XCTestExpectation() + let answer: Int? = 1 + + // when + viewModel.$remainingTime + .receive(on: DispatchQueue.global()) + .filter({ $0 != nil }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { remainStation in + // then + XCTAssertEqual(remainStation, answer) + expectation.fulfill() + } + .store(in: &self.cancellables) + + wait(for: [expectation], timeout: 10) + } + + func test_updateBoardBus() throws { +// // given +// let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") +// let expectation = XCTestExpectation() +// let targetBus = BoardedBus(location: CGFloat(Double(("0.017" as NSString).floatValue)/Double(("0.378" as NSString).floatValue)), remainStation: 1) +// +// // when +// viewModel.$boardedBus +// .receive(on: DispatchQueue.global()) +// .filter({ $0 != nil }) +// .sink { completion in +// // then +// guard case .failure(let error) = completion else { return } +// XCTFail("\(error.localizedDescription)") +// expectation.fulfill() +// } receiveValue: { boardedBus in +// guard let boardedBus = boardedBus else { return } +// // then +// XCTAssertEqual(boardedBus.remainStation, targetBus.remainStation) +// XCTAssertEqual(boardedBus.location, targetBus.location) +// expectation.fulfill() +// } +// .store(in: &self.cancellables) +// sleep(1) +// viewModel.findBoardBus(gpsY: 37.486795, gpsX: 126.947757) +// +// wait(for: [expectation], timeout: 10) + } + + func test_remainintTime() throws { + // given + let viewModel = MovingStatusViewModel(apiUseCase: DummyMovingStatusAPIUseCase(), calculateUseCase: MovingStatusCalculateUseCase(), busRouteId: 100100260, fromArsId: "21211", toArsId: "21210") + let expectation = XCTestExpectation() + let answer: Int? = 1 + + // when + viewModel.$remainingTime + .receive(on: DispatchQueue.global()) + .filter({ $0 != nil }) + .sink { completion in + // then + guard case .failure(let error) = completion else { return } + XCTFail("\(error.localizedDescription)") + expectation.fulfill() + } receiveValue: { remainingTime in + // then + XCTAssertEqual(remainingTime, answer) + expectation.fulfill() + } + .store(in: &self.cancellables) + sleep(1) + viewModel.findBoardBus(gpsY: 37.486795, gpsX: 126.947757) + + wait(for: [expectation], timeout: 10) } func testPerformanceExample() throws { diff --git a/BBus/RequestFactoryTests/RequestFactoryTests.swift b/BBus/RequestFactoryTests/RequestFactoryTests.swift index 2f522e5a..927949ed 100644 --- a/BBus/RequestFactoryTests/RequestFactoryTests.swift +++ b/BBus/RequestFactoryTests/RequestFactoryTests.swift @@ -9,24 +9,59 @@ import XCTest class RequestFactoryTests: XCTestCase { + private var requestFactory: Requestable? + override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. + super.setUp() + self.requestFactory = RequestFactory() } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + self.requestFactory = nil } - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func test_request_파라미터2개_생성_일치() throws { + // given + let mockUrl = "http://ws.bus.go.kr/testUrl" + let mockAccessKey = "uAtMsUVNMLIM%2FM9%3D%3D" + let mockParam = ["stId": "10001", "busRouteId": "1001001"] + let answer1 = URL(string: "http://ws.bus.go.kr/testUrl?stId=10001&busRouteId=1001001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer2 = URL(string: "http://ws.bus.go.kr/testUrl?busRouteId=1001001&stId=10001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answers = [answer1, answer2] + + // when + guard let requestResult = self.requestFactory?.request(url: mockUrl, accessKey: mockAccessKey, params: mockParam) else { + XCTFail("request result is nil") + return + } + + // then + XCTAssertNotNil(requestResult) + XCTAssertTrue(answers.contains(requestResult.url)) } - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. + func test_request_파라미터3개_생성_일치() throws { + //given + let mockUrl = "http://www.BBus.test" + let mockAccessKey = "uAtMsUVNMLIM%2FM9%3D%3D" + let mockParam = ["stId": "10001", "routeId": "1001001", "ord": "1"] + let answer1 = URL(string: "http://www.BBus.test?stId=10001&routeId=1001001&ord=1&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer2 = URL(string: "http://www.BBus.test?routeId=1001001&stId=10001&ord=1&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer3 = URL(string: "http://www.BBus.test?ord=1&routeId=1001001&stId=10001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer4 = URL(string: "http://www.BBus.test?stId=10001&ord=1&routeId=1001001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer5 = URL(string: "http://www.BBus.test?routeId=1001001&ord=1&stId=10001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answer6 = URL(string: "http://www.BBus.test?ord=1&stId=10001&routeId=1001001&serviceKey=uAtMsUVNMLIM%2FM9%3D%3D") + let answers = [answer1, answer2, answer3, answer4, answer5, answer6] + + // when + guard let requestResult = self.requestFactory?.request(url: mockUrl, accessKey: mockAccessKey, params: mockParam) else { + XCTFail("request result is nil") + return } + + // then + XCTAssertNotNil(requestResult) + XCTAssertTrue(answers.contains(requestResult.url)) } - }