Skip to content

Commit

Permalink
Merge branch 'master' into tarun/add-tags-for-samples
Browse files Browse the repository at this point in the history
  • Loading branch information
Tarun-Arora authored Aug 29, 2023
2 parents 9529690 + 46fc52b commit c8fa645
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 16 deletions.
11 changes: 11 additions & 0 deletions install/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ sudo python bootstrap_gunicorn.py
4. In case of any gunicorn error try manually running `/etc/init.d/platform start` command and recheck the platform status.
### Setting Up The Bucket
After the completion of the automated installation of the platform, the following folder structure is created in the 'SAMPLE_REPOSITORY' set during installation:
- `LogFiles/` - Directory containing log files of the tests completed
- `QueuedFiles/` - Directory containing files related to queued samples
Expand All @@ -198,6 +199,16 @@ The `serve_file_download` function in the `utility.py` file implements the gener
For more information about Signed URLs, you can refer to the [official documentation](https://cloud.google.com/storage/docs/access-control/signed-urls).
## Setting up cron job to run tests
Now the server being running, new tests would be queued and therefore a cron job is to be setup to run those tests.
The file `mod_ci/cron.py` is to be run in periodic intervals. To setup a cron job follow the steps below:
1. Open your terminal and enter the command `sudo crontab -e`.
2. To setup a cron job that runs this file every 10 minutes, append this at the bottom of the file
```
*/10 * * * * python /var/www/sample-platform/mod_ci/cron.py > /var/www/sample-platform/logs/cron.log 2>&1
```
Change the `/var/www/sample-plaform` directory, if you have installed the platform in a different directory.
## File upload size for HTTP
Expand Down
34 changes: 34 additions & 0 deletions migrations/versions/b3ed927671bd_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Add last_passed_on and description fields in regression_test
Revision ID: b3ed927671bd
Revises: a5183973c3e9
Create Date: 2023-08-17 00:41:01.237549
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import mysql

# revision identifiers, used by Alembic.
revision = 'b3ed927671bd'
down_revision = 'a5183973c3e9'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('regression_test', schema=None) as batch_op:
batch_op.add_column(sa.Column('last_passed_on', sa.Integer(), nullable=True))
batch_op.create_foreign_key('regression_test_ibfk_2', 'test', ['last_passed_on'], ['id'], onupdate='CASCADE', ondelete='SET NULL')
batch_op.add_column(sa.Column('description', sa.String(length=1024), nullable=True))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('regression_test', schema=None) as batch_op:
batch_op.drop_constraint('regression_test_ibfk_2', type_='foreignkey')
batch_op.drop_column('last_passed_on')
batch_op.drop_column('description')
# ### end Alembic commands ###
43 changes: 34 additions & 9 deletions mod_ci/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from markdown2 import markdown
from pymysql.err import IntegrityError
from sqlalchemy import and_, func, or_
from sqlalchemy.orm.query import Query
from sqlalchemy.sql import label
from sqlalchemy.sql.functions import count
from werkzeug.utils import secure_filename
Expand Down Expand Up @@ -421,6 +422,8 @@ def start_test(compute, app, db, repository: Repository.Repository, test, bot_to
if 'error' not in result:
db.add(status)
db.commit()
else:
log.error(f"Error creating test instance for test {test.id}, result: {result}")


def create_instance(compute, project, zone, test, reportURL) -> Dict:
Expand Down Expand Up @@ -897,12 +900,12 @@ def start_ci():
# If it's a valid PR, run the tests
pr_nr = payload['pull_request']['number']

draft = payload['pull_request']['draft']
is_draft = payload['pull_request']['draft']
action = payload['action']
active = action in ['opened', 'synchronize', 'reopened', 'ready_for_review']
inactive = action in ['closed', 'converted_to_draft']
is_active = action in ['opened', 'synchronize', 'reopened', 'ready_for_review']
is_inactive = action in ['closed', 'converted_to_draft']

if not draft and active:
if not is_draft and is_active:
try:
commit_hash = payload['pull_request']['head']['sha']
except KeyError:
Expand All @@ -918,7 +921,7 @@ def start_ci():
if repository.get_pull(number=pr_nr).mergeable is not False:
add_test_entry(g.db, commit_hash, TestType.pull_request, pr_nr=pr_nr)

elif inactive:
elif is_inactive:
pr_action = 'closed' if action == 'closed' else 'converted to draft'
g.log.debug(f'PR was {pr_action}, no after hash available')

Expand Down Expand Up @@ -1028,14 +1031,17 @@ def start_ci():
actor=payload['sender']['login'],
branch=payload['workflow_run']['head_branch']
):
workflow_run_name = workflow[workflow_run.workflow_id]
if workflow_run_name not in [Workflow_builds.LINUX, Workflow_builds.WINDOWS]:
continue
if workflow_run.head_sha == commit_hash:
if workflow_run.status == "completed":
if workflow_run.conclusion != "success":
has_failed = True
break
if workflow[workflow_run.workflow_id] == Workflow_builds.LINUX:
if workflow_run_name == Workflow_builds.LINUX:
builds["linux"] = True
elif workflow[workflow_run.workflow_id] == Workflow_builds.WINDOWS:
elif workflow_run_name == Workflow_builds.WINDOWS:
builds["windows"] = True
else:
is_complete = False
Expand Down Expand Up @@ -1130,6 +1136,12 @@ def update_build_badge(status, test) -> None:
shutil.copyfile(original_location, build_status_location)
g.log.info('Build badge updated successfully!')

regression_testid_passed = get_query_regression_testid_passed(test.id)
test_ids_to_update = [result[0] for result in regression_testid_passed]
g.db.query(RegressionTest).filter(RegressionTest.id.in_(test_ids_to_update)
).update({"last_passed_on": test.id}, synchronize_session=False)
g.db.commit()


@mod_ci.route('/progress-reporter/<test_id>/<token>', methods=['POST'])
def progress_reporter(test_id, token):
Expand Down Expand Up @@ -1541,8 +1553,14 @@ def set_avg_time(platform, process_type: str, time_taken: int) -> None:
g.db.commit()


def get_info_for_pr_comment(test_id: int) -> PrCommentInfo:
"""Return info about the given test id for use in a PR comment."""
def get_query_regression_testid_passed(test_id: int) -> Query:
"""Get sqlalchemy query to fetch all regression tests which passed on given test id.
:param test_id: test id of the test whose query is required
:type test_id: int
:return: Query object for passed regression tests
:rtype: sqlalchemy.orm.query.Query
"""
regression_testid_passed = g.db.query(TestResult.regression_test_id).outerjoin(
TestResultFile, TestResult.test_id == TestResultFile.test_id).filter(
TestResult.test_id == test_id,
Expand All @@ -1567,6 +1585,13 @@ def get_info_for_pr_comment(test_id: int) -> PrCommentInfo:
)))
)).distinct().union(g.db.query(regression_testid_passed.c.regression_test_id))

