Skip to main content

packages - Automated testing for compatibility with older Mathematica versions


I have several packages which I actively develop and maintain. I try to stay up to date with new releases of Mathematica and usually update within a couple of months of a new version coming out. As a result, I can always be sure that my packages work with the latest version of Mathematica. However, not all users of my packages will want or even be able to update when new releases come out. I would like to support these users, but doing so is difficult when all my development and testing happens with only the latest version.


Is there an automated way of checking which version of Mathematica is required by my packages? In particular, I would like to know if I am using functionality which was only introduced in a specific version of Mathematica. Some specific examples I have encountered are:



  • DeleteDuplicates only appeared in version 7.0

  • SeriesCoefficient had a change in behaviour in some cases in version 7.0.

  • Wavelet support was added in version 8.0


I am aware that the documentation usually says when a feature is new in a specific version, but some of my packages are quite large and it would not be feasible to manually check the reference pages for each function which is used. The NewInXXAlphabeticalListing guides would be less effort to check, but I would prefer an automated solution.


Once I know which version is required I can add a check against $VersionNumber when my package is loaded and output an warning if the version being used is to old.




Answer



The only reliable way seems to have a good set of unit test suites, and run them in earlier versions of Mathematica (I mention this here since the answer and comments mentioning this were deleted). However, having explicit rules for when functions were introduced and / or last changed, extracted from the docs, seems to me a good thing, which may help reduce some work, give hints, etc. So, in addition to the suggestions in other answers / comments (I particularly support the unit testing suggestion), the following code can be executed to extract the versioning information from the documentation:


ClearAll[getVersionSince];
getVersionSince::fail = "Unable to extract version information for function `1`";
getVersionSince[file_String?FileExistsQ] :=
With[{nb = NotebookOpen[file, Visible -> False]},
With[{res =
Cases[
NotebookGet[nb],
Cell[s_String, "History", ___]:>

StringCases[StringTrim@s,
{"New in" ~~ (Whitespace | "") ~~ d : ((DigitCharacter | ".") ..) ~~ __ ~~
"Last modified in" ~~ (Whitespace | "") ~~ m : (((DigitCharacter | ".") ..)) :>
{d, m},
"New in" ~~ (Whitespace | "") ~~ d : ((DigitCharacter | ".") ..) :> d}
],
Infinity]},
NotebookClose[nb];
First@res /; res =!= {}] /; nb =!= $Failed];


getVersionSince[file_String?FileExistsQ] :=
(Message[getVersionSince::fail, Style[FileBaseName[file], Red]]; $Failed);

Here is the setup I used:


$docdir = 
FileNameJoin[{$InstallationDirectory, "Documentation", "English",
"System", "ReferencePages", "Symbols"}];
$functions = FileNames["*.nb", {$docdir}];

To produce the rules for the functions, you can use something like



rules = Table[FileBaseName[f] -> getVersionSince[f],{f, $functions}]

I actually used


j = 0;
Monitor[
functionRules =
abortableTableAlt[(j++; FileBaseName[f] -> getVersionSince[f]), {f, $functions}],
j]

where the abortable table function abortableTableAlt is described at the bottom of this answer. The process was time and memory-consuming, so I saved the result to a file, available from this gist. The result has this format:



{"AbelianGroup" -> {"8"}, "AbortKernels" -> {"7"}, "Abort" -> {"2"}, 
"AbortProtect" -> {"2"}, "Abs" -> {"1"}, "AbsoluteCurrentValue" -> {"6"},
"AbsoluteDashing" -> {{"2", "6"}}, "AbsoluteFileName" -> {"7"}, << 3400 >>,
"$UserName" -> {"3"}, "$Version" -> {"1"}, "$VersionNumber" -> {"2"}, "$$Media" -> {"1"}}

Where a single number (string) is the earliest version when the function (or at least the doc.page) was introduced, and the format like {{"2","6"}} means that it was introduced in v.2 and last modified in v.6. In total, 3412 notebooks were processed. The only function for which this code failed was KConnectedComponents.


You can import the file from the mentioned gist, and I'd recommend to use Dispatch to speed up the rule application. Alternatively, you can use Save to attach these rules to whatever function (symbol) you want. As was suggested in another answer, this can be combined with some tool to extract dependencies, which would give you a list of system symbols used in your code. One such tool was developed David Wagner way back in 1996 and desribed in his article in the Mathematica Journal. I developed a similar tool here, but it is a work in progress and contains some bugs as of now.


Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...