Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

ENH: Add minimal classdef support for MOcovMFile #9

Merged
merged 6 commits into from Dec 24, 2016

Conversation

Projects
None yet
2 participants

Avoid prepending mocov_line_covered to:

  • classdef;
  • properties and methods section opening statements;
  • inside the entire properties section, since a limited subset of the M-file syntax is allowed.

This change tries to solve issue #8 .

Anderson Bravalheri ENH: Add minimal classdef support for MOcovMFile
Avoid prepending `mocov_line_covered` to:

- `classdef`;
- `properties` and `methods` section opening statements;
- inside the entire `properties` section, since a limited subset of the
  M-file syntax is allowed.
8715d0f
Contributor

nno commented Dec 19, 2016

Thanks, great work! This looks very good to me.
I have a few minor comments which I will add, but with some minor changes this can be merged soon.

+ initTestSuite;
+end
+
+function fullname = tempfile(filename, contents)
@nno

nno Dec 20, 2016

Contributor

Can you change the name to something that indicates that contents is written, for example write_to_tempfile?

@abravalheri

abravalheri Dec 20, 2016 edited

Sure!

I was wondering about creating a 'support' folder inside 'tests', to this and other methods that can be shared. What do you thing?

+
+function fullname = tempfile(filename, contents)
+ tempfolder = fullfile(tempdir, 'mocov_fixtures');
+ [~, ~, ~] = mkdir(tempfolder);
@nno

nno Dec 20, 2016

Contributor
  • it seems unnecessary to have output parameters here. mkdir(tempfolder); should be sufficient
  • please do not use ~ for unused output parameters, as older versions of Matlab do not support it. Most of the time I use unused as variable name instead.
@abravalheri

abravalheri Dec 20, 2016

If I am not wrong, Matlab issues a warning if mkdir tries to create an already existent, unless all the 3 return values are captured.

Can I opt by the unused suggestion?

+end
+
+function test_classdef_line_not_executable
+ mfile = MOcovMFile(create_classdef);
@nno

nno Dec 20, 2016

Contributor

It seems that after the test, the file and directory that were created are not deleted afterwards. Can you please add the required statements, preferably using onCleanup?
For example see https://github.com/MOxUnit/MOxUnit/blob/master/tests/test_moxunit_util_elem2str.m, function remove_path_directory minus the rmpath statement.

Alternatively it may be sufficient to just create a temporary file with tempname, without a directory, and delete the file afterwards.

@abravalheri

abravalheri Dec 20, 2016