return regression_testid_passed


def get_info_for_pr_comment(test_id: int) -> PrCommentInfo:
"""Return info about the given test id for use in a PR comment."""
regression_testid_passed = get_query_regression_testid_passed(test_id)

passed = g.db.query(label('category_id', Category.id), label(
'success', count(regressionTestLinkTable.c.regression_id))).filter(
regressionTestLinkTable.c.regression_id.in_(regression_testid_passed),
Expand Down
5 changes: 4 additions & 1 deletion mod_regression/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def test_edit(regression_id):
test.expected_rc = form.expected_rc.data
test.input_type = InputType.from_string(form.input_type.data)
test.output_type = OutputType.from_string(form.output_type.data)
test.description = form.description.data

g.db.commit()
g.log.info(f'regression test with id: {regression_id} updated!')
Expand All @@ -172,6 +173,7 @@ def test_edit(regression_id):
form.expected_rc.data = test.expected_rc
form.input_type.data = test.input_type.value
form.output_type.data = test.output_type.value
form.description.data = test.description

return {'form': form, 'regression_id': regression_id}

Expand Down Expand Up @@ -241,7 +243,8 @@ def test_add():
category_id=form.category_id.data,
expected_rc=form.expected_rc.data,
input_type=InputType.from_string(form.input_type.data),
output_type=OutputType.from_string(form.output_type.data)
output_type=OutputType.from_string(form.output_type.data),
description=form.description.data,
)
g.db.add(new_test)
category = Category.query.filter(Category.id == form.category_id.data).first()
Expand Down
5 changes: 3 additions & 2 deletions mod_regression/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from flask_wtf import FlaskForm
from wtforms import (HiddenField, IntegerField, SelectField, StringField,
SubmitField)
from wtforms.validators import DataRequired, InputRequired
SubmitField, TextAreaField)
from wtforms.validators import DataRequired, InputRequired, Length

from mod_regression.models import InputType, OutputType

Expand All @@ -21,6 +21,7 @@ class CommonTestForm(FlaskForm):

