示例#1
0
def test_sort_stack_topological_cycle():
    nodes = {1, 2, 3, 4}
    edges = {
        Edge(child=1, parent=2),
        Edge(child=2, parent=3),
        Edge(child=3, parent=1),
        Edge(child=1, parent=4),
    }

    with pytest.raises(ValueError):
        sort_stack_topological(nodes, edges)
示例#2
0
def test_sort_stack_topological_complex():
    nodes = set('PHID-DREV-{}'.format(i) for i in range(10))
    edges = {
        Edge(child='PHID-DREV-1', parent='PHID-DREV-0'),
        Edge(child='PHID-DREV-2', parent='PHID-DREV-0'),
        Edge(child='PHID-DREV-2', parent='PHID-DREV-3'),
        Edge(child='PHID-DREV-4', parent='PHID-DREV-2'),
        Edge(child='PHID-DREV-5', parent='PHID-DREV-4'),
        Edge(child='PHID-DREV-6', parent='PHID-DREV-1'),
        Edge(child='PHID-DREV-7', parent='PHID-DREV-6'),
        Edge(child='PHID-DREV-7', parent='PHID-DREV-5'),
        Edge(child='PHID-DREV-9', parent='PHID-DREV-7'),
        Edge(child='PHID-DREV-8', parent='PHID-DREV-9'),
    }

    order = sort_stack_topological(nodes,
                                   edges,
                                   key=lambda x: int(x.split('-')[2]))
    assert order == [
        'PHID-DREV-0',
        'PHID-DREV-1',
        'PHID-DREV-3',
        'PHID-DREV-2',
        'PHID-DREV-4',
        'PHID-DREV-5',
        'PHID-DREV-6',
        'PHID-DREV-7',
        'PHID-DREV-9',
        'PHID-DREV-8',
    ]
示例#3
0
def test_sort_stack_topological_linear():
    revs = ["PHID-DREV-{}".format(i) for i in range(10)]
    nodes = {phid for phid in revs}
    edges = {Edge(child=revs[i], parent=revs[i - 1]) for i in range(1, 10)}

    order = sort_stack_topological(nodes, edges)
    assert order == revs
示例#4
0
def test_sort_stack_topological_complex():
    nodes = set("PHID-DREV-{}".format(i) for i in range(10))
    edges = {
        Edge(child="PHID-DREV-1", parent="PHID-DREV-0"),
        Edge(child="PHID-DREV-2", parent="PHID-DREV-0"),
        Edge(child="PHID-DREV-2", parent="PHID-DREV-3"),
        Edge(child="PHID-DREV-4", parent="PHID-DREV-2"),
        Edge(child="PHID-DREV-5", parent="PHID-DREV-4"),
        Edge(child="PHID-DREV-6", parent="PHID-DREV-1"),
        Edge(child="PHID-DREV-7", parent="PHID-DREV-6"),
        Edge(child="PHID-DREV-7", parent="PHID-DREV-5"),
        Edge(child="PHID-DREV-9", parent="PHID-DREV-7"),
        Edge(child="PHID-DREV-8", parent="PHID-DREV-9"),
    }

    order = sort_stack_topological(nodes,
                                   edges,
                                   key=lambda x: int(x.split("-")[2]))
    assert order == [
        "PHID-DREV-0",
        "PHID-DREV-1",
        "PHID-DREV-3",
        "PHID-DREV-2",
        "PHID-DREV-4",
        "PHID-DREV-5",
        "PHID-DREV-6",
        "PHID-DREV-7",
        "PHID-DREV-9",
        "PHID-DREV-8",
    ]