The point about having a temporary folder is to be confident about using addpath (I don't know what kind of mfiles exist under the temp dir, and maybe them can shadow some other function)...

I guess a way to avoid it is explicitly requiring the folder to be specified in the filename (is this your suggestion in other words?).

@abravalheri

abravalheri Dec 20, 2016

(For this test, it is not useful... but for the syntax check, it is...)

+ method_opening = [8, 13];
+
+ for l = method_opening
+ assert(~executable_lines(l), ...
@nno

nno Dec 20, 2016

Contributor

What if at some point lines in create_classdef were deleted or added? Then line numbers would not match anymore...
One possibility is to include a 'test of the test', for example, say we would add a method called assert_line_contains, then here we could include within the for loop:
assert_line_contains(lines{i},'methods');.

@abravalheri

abravalheri Dec 20, 2016

When I was writing the tests, I was also uncomfortable with the hard coded approach, but without ideas about how to make it simple.

This suggestion seems to have a perfect balance! Thank you.

+ method_lines = [10, 15];
+
+ for l = method_lines
+ assert(executable_lines(l), ...
@nno

nno Dec 20, 2016

Contributor

See previous comment line 52.

+ properties_lines = [2:4, 5:7];
+
+ for l = properties_lines
+ assert(~executable_lines(l), ...
@nno

nno Dec 20, 2016

Contributor

See previous comment line 52.

@abravalheri

abravalheri Dec 20, 2016

With line ranges do you think 'assert(ing)_line_contains' just the first and last lines are enough?

@@ -0,0 +1,68 @@
+function test_suite = test_write_lines_with_prefix_generate_valid_classdef_files
+ initTestSuite;
@nno

nno Dec 20, 2016

Contributor

Can we merge this file with test_MOcovMFile_recognizes_classdef_syntax.m? There seems to be code duplication in the tempfile and create_classdef functions, and it seems that the remaining test_generate_valid_file function can just be moved.

+end
+
+function fullname = tempfile(filename, contents)
+ tempfolder = fullfile(tempdir, 'mocov_fixtures');
@nno

nno Dec 20, 2016

Contributor

Please see comments for file test_MOcovMFile_recognizes_classdef_syntax.m.

Contributor

nno commented Dec 20, 2016

@abravalheri I would also be happy to implement the suggested changes myself - just please let me know.

@abravalheri

@nno thank you for the review.
Some consideration follows and I will submit another proposal for review ASAP.

+ initTestSuite;
+end
+
+function fullname = tempfile(filename, contents)
@abravalheri

abravalheri Dec 20, 2016 edited

Sure!

I was wondering about creating a 'support' folder inside 'tests', to this and other methods that can be shared. What do you thing?

+
+function fullname = tempfile(filename, contents)
+ tempfolder = fullfile(tempdir, 'mocov_fixtures');
+ [~, ~, ~] = mkdir(tempfolder);
@abravalheri

abravalheri Dec 20, 2016

If I am not wrong, Matlab issues a warning if mkdir tries to create an already existent, unless all the 3 return values are captured.

Can I opt by the unused suggestion?

+end
+
+function test_classdef_line_not_executable
+ mfile = MOcovMFile(create_classdef);
@abravalheri

abravalheri Dec 20, 2016

The point about having a temporary folder is to be confident about using addpath (I don't know what kind of mfiles exist under the temp dir, and maybe them can shadow some other function)...

I guess a way to avoid it is explicitly requiring the folder to be specified in the filename (is this your suggestion in other words?).

@abravalheri

abravalheri Dec 20, 2016

(For this test, it is not useful... but for the syntax check, it is...)

+ method_opening = [8, 13];
+
+ for l = method_opening
+ assert(~executable_lines(l), ...
@abravalheri

abravalheri Dec 20, 2016

When I was writing the tests, I was also uncomfortable with the hard coded approach, but without ideas about how to make it simple.

This suggestion seems to have a perfect balance! Thank you.

+ properties_lines = [2:4, 5:7];
+
+ for l = properties_lines
+ assert(~executable_lines(l), ...
@abravalheri

abravalheri Dec 20, 2016

With line ranges do you think 'assert(ing)_line_contains' just the first and last lines are enough?

Anderson Bravalheri added some commits Dec 20, 2016

@abravalheri abravalheri pushed a commit to abravalheri/MOcov that referenced this pull request Dec 20, 2016

Anderson Bravalheri + (none) ENH: Improve #9 by extracting support functions f3dd7e3

Anderson Bravalheri added some commits Dec 20, 2016

Contributor

nno commented Dec 20, 2016

@abravalheri thanks for all the changes, it has really improved. A few minor questions:

  • is it necessary to have the support directory? I would be fine to put the supporting functions directly in the 'tests' directory. That also has the advantage that if the test file is executed directly from the tests directory, then there is no need to change / set the path
  • similarly, is it necessary to make a temporary directory when writing an m-file with a class definition? How about using tempname directory - even if it has no '.m' extension? This also avoids the need to create a temporary directory and/or deleting such a directory afterwards.
Contributor

nno commented Dec 23, 2016

@abravalheri I would also be happy to implement these minor suggestions myself and then merge. Please let me know your preference.

@nno Sorry, for the delay. I was planning to finish this pull request by following your suggestions. I just have a doubt about the second one:

After my last commit, the only place left a tempfolder was used is to store the decorated file, since it has the same name that the original file (already stored in the tempdir) and needs to run in order to automatically verify if its syntax is correct (unfortunately octave has no linter, and I think this test is important because is the only that directly assert all the things work as expected).

There is a workaround for it: just create the original file with an arbitrary name that do not corresponds to the class name (since it does not run)... But this does not exactly corresponds to the reality (do it?), and seems to me a little bit hacky.

Well, tests (besides automatic verification) are also supposed to serve as documentation, and this workaround may confuse unadvised readers ...
The question is: it is worth to remove the tempfolder and make the workaround that differs from the practical usage? Do you suggest another approach that I couldn't see yet?

abravalheri commented Dec 24, 2016 edited

Right now, I was trying to remove the support folder and the associates addpath statement in the Makefile.

I don't know the internals of MOxUnit (does it use the run command?), but just leaving the shared helper files directly under the tests directory does not seem to work on Octave:

$ ls tests
test_mocov_line_covered.m       test_get_absolute_path.m                      create_tempfolder.m
test_mocov_is_absolute_path.m   test_MOcovMFile_recognizes_classdef_syntax.m  create_tempfile.m
test_mocov_get_relative_path.m  ensure_path_in_tempdir.m                      assertStringContains.m
$ make test
make test-matlab
make[1]: Entering directory `/home/vagrant/drafts/MOcov'
matlab binary could not be found, skipping
make[1]: Leaving directory `/home/vagrant/drafts/MOcov'
make test-octave
make[1]: Entering directory `/home/vagrant/drafts/MOcov'
if [ -n "/usr/bin/octave" ]; then \
                /usr/bin/octave --no-gui --quiet --eval "orig_dir=pwd();cd('/home/vagrant/drafts/MOcov/MOcov');addpath(pwd);cd(orig_dir);if(isempty($
hich('moxunit_runtests'))),error('MOxUnit is required; see https://github.com/MOxUnit/MOxUnit');end;success=moxunit_runtests('/home/vagrant/drafts/M$
cov/tests');exit(~success);"; \
        else \
                echo "octave binary could not be found, skipping"; \
        fi;
suite: 9 tests
FFFFF....
--------------------------------------------------

failure: 'create_tempfile' undefined near line 10 column 16
  test_MOcovMFile_recognizes_classdef_syntax>create_classdef:10 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)
  test_MOcovMFile_recognizes_classdef_syntax>test_classdef_line_not_executable:35 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)

failure: 'create_tempfile' undefined near line 10 column 16
  test_MOcovMFile_recognizes_classdef_syntax>create_classdef:10 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)
  test_MOcovMFile_recognizes_classdef_syntax>test_methods_opening_section_not_executable:50 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)

failure: 'create_tempfile' undefined near line 10 column 16
  test_MOcovMFile_recognizes_classdef_syntax>create_classdef:10 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)
  test_MOcovMFile_recognizes_classdef_syntax>test_method_body_executable:68 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)

failure: 'create_tempfile' undefined near line 10 column 16
  test_MOcovMFile_recognizes_classdef_syntax>create_classdef:10 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)
  test_MOcovMFile_recognizes_classdef_syntax>test_properties_line_not_executable:86 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)

failure: 'create_tempfile' undefined near line 10 column 16
  test_MOcovMFile_recognizes_classdef_syntax>create_classdef:10 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)
  test_MOcovMFile_recognizes_classdef_syntax>test_generate_valid_file:117 (/home/vagrant/drafts/MOcov/tests/test_MOcovMFile_recognizes_classdef_syntax.m)

--------------------------------------------------

FAILED (failure=5)

make[1]: *** [test-octave] Error 1
make[1]: Leaving directory `/home/vagrant/drafts/MOcov'
make: *** [test] Error 2

(According to my experience with another Octave project, Octave seems to implement the run command a little bit differently from Matlab...)

(Tested on Ubuntu 14.04, Octave 4.0.2 x86_64-pc-linux-gnu)

Contributor

nno commented Dec 24, 2016

@abravalheri thanks again for all your work on this.

  • for the supporting files, you may be right that the directory in which they reside (tests, or tests/support) has to be added to the path. An alternative solution is to put the helper functions (create_tempfile, assertStringContains) as subfunction in the test_MOcovMFile_recognizes_classdef_syntax.m file. The advantage is that the test becomes 'self-contained' and is not dependent on path settings. The disadvantage is that the helper functions cannot be re-used, which may in the future lead to code duplication. For now there would be no code duplication though, so how about putting the helper functions in the test_MOcovMFile_recognizes_classdef_syntax.m file?
  • for the class file, what to you think of the approach of generating a random class name that is long enough? For example char(64+ceil(26*rand(1,20))), which is almost certainly a unique filename?
Anderson Bravalheri ENH: Remove 'support' dir and tempfolder (#9)
For now helper functions for classdef syntax check do not
need to be shared, so them can go back to the test file itself.

Tempfolder usage can be avoided to make test simpler.

Additionally make whitespace consistent in test file.

OBS: The function name assertSringContains was written using
camelCase to be consistent with MOxUnit assertion helpers.
25df341

@nno I have made some changes following the direction you pointed (and also added a comment to clarify the 'original X decorated file' name sharing dilemma).

Thank you for the revision.
I wish you a merry Christmas.

Contributor

nno commented Dec 24, 2016

@abravalheri thanks for the final changes, it looks great. Il will merge this PR now.
Merry Christmas!

@nno nno merged commit 2d98320 into MOcov:master Dec 24, 2016

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment