66
77import hglib
88import pytest
9+ import responses
910
1011from conftest import MockBuild
1112from libmozevent .bus import MessageBus
@@ -647,6 +648,7 @@ async def test_crash_utf8_author(PhabricatorMock, mock_mc):
647648 assert details ["revision" ] == mock_mc .repo .tip ().node .decode ("utf-8" )
648649
649650
651+ @responses .activate
650652@pytest .mark .asyncio
651653async def test_unexpected_push_failure (PhabricatorMock , mock_mc ):
652654 """
@@ -669,6 +671,14 @@ async def test_unexpected_push_failure(PhabricatorMock, mock_mc):
669671 from libmozevent import mercurial
670672
671673 mercurial .MAX_PUSH_RETRIES = 1
674+ mercurial .TRY_STATUS_URL = "http://test.status/try"
675+ mercurial .PUSH_RETRY_EXPONENTIAL_DELAY = 0
676+ mercurial .TRY_STATUS_DELAY = 0
677+ mercurial .TRY_STATUS_MAX_WAIT = 0
678+
679+ responses .get (
680+ "http://test.status/try" , status = 200 , json = {"result" : {"status" : "open" }}
681+ )
672682
673683 repository_mock = MagicMock (spec = Repository )
674684 repository_mock .push_to_try .side_effect = [
@@ -712,8 +722,12 @@ async def test_unexpected_push_failure(PhabricatorMock, mock_mc):
712722 tip .node .decode ("utf-8" )
713723 )
714724 assert details ["revision" ] == tip .node .decode ("utf-8" )
725+ assert [(call .request .method , call .request .url ) for call in responses .calls ] == [
726+ ("GET" , "http://test.status/try" )
727+ ]
715728
716729
730+ @responses .activate
717731@pytest .mark .asyncio
718732async def test_push_failure_max_retries (PhabricatorMock , mock_mc ):
719733 """
@@ -736,6 +750,23 @@ async def test_push_failure_max_retries(PhabricatorMock, mock_mc):
736750 from libmozevent import mercurial
737751
738752 mercurial .MAX_PUSH_RETRIES = 2
753+ mercurial .TRY_STATUS_URL = "http://test.status/try"
754+ mercurial .PUSH_RETRY_EXPONENTIAL_DELAY = 2
755+ mercurial .TRY_STATUS_DELAY = 0
756+ mercurial .TRY_STATUS_MAX_WAIT = 0
757+
758+ sleep_history = []
759+
760+ class AsyncioMock (object ):
761+ async def sleep (self , value ):
762+ nonlocal sleep_history
763+ sleep_history .append (value )
764+
765+ mercurial .asyncio = AsyncioMock ()
766+
767+ responses .get (
768+ "http://test.status/try" , status = 200 , json = {"result" : {"status" : "open" }}
769+ )
739770
740771 repository_mock = MagicMock (spec = Repository )
741772 repository_mock .push_to_try .side_effect = hglib .error .CommandError (
@@ -767,3 +798,104 @@ async def test_push_failure_max_retries(PhabricatorMock, mock_mc):
767798 details ["message" ]
768799 == "Max number of retries has been reached pushing the build to try repository"
769800 )
801+
802+ assert [(call .request .method , call .request .url ) for call in responses .calls ] == [
803+ ("GET" , "http://test.status/try" ),
804+ ("GET" , "http://test.status/try" ),
805+ ("GET" , "http://test.status/try" ),
806+ ]
807+ assert sleep_history == [2 , 4 , 8 ]
808+
809+
810+ @responses .activate
811+ @pytest .mark .asyncio
812+ async def test_push_closed_try (PhabricatorMock , mock_mc ):
813+ """
814+ Detect when try tree is in a closed state and wait before it is opened to retry
815+ """
816+
817+ diff = {
818+ "revisionPHID" : "PHID-DREV-badutf8" ,
819+ "baseRevision" : "missing" ,
820+ "phid" : "PHID-DIFF-badutf8" ,
821+ "id" : 555 ,
822+ }
823+ build = MockBuild (4444 , "PHID-REPO-mc" , 5555 , "PHID-build-badutf8" , diff )
824+ with PhabricatorMock as phab :
825+ phab .load_patches_stack (build )
826+
827+ bus = MessageBus ()
828+ bus .add_queue ("phabricator" )
829+
830+ from libmozevent import mercurial
831+
832+ mercurial .MAX_PUSH_RETRIES = 2
833+ mercurial .TRY_STATUS_URL = "http://test.status/try"
834+ mercurial .PUSH_RETRY_EXPONENTIAL_DELAY = 2
835+ mercurial .TRY_STATUS_DELAY = 42
836+ mercurial .TRY_STATUS_MAX_WAIT = 1
837+
838+ sleep_history = []
839+
840+ class AsyncioMock (object ):
841+ async def sleep (self , value ):
842+ nonlocal sleep_history
843+ sleep_history .append (value )
844+
845+ mercurial .asyncio = AsyncioMock ()
846+
847+ responses .get (
848+ "http://test.status/try" , status = 200 , json = {"result" : {"status" : "closed" }}
849+ )
850+ responses .get ("http://test.status/try" , status = 500 )
851+ responses .get (
852+ "http://test.status/try" , status = 200 , json = {"result" : {"status" : "open" }}
853+ )
854+
855+ repository_mock = MagicMock (spec = Repository )
856+ repository_mock .push_to_try .side_effect = [
857+ hglib .error .CommandError (
858+ args = ("push" , "try_url" ),
859+ ret = 1 ,
860+ err = "abort: push failed on remote" ,
861+ out = "" ,
862+ ),
863+ mock_mc .repo .tip (),
864+ ]
865+ repository_mock .try_name = "try"
866+ repository_mock .retries = 0
867+
868+ worker = MercurialWorker (
869+ "mercurial" , "phabricator" , repositories = {"PHID-REPO-mc" : repository_mock }
870+ )
871+ worker .register (bus )
872+
873+ assert bus .queues ["mercurial" ].qsize () == 0
874+
875+ resp = await worker .handle_build (repository_mock , build )
876+ assert resp is None
877+ assert repository_mock .push_to_try .call_count == 1
878+
879+ assert bus .queues ["mercurial" ].qsize () == 1
880+ assert bus .queues ["phabricator" ].qsize () == 0
881+
882+ build = await bus .receive ("mercurial" )
883+ resp = await worker .handle_build (repository_mock , build )
884+ assert resp is not None
885+ mode , out_build , details = resp
886+
887+ assert mode == "success"
888+ assert out_build == build
889+ tip = mock_mc .repo .tip ()
890+ assert details [
891+ "treeherder_url"
892+ ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}" .format (
893+ tip .node .decode ("utf-8" )
894+ )
895+ assert details ["revision" ] == tip .node .decode ("utf-8" )
896+ assert [(call .request .method , call .request .url ) for call in responses .calls ] == [
897+ ("GET" , "http://test.status/try" ),
898+ ("GET" , "http://test.status/try" ),
899+ ("GET" , "http://test.status/try" ),
900+ ]
901+ assert sleep_history == [42 , 42 , 2 ]
0 commit comments