示例#5
0
def test_draw_stack_graph_complex():
    nodes = set('PHID-DREV-{}'.format(i) for i in range(10))
    edges = {
        Edge(child='PHID-DREV-1', parent='PHID-DREV-0'),
        Edge(child='PHID-DREV-2', parent='PHID-DREV-0'),
        Edge(child='PHID-DREV-2', parent='PHID-DREV-3'),
        Edge(child='PHID-DREV-4', parent='PHID-DREV-2'),
        Edge(child='PHID-DREV-5', parent='PHID-DREV-4'),
        Edge(child='PHID-DREV-6', parent='PHID-DREV-1'),
        Edge(child='PHID-DREV-7', parent='PHID-DREV-6'),
        Edge(child='PHID-DREV-7', parent='PHID-DREV-5'),
        Edge(child='PHID-DREV-9', parent='PHID-DREV-7'),
        Edge(child='PHID-DREV-8', parent='PHID-DREV-9'),
    }
    order = sort_stack_topological(nodes,
                                   edges,
                                   key=lambda x: int(x.split('-')[2]))

    width, rows = draw_stack_graph(nodes, edges, order)
    assert width == 3
    assert rows == [{
        'above': [0, 1],
        'below': [],
        'node': 'PHID-DREV-0',
        'other': [],
        'pos': 0
    }, {
        'above': [0],
        'below': [0],
        'node': 'PHID-DREV-1',
        'other': [1],
        'pos': 0
    }, {
        'above': [2],
        'below': [],
        'node': 'PHID-DREV-3',
        'other': [0, 1],
        'pos': 2
    }, {
        'above': [1],
        'below': [1, 2],
        'node': 'PHID-DREV-2',
        'other': [0],
        'pos': 1
    }, {
        'above': [1],
        'below': [1],
        'node': 'PHID-DREV-4',
        'other': [0],
        'pos': 1
    }, {
        'above': [1],
        'below': [1],
        'node': 'PHID-DREV-5',
        'other': [0],
        'pos': 1
    }, {
        'above': [0],
        'below': [0],
        'node': 'PHID-DREV-6',
        'other': [1],
        'pos': 0
    }, {
        'above': [0],
        'below': [0, 1],
        'node': 'PHID-DREV-7',
        'other': [],
        'pos': 0
    }, {
        'above': [0],
        'below': [0],
        'node': 'PHID-DREV-9',
        'other': [],
        'pos': 0
    }, {
        'above': [],
        'below': [0],
        'node': 'PHID-DREV-8',
        'other': [],
        'pos': 0
    }]
示例#6
0
def test_sort_stack_topological_favors_minimum():
    nodes = set(range(10))
    edges = {Edge(child=0, parent=i) for i in range(1, 10)}

    order = sort_stack_topological(nodes, edges)
    assert order == list(range(1, 10)) + [0]
示例#7
0
def test_sort_stack_topological_single_node():
    order = sort_stack_topological({"PHID-DREV-0"}, set())
    assert len(order) == 1
    assert order[0] == "PHID-DREV-0"
示例#8
0
def test_draw_stack_graph_complex():
    nodes = set("PHID-DREV-{}".format(i) for i in range(10))
    edges = {
        Edge(child="PHID-DREV-1", parent="PHID-DREV-0"),
        Edge(child="PHID-DREV-2", parent="PHID-DREV-0"),
        Edge(child="PHID-DREV-2", parent="PHID-DREV-3"),
        Edge(child="PHID-DREV-4", parent="PHID-DREV-2"),
        Edge(child="PHID-DREV-5", parent="PHID-DREV-4"),
        Edge(child="PHID-DREV-6", parent="PHID-DREV-1"),
        Edge(child="PHID-DREV-7", parent="PHID-DREV-6"),
        Edge(child="PHID-DREV-7", parent="PHID-DREV-5"),
        Edge(child="PHID-DREV-9", parent="PHID-DREV-7"),
        Edge(child="PHID-DREV-8", parent="PHID-DREV-9"),
    }
    order = sort_stack_topological(nodes,
                                   edges,
                                   key=lambda x: int(x.split("-")[2]))

    width, rows = draw_stack_graph(nodes, edges, order)
    assert width == 3
    assert rows == [
        {
            "above": [0, 1],
            "below": [],
            "node": "PHID-DREV-0",
            "other": [],
            "pos": 0
        },
        {
            "above": [0],
            "below": [0],
            "node": "PHID-DREV-1",
            "other": [1],
            "pos": 0
        },
        {
            "above": [2],
            "below": [],
            "node": "PHID-DREV-3",
            "other": [0, 1],
            "pos": 2
        },
        {
            "above": [1],
            "below": [1, 2],
            "node": "PHID-DREV-2",
            "other": [0],
            "pos": 1
        },
        {
            "above": [1],
            "below": [1],
            "node": "PHID-DREV-4",
            "other": [0],
            "pos": 1
        },
        {
            "above": [1],
            "below": [1],
            "node": "PHID-DREV-5",
            "other": [0],
            "pos": 1
        },
        {
            "above": [0],
            "below": [0],
            "node": "PHID-DREV-6",
            "other": [1],
            "pos": 0
        },
        {
            "above": [0],
            "below": [0, 1],
            "node": "PHID-DREV-7",
            "other": [],
            "pos": 0
        },
        {
            "above": [0],
            "below": [0],
            "node": "PHID-DREV-9",
            "other": [],
            "pos": 0
        },
        {
            "above": [],
            "below": [0],
            "node": "PHID-DREV-8",
            "other": [],
            "pos": 0
        },
    ]