sample_id = SelectField("Sample", coerce=int)
command = StringField("Command")
description = TextAreaField("Description", validators=[Length(max=1024)])
input_type = SelectField(
"Input Type",
[DataRequired(message="Input Type is not selected")],
Expand Down
6 changes: 5 additions & 1 deletion mod_regression/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ class RegressionTest(Base):
output_files = relationship('RegressionTestOutput', back_populates='regression_test')
expected_rc = Column(Integer)
active = Column(Boolean(), default=True)
last_passed_on = Column(Integer, ForeignKey('test.id', onupdate="CASCADE", ondelete="SET NULL"))
description = Column(String(length=1024))

def __init__(self, sample_id, command, input_type, output_type, category_id, expected_rc, active=True) -> None:
def __init__(self, sample_id, command, input_type, output_type, category_id, expected_rc,
active=True, description="") -> None:
"""
Parametrized constructor for the RegressionTest model.
Expand All @@ -122,6 +125,7 @@ def __init__(self, sample_id, command, input_type, output_type, category_id, exp
self.category_id = category_id
self.expected_rc = expected_rc
self.active = active
self.description = description

def __repr__(self) -> str:
"""
Expand Down
2 changes: 1 addition & 1 deletion run.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
storage_client_bucket = storage_client.bucket(app.config.get('GCS_BUCKET_NAME', ''))

# Save build commit
repo = git.Repo(search_parent_directories=True)
repo = git.Repo(app.config.get('INSTALL_FOLDER', ''))
app.config['BUILD_COMMIT'] = repo.head.object.hexsha


Expand Down
2 changes: 1 addition & 1 deletion templates/ci/pr_comment.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ It seems that not all tests were passed completely. This is an indication that t
Your PR breaks these cases:
<ul>
{% for test in failed_tests %}
<li> ccextractor {{ test.command }} <a href="{{ url_for('sample.sample_by_id', sample_id=test.sample.id, _external=True) }}">{{ test.sample.sha[:10] }}...</a> </li>
<li> ccextractor {{ test.command }} <a href="{{ url_for('sample.sample_by_id', sample_id=test.sample.id, _external=True) }}">{{ test.sample.sha[:10] }}...</a>, Last passed: {% if test.last_passed_on %}<a href="{{ url_for('test.by_id', test_id=test.last_passed_on, _external=True) }}">Test {{ test.last_passed_on }}</a>{% else %}<span>Never</span>{% endif %}</li>
{% endfor %}
</ul>
{% else %}
Expand Down
3 changes: 3 additions & 0 deletions templates/regression/test_add.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ <h5>Regression Test Add</h5>
<div class="medium-12 columns">
{{ macros.render_field(form.sample_id) }}
</div>
<div class="medium-12 columns">
{{ macros.render_field(form.description) }}
</div>
<div class="medium-12 columns">
{{ macros.render_field(form.command) }}
</div>
Expand Down
3 changes: 3 additions & 0 deletions templates/regression/test_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ <h1>Regression Test Edit</h1>
<div class="medium-12 columns">
{{ macros.render_field(form.command) }}
</div>
<div class="medium-12 columns">
{{ macros.render_field(form.description) }}
</div>
<div class="medium-12 columns">
{{ macros.render_field(form.input_type) }}
</div>
Expand Down
1 change: 1 addition & 0 deletions templates/regression/test_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ <h1>Regression test {{ test.id }}</h1>
<p>Command: {{ test.command }}</p>
<p>Input type: {{ test.input_type.description }}</p>
<p>Output type: {{ test.output_type.description }}</p>
<p>Description: {{ test.description or "No description" }}</p>
<p>Output files:</p>
<ul>
{% for i in range(test.output_files|length) %}
Expand Down
4 changes: 3 additions & 1 deletion tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def generate_signed_url(**kwargs):

def load_config(file):
"""Load start config."""
from utility import ROOT_DIR
key_paths = generate_keys()
with open(key_paths['secret_key_path'], 'rb') as secret_key_file:
secret_key = secret_key_file.read()
Expand All @@ -116,7 +117,8 @@ def load_config(file):
'CSRF_SESSION_KEY': secret_csrf,
'ZONE': "test_zone",
'PROJECT_NAME': "test_zone",
'GCS_SIGNED_URL_EXPIRY_LIMIT': 720
'GCS_SIGNED_URL_EXPIRY_LIMIT': 720,
'INSTALL_FOLDER': ROOT_DIR,
}


Expand Down
11 changes: 11 additions & 0 deletions tests/test_ci/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,18 @@ def extractall(*args, **kwargs):
customized_test = CustomizedTest(1, 1)
g.db.add(customized_test)
g.db.commit()

# Test when gcp create instance fails
mock_wait_for_operation.return_value = 'error occurred'
start_test(mock.ANY, self.app, mock_g.db, repository, test, mock.ANY)
mock_g.db.commit.assert_not_called()
mock_create_instance.reset_mock()
mock_wait_for_operation.reset_mock()

# Test when gcp create instance is successful
mock_wait_for_operation.return_value = 'success'
start_test(mock.ANY, self.app, mock_g.db, repository, test, mock.ANY)
mock_g.db.commit.assert_called_once()
mock_create_instance.assert_called_once()
mock_wait_for_operation.assert_called_once()

Expand Down
10 changes: 10 additions & 0 deletions tests/test_regression/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,16 @@ def test_category_delete(self):
))
self.assertEqual(response.status_code, 302)

def test_edit_test_get_request(self):
"""Test editing of regression test with a GET request."""
self.create_user_with_role(self.user.name, self.user.email, self.user.password, Role.admin)

with self.app.test_client() as c:
c.post('/account/login', data=self.create_login_form_data(self.user.email, self.user.password))
response = c.get('/regression/test/2/edit')
self.assertEqual(response.status_code, 200)
self.assertIn('Editing regression test with id 2', str(response.data))

def test_edit_test(self):
"""Check it will edit a regression test."""
self.create_user_with_role(self.user.name, self.user.email, self.user.password, Role.admin)
Expand Down

0 comments on commit c8fa645

Please sign in to comment.