示例#9
0
def revision(revision_id):
    api = LandoAPI(
        current_app.config["LANDO_API_URL"],
        auth0_access_token=session.get("access_token"),
        phabricator_api_token=get_phabricator_api_token(),
    )

    form = TransplantRequestForm()
    sec_approval_form = SecApprovalRequestForm()

    errors = []
    if form.is_submitted():
        if not is_user_authenticated():
            errors.append("You must be logged in to request a landing")

        elif not form.validate():
            for _, field_errors in form.errors.items():
                errors.extend(field_errors)

        else:
            try:
                api.request(
                    "POST",
                    "transplants",
                    require_auth0=True,
                    json={
                        "landing_path": json.loads(form.landing_path.data),
                        "confirmation_token": form.confirmation_token.data,
                    },
                )
                # We don't actually need any of the data from the
                # the submission. As long as an exception wasn't
                # raised we're successful.
                return redirect(url_for("revisions.revision", revision_id=revision_id))
            except LandoAPIError as e:
                if not e.detail:
                    raise

                errors.append(e.detail)

    # Request the entire stack.
    try:
        stack = api.request("GET", "stacks/D{}".format(revision_id))
    except LandoAPIError as e:
        if e.status_code == 404:
            raise RevisionNotFound(revision_id)
        else:
            raise

    # Build a mapping from phid to revision and identify
    # the data for the revision used to load this page.
    revision = None
    revisions = {}
    for r in stack["revisions"]:
        revisions[r["phid"]] = r
        if r["id"] == "D{}".format(revision_id):
            revision = r["phid"]

    # Build a mapping from phid to repository.
    repositories = {}
    for r in stack["repositories"]:
        repositories[r["phid"]] = r

    # Request all previous transplants for the stack.
    transplants = api.request(
        "GET", "transplants", params={"stack_revision_id": "D{}".format(revision_id)}
    )

    # The revision may appear in many `landable_paths`` if it has
    # multiple children, or any of its landable descendents have
    # multiple children. That being said, there should only be a
    # single unique path up to this revision, so find the first
    # it appears in. The revisions up to the target one in this
    # path form the landable series.
    #
    # We also form a set of all the revisions that are landable
    # so we can present selection for what to land.
    series = None
    landable = set()
    for p in stack["landable_paths"]:
        for phid in p:
            landable.add(phid)

        try:
            series = p[: p.index(revision) + 1]
        except ValueError:
            pass

    dryrun = None
    target_repo = None
    if series and is_user_authenticated():
        landing_path = [
            {
                "revision_id": revisions[phid]["id"],
                "diff_id": revisions[phid]["diff"]["id"],
            }
            for phid in series
        ]
        form.landing_path.data = json.dumps(landing_path)

        dryrun = api.request(
            "POST",
            "transplants/dryrun",
            require_auth0=True,
            json={"landing_path": landing_path},
        )
        form.confirmation_token.data = dryrun.get("confirmation_token")

        series = list(reversed(series))
        target_repo = repositories.get(revisions[series[0]]["repo_phid"])

    phids = set(revisions.keys())
    edges = set(Edge(child=e[0], parent=e[1]) for e in stack["edges"])
    order = sort_stack_topological(
        phids, edges, key=lambda x: int(revisions[x]["id"][1:])
    )
    drawing_width, drawing_rows = draw_stack_graph(phids, edges, order)

    annotate_sec_approval_workflow_info(revisions)

    # Are we showing the "sec-approval request submitted" dialog?
    # If we are then fill in its values.
    submitted_revision = request.args.get("show_approval_success")
    submitted_rev_url = None
    if submitted_revision:
        for rev in revisions.values():
            if rev["id"] == submitted_revision:
                submitted_rev_url = rev["url"]
                break

    return render_template(
        "stack/stack.html",
        revision_id="D{}".format(revision_id),
        series=series,
        landable=landable,
        dryrun=dryrun,
        stack=stack,
        rows=list(zip(reversed(order), reversed(drawing_rows))),
        drawing_width=drawing_width,
        transplants=transplants,
        revisions=revisions,
        revision_phid=revision,
        sec_approval_form=sec_approval_form,
        submitted_rev_url=submitted_rev_url,
        target_repo=target_repo,
        errors=errors,
        form=form,
    )
def revision(revision_id):
    api = LandoAPI(current_app.config['LANDO_API_URL'],
                   auth0_access_token=session.get('access_token'),
                   phabricator_api_token=get_phabricator_api_token())

    form = TransplantRequestForm()
    errors = []
    if form.is_submitted():
        if not is_user_authenticated():
            errors.append('You must be logged in to request a landing')

        elif not form.validate():
            for _, field_errors in form.errors.items():
                errors.extend(field_errors)

        else:
            try:
                api.request('POST',
                            'transplants',
                            require_auth0=True,
                            json={
                                'landing_path':
                                json.loads(form.landing_path.data),
                                'confirmation_token':
                                form.confirmation_token.data,
                            })
                # We don't actually need any of the data from the
                # the submission. As long as an exception wasn't
                # raised we're successful.
                return redirect(
                    url_for('revisions.revision', revision_id=revision_id))
            except LandoAPIError as e:
                if not e.detail:
                    raise

                errors.append(e.detail)

    # Request the entire stack.
    try:
        stack = api.request('GET', 'stacks/D{}'.format(revision_id))
    except LandoAPIError as e:
        if e.status_code == 404:
            raise RevisionNotFound(revision_id)
        else:
            raise

    # Build a mapping from phid to revision and identify
    # the data for the revision used to load this page.
    revision = None
    revisions = {}
    for r in stack['revisions']:
        revisions[r['phid']] = r
        if r['id'] == 'D{}'.format(revision_id):
            revision = r['phid']

    # Build a mapping from phid to repository.
    repositories = {}
    for r in stack['repositories']:
        repositories[r['phid']] = r

    # Request all previous transplants for the stack.
    transplants = api.request(
        'GET',
        'transplants',
        params={'stack_revision_id': 'D{}'.format(revision_id)})

    # The revision may appear in many `landable_paths`` if it has
    # multiple children, or any of its landable descendents have
    # multiple children. That being said, there should only be a
    # single unique path up to this revision, so find the first
    # it appears in. The revisions up to the target one in this
    # path form the landable series.
    #
    # We also form a set of all the revisions that are landable
    # so we can present selection for what to land.
    series = None
    landable = set()
    for p in stack['landable_paths']:
        for phid in p:
            landable.add(phid)

        try:
            series = p[:p.index(revision) + 1]
        except ValueError:
            pass

    dryrun = None
    target_repo = None
    if series and is_user_authenticated():
        landing_path = [{
            'revision_id': revisions[phid]['id'],
            'diff_id': revisions[phid]['diff']['id'],
        } for phid in series]
        form.landing_path.data = json.dumps(landing_path)

        dryrun = api.request('POST',
                             'transplants/dryrun',
                             require_auth0=True,
                             json={'landing_path': landing_path})
        form.confirmation_token.data = dryrun.get('confirmation_token')

        series = list(reversed(series))
        target_repo = repositories.get(revisions[series[0]]['repo_phid'])

    phids = set(revisions.keys())
    edges = set(Edge(child=e[0], parent=e[1]) for e in stack['edges'])
    order = sort_stack_topological(phids,
                                   edges,
                                   key=lambda x: int(revisions[x]['id'][1:]))
    drawing_width, drawing_rows = draw_stack_graph(phids, edges, order)

    return render_template(
        'stack/stack.html',
        revision_id='D{}'.format(revision_id),
        series=series,
        landable=landable,
        dryrun=dryrun,
        stack=stack,
        rows=list(zip(reversed(order), reversed(drawing_rows))),
        drawing_width=drawing_width,
        transplants=transplants,
        revisions=revisions,
        revision_phid=revision,
        target_repo=target_repo,
        errors=errors,
        form=form,
    )